Schema 简介
数据库用户具有口令并拥有各种数据库的权限。 每个用户拥有一个单一的Schema,此Schema与用户具有相同的名称。Schema包含相应用户的全部数据。 例如, hr 用户拥有 hr Schema,此Schema包含如雇员表之类的模式对象。 在生产数据库中,一个数据库Schema的拥有者通常表示一个数据库应用程序,而不是一个人。
图 2-1 HR Schema
Description of "Figure 2-1 HR Schema"
另见
"Overview of Database Security"了解用户和权限的更多信息
Schema Object 类型
Schema Object 的主要类型如下表所示。
Table 2-1 Schema Objects
Object | 描述 | 详细内容 |
---|---|---|
Table |
表以行存储数据。表是关系数据库中最重要的 Schema Object。 |
"Overview of Tables" |
Indexes |
索引是一种 Schema Object,对于每一个被索引的表行或表簇行,索引都包含一个条目 ,以提供直接、快速的存取。Oracle 数据库支持几种类型的索引。一个索引组织表是一个表,其数据以一个索引结构来存储。 |
"Indexes and Index-Organized Tables" |
Partitions |
分区是大型表和索引的分片。 每个分区有其自己的名称,并可能有其自己(可选)的存储特征。 |
"Overview of Partitions" |
Views |
视图是对一个或多个表、或其他视图中的数据的自定义表示。 你可以把它们看作存储的查询。 视图实际上并不存储数据。 |
"Overview of Views" |
Sequences |
序列是一个由用户创建的对象,可以被多个用户共享,用于生成整数。通常,使用序列生成主键值。 |
"Overview of Sequences" |
Dimensions |
维度定义多个列集之间的父-子关系,列集中的所有列必须都来自同一个表。维度通常用于对数据进行分类(如客户、 产品、和时间之)。 |
"Overview of Dimensions" |
Synonyms |
同义词是另一个Schema Object的别名。因为同义词只是一个别名,它在数据字典中除了其定义之外,没有存储。 |
"Overview of Synonyms" |
PL/SQL subprograms and packages |
PL/SQL 是 Oracle 对 SQL 的过程化扩展。PL/SQL 子程序是命名的 PL/SQL 块,可以带参数调用。PL/SQL 包用于将逻辑上相关的 PL/SQL 类型、 变量、和子程序进行分组。 |
"PL/SQL Subprograms" |
还有一些其他类型的对象也存储在数据库中,并以 SQL 语句来创建和操作,但不包含在模式中。 这些对象包括数据库用户、 角色、 上下文、和目录对象。
See Also:
《Oracle Database Administrator’s Guide》了解如何管理 Schema Object
《Oracle Database SQL Language Reference》关于 Schema Object 和 Database Objects 的详细信息
Schema Object 存储
一些模式对象将数据存储在称为段的逻辑存储结构中。例如,一个未分区的堆组织表或索引会创建一个段。
其他模式对象,如视图和序列,则只包含元数据。这一节仅描述有段的模式对象。
Oracle 数据库逻辑上将模式对象存储在表空间中。在模式和表空间之间没有任何的关系:一个表空间可以包含来自不同的模式的对象,一个模式中的对象也可以包含在不同的表空间中。每个对象的数据在物理上包含在一个或多个数据文件中。
下图显示了一种在表、索引段、表空间、和数据文件之间的可能配置。一个表的数据段跨越两个数据文件,这两个数据文件属于同一个表空间。一个段不能跨多个表空间。
图 2-2 段、表空间和数据文件
Description of "Figure 2-2 Segments, Tablespaces, and Data Files"
另见:
"Logical Storage Structures"了解表空间和段
《Oracle Database Administrator’s Guide》了解如何管理模式对象的存储
Schema Object 依赖
一些模式对象会引用其它对象,这就产生了模式对象依赖。
例如,一个视图包含一个引用表或其他视图的查询,或一个 PL/SQL 子程序调用其他子程序。如果对象 A 的定义引用了对象 B,那么 A 相对于 B 来说是依赖对象,或 B 相对于 A 来说是被引用对象。
Oracle 数据库提供了一种自动的机制,以确保依赖对象对于相应的被引用对象来说始终是最新的。 当依赖对象产生后,数据库将跟踪依赖对象和其被引用对象之间的依赖关系。 当被引用对象的更改可能会影响依赖对象时,数据库将相关对象标记为无效。例如,当用户删除一个表,所有基于这个被删除表的视图都将变得不可用。
在一个无效的依赖对象重新变得可用之前,必须基于对被引用对象的新定义重新编译。当引用无效的从属对象时,将自动重新编译。
为演示模式对象之间如何产生依赖,下面的示例脚本创建表 test_table,然后创建一个查询此表的存储过程:
CREATE TABLE test_table ( col1 INTEGER, col2 INTEGER );CREATE OR REPLACE PROCEDURE test_proc
AS
BEGINFOR x IN ( SELECT col1, col2 FROM test_table )LOOP-- process dataNULL;END LOOP;
END;
/
下面的查询显示过程 test_proc 的状态是有效的:
SQL> SELECT OBJECT_NAME, STATUS FROM USER_OBJECTS WHERE OBJECT_NAME = 'TEST_PROC';OBJECT_NAME STATUS
----------- -------
TEST_PROC VALID
下面的查询显示过程 test_proc 的状态是有效的:
SQL> ALTER TABLE test_table ADD col3 NUMBER;Table altered.SQL> SELECT OBJECT_NAME, STATUS FROM USER_OBJECTS WHERE OBJECT_NAME = 'TEST_PROC';OBJECT_NAME STATUS
----------- -------
TEST_PROC VALID
但是, 更改 col1 列的数据类型后, test_proc 过程变得无效,因为该过程依赖于该列。
SQL> ALTER TABLE test_table MODIFY col1 VARCHAR2(20);Table altered.SQL> SELECT OBJECT_NAME, STATUS FROM USER_OBJECTS WHERE OBJECT_NAME = 'TEST_PROC';OBJECT_NAME STATUS
----------- -------
TEST_PROC INVALID
运行或重新编译该过程使它重新有效,如下面的示例中所示:
SQL> EXECUTE test_procPL/SQL procedure successfully completed.SQL> SELECT OBJECT_NAME, STATUS FROM USER_OBJECTS WHERE OBJECT_NAME = 'TEST_PROC';OBJECT_NAME STATUS
----------- -------
TEST_PROC VALID
另见:
《Oracle Database Administrator’s Guide》和《Oracle Database Development Guide》了解如何管理模式对象依赖性。
SYS 和 SYSTEM Schemas
所有 Oracle 数据库都包括默认管理帐户。
管理帐户享有很高的特权,仅用于授权的数据库管理员执行如启停数据库、 管理内存和存储、 创建和管理数据库用户等任务。
SYS 管理帐户在创建数据库时自动创建。 此帐户可以执行所有的数据库管理功能。 SYS 模式存储数据字典基表和视图。 这些基表和视图对数据库的运行至关重要。 SYS 模式中的表只由数据库操作,绝不能被任何用户修改。
在创建数据库时,也会自动创建 SYSTEM 帐户。 SYSTEM 模式存储其它一些用于显示管理信息的表和视图,以及用于各种数据库选项和工具的内部表和视图。 永远不要使用 SYSTEM 模式来存储非管理性用户的表。
See Also:
"User Accounts"
"Connection with Administrator Privileges"
《Oracle Database Administrator’s Guide》了解 SYS, SYSTEM,以及其他管理帐户
示例 Schema
Oracle 数据库可能包含示例 Schema,这是一组相互关联的 Schema,在 Oracle 文档和 Oracle 教学材料中用于演示常见数据库任务。
示例 hr Schema 包含有关雇员、 部门和位置、 工作历史等信息。下图描述了 hr 中表的实体-关系图。这个手册中的大多数例子都使用了这个 Schema 中的 Object。
图 2-3 HR Schema
Description of "Figure 2-3 HR Schema"
See Also:
《Oracle Database Sample Schemas》学习如何安装示例 Schemas
表概述
表是 Oracle 数据库中的数据组织的基本单位。
表描述了一个实体,其相关重要信息必须被记录。 例如,雇员可能是一个实体。
Oracle 数据库表分为以下几个基本类别:
-
关系表
关系表具有简单的列,是最常见的表类型。 示例 2-1 显示了一个用于创建关系表的 CREATE TABLE 语句。
-
Object tables
列对应于对象类型的顶层属性。 请参见 "Overview of Object Tables".
您可以创建一个具有下列组织特征的关系表:
-
堆组织表,它不会以任何特定顺序存储行。 默认情况下, CREATE TABLE 语句创建堆组织表。
-
索引组织表,它按主键值对行进行排序。 对于某些应用程序,索引组织表可以增强性能并更有效地使用磁盘空间。请参见"Overview of Index-Organized Tables".
-
外部表,是一个只读表,它的元数据存储在数据库中,但其数据存储在数据库外。 请参见"Overview of External Tables".
表要么是永久的,要么是临时的。 永久表的定义和数据保持跨会话存在。临时表的定义与永久表有相同的存在方式,但其数据仅在一个事务或会话的持续期间有效。临时表可用于暂存某些应用程序在运行过程中由多个操作所生成的中间结果集。
另见:
《Oracle Database Administrator’s Guide》了解如何管理表
列
表的定义包括表名称和列集。
列标识由表所描述的实体的一个属性。 例如,在雇员表中的列 employee_id,是指雇员实体中的雇员 ID 属性。
通常,当您将创建一个表时,给每列一个列名称、 数据类型、和宽度。例如 employee_id 的数据类型是 NUMBER(6),这表明此列只可以包含长达 6 位数的数字数据宽度。与 DATE 类型一样,宽度可以由数据类型预先确定。
虚拟列
表可以包含虚拟列,与非虚拟列不一样,虚拟列不占用磁盘空间。
数据库通过计算一组用户指定的表达式或函数,按需派生出虚拟的列值。 例如虚拟列 income 可能是 salary 列和 commission_pct 列的一个函数。
另见:
《Oracle Database Administrator’s Guide》了解如何管理虚拟列
不可见列
不可见列是用户指定的列,其值只有在列被名称显式指定时才可见。您可以在不影响现有应用程序的情况下向表添加不可见列,并在必要时使列可见。
通常,不可见列有助于迁移和发展在线应用程序。用例可能是使用SELECT *语句查询三列表的应用程序。向表中添加第四列将破坏应用程序,该应用程序需要三列数据。添加第四个不可见列使应用程序功能正常。然后,开发人员可以修改应用程序以处理第四列,并使该列在应用程序运行时可见。
下面的示例创建一个具有不可见列计数的表产品,然后使不可见列可见:
CREATE TABLE products ( prod_id INT, count INT INVISIBLE );
ALTER TABLE products MODIFY ( count VISIBLE );
See Also:
-
《Oracle Database Administrator’s Guide》了解如何管理不可见列
-
《Oracle Database SQL Language Reference》了解更多信息关于不可见列
行
行是对应于表中某条记录的列信息的集合。
例如,雇员表中的一行描述一个特定的雇员的属性:雇员ID、姓、名等等。在你创建表之后,你可以使用SQL进行插入、查询、删除和修改操作。
示例: CREATE TABLE 和 ALTER TABLE 语句
用于创建表的 Oracle SQL 命令是 CREATE TABLE。
例 2-1 CREATE TABLE employees
下面的示例显示了 hr 示例 Schema 中的 employees 表的 CREATE TABLE 语句。语句指定诸如 employee_id、first_name 等列,并为每个列指定数据类型,如数字或日期。
CREATE TABLE employees( employee_id NUMBER(6), first_name VARCHAR2(20), last_name VARCHAR2(25)CONSTRAINT emp_last_name_nn NOT NULL, email VARCHAR2(25)CONSTRAINT emp_email_nn NOT NULL, phone_number VARCHAR2(20), hire_date DATECONSTRAINT emp_hire_date_nn NOT NULL, job_id VARCHAR2(10)CONSTRAINT emp_job_nn NOT NULL, salary NUMBER(8,2), commission_pct NUMBER(2,2), manager_id NUMBER(6), department_id NUMBER(4), CONSTRAINT emp_salary_minCHECK (salary > 0), CONSTRAINT emp_email_ukUNIQUE (email)) ;
例 2-2 ALTER TABLE employees
下面的示例展示了一个ALTER TABLE语句,它向雇员表添加了一个完整性约束。完整性约束强制实施业务规则和防止向表中输入无效信息。
ALTER TABLE employees
ADD ( CONSTRAINT emp_emp_id_pkPRIMARY KEY (employee_id), CONSTRAINT emp_dept_fkFOREIGN KEY (department_id)REFERENCES departments, CONSTRAINT emp_job_fkFOREIGN KEY (job_id)REFERENCES jobs (job_id), CONSTRAINT emp_manager_fkFOREIGN KEY (manager_id)REFERENCES employees) ;
例 2-3 employees 表中的行
下面的示例展示了hr.employees 表中的 8 行和 6 列。
EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY COMMISSION_PCT DEPARTMENT_ID
----------- ----------- ------------- ------- -------------- -------------100 Steven King 24000 90101 Neena Kochhar 17000 90102 Lex De Haan 17000 90103 Alexander Hunold 9000 60107 Diana Lorentz 4200 60149 Eleni Zlotkey 10500 .2 80174 Ellen Abel 11000 .3 80178 Kimberely Grant 7000 .15
前面的输出说明了表、列和行的如下重要特征:
-
表中的行描述一名雇员的全部属性: 名称、 薪金、 部门等。 例如,输出的第一行显示名为 Steven King 的雇员记录。
-
列描述该雇员的一个属性。 在本例中, employee_id 列是主键,其含义是指每一位员工由其雇员 id 唯一地标识,保证任何两个雇员都不具有相同的雇员 id。
-
非键列可以包含具有相同值的行。在本例中,雇员 101、102 的薪金相同,都是 17000。
-
外键列会引用同一个表或另一个表中的主键或唯一键。 在本例中,department_id 值 90 对应于 departments 表中的 department_id 列等于 90 的行。
-
域是行和列的交叉点。 它只可以包含一个值。 例如,雇员 104 的部门 ID 域包含值 60。
-
域可以没有值。 在这种情况下,该域包含空值。 雇员 100 的 commission_pct 列的值为空,而雇员 149 的相应值是.2。一个列通常允许为空,除非已对该列定义了 NOT NULL 或主键完整性约束,在这种情况下如果该列无值,是不可以插入任何行的。
另见:
《Oracle Database SQL Language Reference》关于 CREATE TABLE 的语法和语义
Oracle 数据类型
每个列都有一种数据类型,这与特定的存储格式、限制、和有效的值范围相关联。 一个值的数据类型与一组固定的属性相关联。
这些属性会使 Oracle 数据库区别对待不同数据类型的值。例如,你可以把 NUMBER 数据类型的值相乘,但不能把 RAW 数据类型的值相乘。
在创建表时,您必须为每个列指定数据类型。 随后在该列中插入的每个值都假定为该列的数据类型。
Oracle 数据库提供了几种内建的数据类型。 最常用的数据类型分为以下几类:
-
字符数据类型
-
数字数据类型
-
日期时间数据类型
-
ROWID 数据类型
-
格式模型和数据类型
其他几种重要的内建类型包括 RAW、大对象 (LOB)、和集合。PL/SQL 具有用于常量和变量的数据类型,包括布尔、引用类型、复合类型 (记录)和用户定义类型。
另见:
-
"Overview of LOBs"
-
《Oracle Database SQL Language Reference》了解内置的 SQL 数据类型
-
《Oracle Database PL/SQL Packages and Types Reference》了解 PL/SQL 数据类型
-
《Oracle Database Development Guide》关于如何使用内置数据类型的信息
字符数据类型
字符数据类型存储在字符串中的字符 (字母或数字) 数据。 最常用的字符数据类型是 VARCHAR2,它是用于存储字符数据的最有效的选项。
与字符编码模式相对应的字节值,一般称为字符集。 数据库字符集是在数据库创建时建立的。字符集的例子有 7 位 ASCII 码、 EBCDIC 码、和 Unicode UTF-8。
字符数据类型的长度语义可以以字节或字符为单位。将字符串处理为字节序列称为字节语义。这是字符数据类型的默认值。字符串作为字符序列的处理称为字符语义。字符是数据库字符集的一个编码点。
另见:
-
"Character Sets"
-
《Oracle Database 2 Day Developer's Guide》获取有关数据类型的简要介绍
-
《Oracle Database Development Guide》学习如何选择字符数据类型。
VARCHAR2 和 CHAR 数据类型
例如:'LILA'、 'St. George Island' 和 '101',都是字符文本; 5001 是数字文本。字符文本包括在单引号中,以便数据库可以将它们与Schema Object 名称区分开来。
当创建一个有 VARCHAR2 列的表时,指定该列的最大字符串长度。在示例 2-1 中, last_name 列是 VARCHAR2(25)数据类型,这意味着该列中存储的名字最多可以长达 25 个 字节。
对每一行, Oracle 数据库将按可变长度字段来存储值,如果一个值超出最大长度,数据库将返回一个错误。例如,在单字节字符集中,如果您在某行的 last_name 列输入 10 个字符,那么在行片中该列只存储 10 个字符 (10 个字节) 而不是 25。使用 VARCHAR2 可以减少空间占用。
与 VARCHAR2 不同, CHAR 存储固定长度的字符串。当创建一个有 CHAR 列的表时,需要为该列指定一个字符串长度。默认值是 1 个字节。数据库使用空格填充到指定的长度的值。
Oracle 数据库将使用非填充比较语义来比较 VARCHAR2 值,而使用空白填充比较语义来比较 CHAR 值。
另见:
《Oracle Database SQL Language Reference》了解有关空格填充和非填充比较语义的详细信息
NCHAR 和 NVARCHAR2 数据类型
Unicode 是一种通用的编码字符集,可以将任何语言的信息存储在一种字符集中。NCHAR 存储对应于国家字符集的固定长度字符串,而 NVARCHAR2 存储可变长度的字符串。
当您创建数据库时,指定国家字符集。 NCHAR 和 NVARCHAR2 数据类型的字符集必须是 AL16UTF16 或 UTF8。这两种字符集都使用 Unicode 编码。
当您创建一个有 NCHAR 或 NVARCHAR2 列的表时,其最大大小始终是字符长度语义。对于 NCHAR 或 NVARCHAR2,字符长度语义是默认的,也是唯一的长度语义。
另见:
《Oracle Database Globalization Support Guide》了解 Oracle 全球化支持功能的信息
数字数据类型
Oracle 数据库的数字数据类型存储固定和浮点数字、零、或无穷。某些数值类型也可以存储未定义操作的结果值,叫做"非数字"或 NAN。
Oracle 数据库以变长格式存储数字数据。用科学计数法存储值,其中一个字节用于存储指数。数据库使用最多 20 个字节存储尾数,即浮点数的有效位数部分。Oracle 数据库不会存储前导零和结尾零。
NUMBER 数据类型
NUMBER 数据类型存储固定和浮点数字。数据库可以存储几乎任何规模的数字。此数据保证在运行 Oracle 数据库的不同操作系统之间可移植。大多数情况下,当您必须存储数值数据时,推荐使用 NUMBER 数据类型。
按 NUMBER(p,s)的形式定义定点数, p 和 s 有以下特征:
-
精度
精度指定数字的总长度。如果不指定精度, 则列按应用程序提供的数据存储值,不作任何舍入。.
-
小数位数
小数位数指从十进制的小数点到最小有效数字的位数。正小数位数从小数点向右计数直至最小有效位。负小数位数从小数点向左计数直至(但不包括)最小有效数字。如果您指定了精度但没有指定小数位数,如 NUMBER(6),那么小数位数为 0。
在示例 2-1 中,工资列是 NUMBER(8,2)类型,精度为 8,而小数位数为2。因此,数据库将 10 万元的薪金存储为 100000.00 。
浮点数
Oracle 数据库为浮点数提供了两种互斥的数值数据类型: BINARY_FLOAT 和 BINARY_DOUBLE。
这两种类型支持所有 NUMBER 数据类型提供的基本功能。然而,相比 NUMBER 使用十进制精度, BINARY_FLOAT 和 BINARY_DOUBLE 使用二进制精度,可以使算术计算更快并且通常可以降低存储需求。
BINARY_FLOAT 和 BINARY_DOUBLE 是近似数字数据类型。它们存储十进制的值的近似表示,而不是精确的表示形式。例如值 0.1 不能用 BINARY_DOUBLE 或 BINARY_FLOAT 完全精确地表示。他们经常用于科学计算。其行为类似于 Java 或 XMLSchema 中的 FLOAT 和 DOUBLE 数据类型。
另见:
《Oracle Database SQL Language Reference》了解有关精度、小数位数、和数值类型的
日期时间数据类型
日期时间数据类型包括 DATE 和 TIMESTAMP。 Oracle 数据库为时间戳提供全面的时区支持。
DATE 数据类型
DATE 数据类型存储日期和时间。尽管日期时间可以用字符或数字数据类型表示, DATE 具有特殊的相关属性。
数据库在内部将日期存储为数字。日期被存储为固定长度的域,共 7 个字节,分别对应世纪、 年、 月、 日、 小时、分、和秒。
数据库根据指定的格式模型显示日期。格式模型是一个描述在字符串中的日期时间格式的字符文字。 标准的日期格式是 DD-MON-RR,它会以 01-JAN-11 的形式显示日期。
RR 类似于 YY (年的最后两位),但返回值的世纪值会因指定的两位年份及本年度的最后两位不同而不同。假设在 1999 年,数据库显示为 01-JAN-11。如果日期格式使用 RR,那么 11 指的是 2011,而如果格式使用 YY,那么 11 指的是 1911。 您可以更改实例或会话级别的默认日期格式。
Oracle 数据库以 24 小时制格式存储时间 — — HH:MI:SS。如果不输入的任何时间部分,那么默认情况下,日期字段中的时间是凌晨 00: 00: 00,如果只输入了时间,则日期部分默认为当月的第一天。
另见:
-
《Oracle Database Development Guide》了解有关世纪和日期格式掩码的详细信息
-
《Oracle Database SQL Language Reference》了解日期时间格式代码的有关信息
-
《Oracle Database Development Guide》了解如何使用datetime数据类型执行算术操作。
TIMESTAMP 数据类型
TIMESTAMP 数据类型是日期数据类型的扩展。
除了存储 DATE 数据类型的信息,TIMESTAMP 同时还存储秒的小数部分。 TIMESTAMP 数据类型对于存储精确时间的值很有用,比如那些必须跟踪事件顺序的应用程序。
DATETIME 数据类型 TIMESTAMP WITH TIME ZONE 和 TIMESTAMP WITH LOCAL TIME ZONE 能感知时区。当用户选择数据时,其值会被调整以适配用户会话的时区。此数据类型可用于收集和评估各地理区域的日期信息。
另见:
《Oracle Database SQL Language Reference》了解有关创建时间戳列,和往其中输入数据的语法的详细信息
ROWID 数据类型
存储在数据库中的每一行都有一个地址。 Oracle 数据库使用 ROWID 数据类型存储在数据库中的每一行的地址 (rowid)。
Rowids 分为以下几类:
-
物理 rowids 存储堆组织表、 表簇、表分区、和索引分区中的行地址。
-
逻辑 rowids 存储索引组织表中的行地址。
-
外部 rowids 是外来表(如通过网关访问的 DB2 表)中的标识符。他们不是标准的 Oracle 数据库 rowids。
有一种数据类型称为通用 rowid 或 UROWID,支持各种 rowids。
使用 Rowid
最常见的 B 树索引,包含一个被划分成多个键范围的排序列表。每个键都与一个指向关联的行地址的 rowid 相关联,用于快速访问行。
最终用户和应用程序开发人员也可以使用 rowids 的几个重要功能:
-
Rowids 是访问特定行的最快方式。
-
Rowids 提供了查看表的组织方式的能力。
-
Rowids 是给定表中的行的唯一标识符。
您也可以创建带 ROWID 数据类型列的表。例如,您可以定义一个异常表,其中一列为 ROWID 数据类型,以存储违反了完整性约束的行 rowids。使用ROWID 数据类型定义的列像其他表中的列一样, 其值可以更新,等等。
ROWID 伪列
Oracle 数据库中的每个表都有一个名为 ROWID 的 伪列。
伪列类似于表列,但实际上并不存储在表中。您可以从伪列中选择数据,但不能插入、更新、或删除它们的值。 伪列也类似于不带参数的 SQL 函数。不带参数的函数对于结果集中的每一行通常返回相同的值,而伪列通常为每个行返回不同的值。
ROWID 伪列的值是表示的每个行的地址的字符串。这些字符串具有的数据类型 ROWID。在执行 SELECT 或 DESCRIBE 列出表的结构时,此伪列并不显示,它也不占用空间。但是,每个行的 rowid 可以通过使用保留字ROWID 作为列名的 SQL 查询来检索。
下面的示例查询显示雇员表中雇员 ID 为 100 的行的 ROWID 伪列。
SQL> SELECT ROWID FROM employees WHERE employee_id = 100;ROWID
------------------
AAAPecAAFAAAABSAAA
另见:
-
"Rowid Format"
-
《Oracle Database Development Guide》了解如何通过地址标识行
-
《Oracle Database SQL Language Reference》了解 rowid 类型
格式模型和数据类型
格式模型是一个字符文本,用来描述存储在一个字符串中的日期时间或数值数据的格式。格式模式不会更改数据库中的值的内部表示形式。
当您将一个字符串转换成日期或数字时,格式模型决定了数据库如何解释字符串。在 SQL 中,你可以使用格式模型作为 TO_CHAR 和 TO_DATE 函数的参数,来格式化将从数据库中返回的一个值,或将存储到数据库中的一个值。
下面的语句选择部门 80 中的雇员的薪水,并使用 TO_CHAR 函数来将这些薪水的值,转换为以数字格式模型 '$ 99,990.99' 指定的字符值:
SQL> SELECT last_name employee, TO_CHAR(salary, '$99,990.99') AS "SALARY"2 FROM employees3 WHERE department_id = 80 AND last_name = 'Russell';EMPLOYEE SALARY
------------------------- -----------
Russell $14,000.00
下面的示例更新一个雇佣日期,使用 TO_DATE 函数与格式掩码 'YYYY MM DD' 将字符串'1998 05 20'转换为日期值。
SQL> UPDATE employees2 SET hire_date = TO_DATE('1998 05 20','YYYY MM DD')3 WHERE last_name = 'Hunold';
另见:
《Oracle Database SQL Language Reference》了解格式模型
完整性约束
完整性约束是一种命名规则,用于限制一个或多个表中的列值。
这些规则可以防止向表中输入无效数据。此外,约束也可以防止当存在某些依赖项时删除表中的数据。
如果启用了约束,则输入或更新数据时,Oracle数据库会防止输入不符合约束的数据。如果禁用了约束,那么Oracle数据库允许不符合约束的数据进入数据库。
在例 2-1 中,CREATE TABLE 语句对 last_name、email、hire_date 和 job_id 列指定了非空约束。约束子句标识列和约束的条件。这些约束确保指定的列中不包含空值。例如,试图插入一个没有 job ID 的新雇员将产生一个错误。
您可以在创建表时或创建表后创建一个约束。如果需要,可以暂时禁用约束。数据库将约束存储在数据字典中。
另见:
-
"Data Integrity"了解完整性约束
-
"Overview of the Data Dictionary"了解数据字典
-
《Oracle Database SQL Language Reference》了解 SQL 约束子句
表存储
Oracle 数据库使用表空间中的数据段保存表数据。
段包含由数据块组成的扩展盘区。表数据段(或表簇的簇数据段)位于表所有者的默认空间中,或 CREATE TABLE 语句中所指定的表空间中。
另见:
"User Segments"了解段类型和如何创建
表组织
默认情况下,表按堆的形式来组织数据,这意味着数据库将行存放在最适合他们的位置,而不是按用户指定的顺序来存放。因此,堆组织表是一个无序的行的集合。
当用户往其中添加行时,数据库将行置于在数据段中第一个可用的空闲空间。不能保证行按照插入它们的顺序进行检索。
hr.departments 表是一个堆组织表。它具有部门 ID、 名称、 经理 ID、位置 id 等列。当插入行时,数据库将其存储到适合的位置。表段中的某个数据块可能包含如下示例所示的无序的行:
50,Shipping,121,1500
120,Treasury,,1700
70,Public Relations,204,2700
30,Purchasing,114,1700
130,Corporate Tax,,1700
10,Administration,200,1700
110,Accounting,205,1700
列的顺序对于表中的所有行都是相同的。通常,数据库按它们被列在 CREATE TABLE 语句中的列顺序来存储列,但这个顺序是不保证的。例如,如果表中有 LONG 类型的列,那么数据库总是将其存储到行中的最后一列。此外,如果向表中添加一个新列,则新列将被存储为最后一列。
与常规列不同,表可以包含虚拟列,它不占用磁盘上的空间。数据库通过计算一组用户指定的表达式或函数,在虚拟列中按需派生一个值。您可以对虚拟列创建索引、 收集统计数据、并创建完整性约束。因此,虚拟列非常像非虚拟列。
另见:
-
"Overview of Index-Organized Tables"
-
《Oracle Database SQL Language Reference》了解虚拟列
行存储
数据库将行存储在数据块中。在表中,少于 256 个列的行,被存储在一个或多个行片中。
数据库尽可能将每一行作为一个行片来存储。但是,如果所有的行数据不能插入到一个单一数据块中,或者对现有的行的更新导致行溢出,则数据库使用多个行片来存储此行。
表簇中的行除了包含非聚集表中的行相同的信息,还包含对其所属簇键的引用信息。
另见:
"Data Block Format"了解数据块的组件。
行片 Rowids
堆组织表中的每一行都有一个在该表中唯一的 rowid,与一个行片的物理地址相对应。对于表簇,处于同一个数据块的不同表中的行可以有相同的 rowid。
Oracle 数据库在内部使用 rowids 来构建索引。例如,在 B 树索引中的每个键与一个指向关联行地址的 rowid 相关联,以便快速访问。物理 rowids 提供对表行尽可能最快的访问,使数据库几乎只需单次I/O 就可以检索到行。
另见:
-
"Rowid Format"了解 rowid 结构
-
"Overview of B-Tree Indexes"了解B-tree 索引的类型和结构
Null 值存储
Null 是指在某个列中值的缺失。空值表示缺少、 未知、或不适用的数据。
如果 Null 的前后都有数据值,则空值会被存储。在这些情况下,他们需要 1 个字节来存储列的长度(零)。行中末尾的 Null 值不需要存储,因为新的行头表示前一行中的其余列为空。例如,如果表的最后三列为空,则不为这些列存储数据。
另见:
《Oracle Database SQL Language Reference》了解更多关于Null值
表压缩
数据库可以使用表压缩来减少表所需的存储量。
压缩可以节省磁盘空间,减少数据库高速缓存中的内存使用,并在某些情况下可以加快查询执行速度。表压缩对数据库应用程序是透明的。
基本表压缩和高级行压缩
基于字典的表压缩为堆组织表提供了很好的压缩率。
Oracle 数据库支持以下类型的基于字典的表压缩:
-
基本表压缩
这种类型的压缩用于大容量加载操作。数据库不压缩使用常规DML修改的数据。必须使用直接路径 INSERT 操作、ALTER TABLE . . . MOVE 操作,或在线表重新定义,以实现基本的表压缩。
-
高级行压缩
这种类型的压缩用于OLTP应用程序,并压缩由任何SQL操作的数据。数据库实现了具有竞争力的压缩比,同时使应用程序能够在与未使用压缩表上执行的DML的时间想同。
对于上述类型的压缩,数据库以行主要格式存储压缩行。一个行的所有列都存储在一起,接下来是下一行的所有列,依次类推。数据库用存储在块开头的符号表的简短引用替换重复的值。因此,重新创建未压缩数据所需的信息存储在数据块本身中。
压缩的数据块看起来跟正常的数据块差不多。在常规数据块上能正常工作的大多数数据库功能,也可以在压缩的数据块上正常工作。
您可以在表空间、 表、 分区或子分区等级别声明压缩。如果在表空间级指定了压缩,则在该表空间中创建的所有表在缺省情况下都是压缩的。
例 2-4 表级压缩
以下语句将高级行压缩应用于 orders 表:
ALTER TABLE oe.orders ROW STORE COMPRESS ADVANCED;
例 2-5 分区级压缩
如下所示的 CREATE TABLE 语句示例片断,指定其中一个分区的高级行压缩,而其他分区为基本压缩:
CREATE TABLE sales (prod_id NUMBER NOT NULL,cust_id NUMBER NOT NULL, ... )PCTFREE 5 NOLOGGING NOCOMPRESSPARTITION BY RANGE (time_id)( partition sales_2013 VALUES LESS THAN(TO_DATE(...)) ROW STORE COMPRESS BASIC,partition sales_2014 VALUES LESS THAN (MAXVALUE) ROW STORE COMPRESS ADVANCED );
另见:
-
"Row Format"了解如何将值存储在行中
-
"Data Block Compression"了解压缩数据块的格式
-
"SQL*Loader"了解如何使用 SQL*Loader 进行直接路径加载
-
《Oracle Database Administrator’s Guide》和《Oracle Database Performance Tuning Guide》了解表压缩
混合列压缩
使用混合列压缩,数据库将一组行的同一列存储在一起。数据块不以行为主格式存储数据,而是使用行和列方法的组合。
将列数据存储在一起,具有相同的数据类型和类似的特性,极大地增加了压缩所节省的存储空间。数据库压缩由任何SQL操作的数据,尽管直接路径加载的压缩级别更高。数据库操作对压缩对象透明地工作,因此不需要应用程序更改。
另见:
"In-Memory Area"了解更多关于IM列式存储的信息
混合列压缩的类型
如果您的底层存储支持混合的列压缩,那么您可以根据您的需求指定不同类型的压缩。
压缩选项:
-
warehouse 压缩
这种类型的压缩被优化以节省存储空间,并用于数据仓库应用程序。
-
archive 压缩
这种类型的压缩针对最大压缩级别进行了优化,并且适用于历史数据和不发生更改的数据。
混合列压缩优化了Oracle Exadata存储上的数据仓库和决策支持应用程序。Oracle Exadata最大化利用混合列压缩压缩表上的查询性能,利用处理能力、内存和 Infiniband 网络带宽,最大限度地提高了对使用混合列压缩的表的查询性能。
其他Oracle存储系统支持混合列压缩,并提供与Oracle Exadata存储相同的空间节省,但不提供相同级别的查询性能。对于这些存储系统,混合列压缩非常适合于在数据库中归档不常访问的旧数据。
压缩单元
混合列压缩使用一个称为压缩单元的逻辑结构来存储一组行。
将数据加载到表中时,数据库以列式格式存储行组,并将每个列的值存储在一起并进行压缩。在数据库压缩了一组行的列数据后,数据库将数据放入压缩单元。
例如:将混合列压缩应用于 daily_sales 表。在每天结束时,您将使用项目和所售数量填充表,并使用项目ID和日期组成复合主键。下表显示了daily_sales中的行子集。
表 2-2 表 daily_sales 示例
Item_ID | Date | Num_Sold | Shipped_From | Restock |
---|---|---|---|---|
1000 |
01-JUN-18 |
2 |
WAREHOUSE1 |
Y |
1001 |
01-JUN-18 |
0 |
WAREHOUSE3 |
N |
1002 |
01-JUN-18 |
1 |
WAREHOUSE3 |
N |
1003 |
01-JUN-14 |
0 |
WAREHOUSE2 |
N |
1004 |
01-JUN-18 |
2 |
WAREHOUSE1 |
N |
1005 |
01-JUN-18 |
1 |
WAREHOUSE2 |
N |
假设此行子集存储在一个压缩单元中。混合列压缩将每列的值存储在一起,然后使用多个算法压缩每个列。数据库根据多种因素选择算法,包括列的数据类型、列中实际值的基数以及用户选择的压缩级别。
如下图所示,每个压缩单元可以跨越多个数据块。特定列的值可能跨越多个块,也可能不跨越多个块。
图 2-4 压缩单元
Description of "Figure 2-4 Compression Unit"
如果混合列压缩不会节省空间,那么数据库将以DBMS_COMPRESSION.COMP_BLOCK格式存储数据。在这种情况下,数据库将OLTP压缩应用于位于混合列压缩段中的块。
另见:
-
"Row Locks (TX)"
-
《Oracle Database Licensing Information User Manual》了解混合列压缩的许可要求
-
《Oracle Database Administrator’s Guide》了解如何使用混合列压缩
-
《Oracle Database SQL Language Reference》用于CREATE TABLE 语法和语义
-
《Oracle Database PL/SQL Packages and Types Reference》了解 DBMS_COMPRESSION 包
DML和混合列压缩
混合列压缩对不同类型的DML操作中的行锁定有影响。
直接路径加载和常规插入
将数据加载到使用混合列压缩的表中时,可以使用常规插入或直接路径加载。直接路径加载锁定整个表,这降低了并发性。
Oracle数据库12c第2版(12.2)在混合列压缩格式中增加了对常规数组插入的支持。传统阵列插入的优点是:
-
插入的行使用行级锁,这增加了并发性。
-
自动数据优化(ADO)和热图支持行级策略的混合列压缩。因此,即使DML活动发生在段的其他部分,数据库也可以对符合条件的块使用混合列压缩。
当应用程序使用常规数组插入时,Oracle数据库在满足以下条件时将行存储在压缩单元中:
-
该表存储在ASSM表空间中。
-
兼容级别为12.2.0.1或更高版本。
-
表定义满足现有的混合列压缩表约束,包括不包含Long类型的列,也不满足行依赖关系。
常规插入会生成 Redo 和 Undo。因此,常规DML语句创建的压缩单元将与DML一起回滚或提交。数据库自动执行索引维护,就像存储在常规数据块中的行一样。
Updates 和 Deletes
默认情况下,如果将更新或删除应用于单元中的任何行,数据库将锁定压缩单元中的所有行。为了避免这个问题,可以选择为表启用行级锁定。在这种情况下,数据库只锁定受更新或删除操作影响的行。
另见:
-
"Automatic Segment Space Management"
-
"Row Locks (TX)"
-
《Oracle Database Administrator’s Guide》了解如何执行常规插入
-
《Oracle Database SQL Language Reference》了解INSERT语句
表簇概述
表簇是一组表,它们共享公共的列,并将相关的数据存储在相同的数据块中。
当表被聚簇时,单个数据块可以包含多个表中的行。例如,一个块可以同时存储来自 employees 表和 departments 表的行,而不只是单个表中的行。
簇键是所有被聚簇的表的共有列或列集。例如, employees 表和 departments 表共享 department_id 列。您在创建表簇时,和创建被添加到表簇的每个表时,指定簇键。
簇键值是一组特定行的簇键列的值。包含相同簇键值的所有数据(例如 department_id=20),物理上存储在一起。每个簇键值在簇或簇索引中只存储一次,而无论在这些不同表中有多少行包含这个值。
打个比方,假设一个人力资源经理有两个书柜: 一个书柜装有一盒盒雇员文件夹,而另一个书柜装有一盒盒部门文件夹。用户经常要找一个特定部门的所有员工的文件夹。为便于检索,经理重新排列了书柜中的所有盒子。她将这些盒子按部门 id 划分。这样,部门 20 的所有雇员文件夹和该部门 20 的文件夹本身放在一个盒子中 ;部门 100 的所有雇员文件夹和该部门 100 的文件夹本身放在另一个盒子中,依次类推。
如果多个表主要是被查询 (而不是修改) ,且各表中的记录是经常被一起查询或联接,在这些情况下可以考虑将他们聚簇化。因为表簇将不同表中的相关行存储在同一个数据块中,被正确使用的表簇相比非聚簇表具有下列优点:
-
对于被聚簇表的联接,可以减少磁盘 I/O。
-
对于被聚簇表的联接,可以提高访问速度。
-
只需更少的空间来存储相关的表和索引数据,因为簇键值不会为每行重复存储。
通常,簇表不适合以下情况:
-
会经常被更新的表。
-
经常需要全表扫描的表。
-
需要被截断的表。
索引化表簇概述
索引化聚簇是使用索引来查找数据的表簇。而簇索引是一个簇键上的 B 树索引。簇索引必须先被创建,然后才能将行插入到簇表中。
例 2-6 创建表簇和相关索引
假定使用簇键 department_id 来创建聚簇 employees_departments_cluster ,如下例所示。
CREATE CLUSTER employees_departments_cluster(department_id NUMBER(4))
SIZE 512;CREATE INDEX idx_emp_dept_cluster ON CLUSTER employees_departments_cluster;
因为未指定 HASHKEYS 子句,所以 employees_departments_cluster 是一个索引化表簇。前面的示例是在簇键department_id上创建一个名为 idx_emp_dept_cluster 的索引
例 2-7 在索引簇中创建表
在该簇中创建雇员表和部门表,并指定 department_id 列为簇键,如下所示 (省略号表示放置列定义的地方):
CREATE TABLE employees ( ... )CLUSTER employees_departments_cluster (department_id);CREATE TABLE departments ( ... )CLUSTER employees_departments_cluster (department_id);
将行添加到 employees 表和 departments 表中。数据库在物理上将雇员表和部门表中每个部门的所有行都存储在相同的数据块中。数据库以堆的形式存储行,并使用索引定位他们。
图 2-5 显示了包含 employees 表和 departments 表所构成的 employees_departments_cluster 表簇。数据库将部门 20 的雇员所在的行存储在一起,部门 110 的行也存储在一起,依次类推。如果表未被聚簇,那么数据库将不保证相关的行被存储在一起。
图 2-5 簇表数据
Description of "Figure 2-5 Clustered Table Data"
B 树簇索引把簇键值与数据所在块的数据库块地址 (DBA) 关联起来。例如,键 20 的索引条目显示包含部门 20 的雇员数据所在块的地址:
20,AADAAAA9d
与非聚簇表上的索引类似,簇索引被单独管理,簇索引也可以与表簇存在于不同的表空间中。
See Also:
-
"Introduction to Indexes"
-
《Oracle Database Administrator’s Guide》了解如何创建和管理索引化聚簇
-
《Oracle Database SQL Language Reference》了解 CREATE CLUSTER 的语法和语义
哈希簇概述
除了索引键被替换为一个哈希函数之外, 哈希簇就像一个索引化聚簇。它没有单独的簇索引存在。对一个哈希簇来说,数据本身就是索引。
对已索引的表或索引化聚簇,数据库用存储在一个单独的索引中的键值查找表行。要查找或存储已索引的表或索引化聚簇中的一个行,数据库必须执行至少两个 I/O 操作:
-
为查找或存储在索引中的键值,需要一个或多个 I/O
-
为读取或写入表或表簇中的行,还需要一个 I/O
为查找或存储在哈希簇中的一个行,数据库将哈希函数应用到行的簇键值。得出的哈希值对应到一个聚簇中的数据块,数据库则按发出的语句读写该块。
哈希是一种可选的表数据存储方法,用来提高数据检索的性能。在满足以下条件时,哈希簇可能是有益的:
-
经常被查询,但不经常被修改的表。
-
哈希键列经常使用等值条件查询,例如, WHERE department_id =20。对于这样的查询,簇键值是已经过哈希运算的。哈希键值直接指向存储行的磁盘区域。
-
您可以合理地猜出哈希键的数目,和每个键值所存储的数据的大小。
创建哈希簇
与创建索引化聚簇类似,使用 CREATE CLUSTER 语句创建一个哈希簇,但得加上一个哈希键。哈希值的数量取决于哈希键。
与索引化聚簇的键类似,簇键是由簇中各表共享的单键列或复合键列。哈希键值是插入到簇键列的实际值或可能值。例如,如果簇键是 department_id,那么哈希键键值可能是 10、 20、 30,等等。
Oracle 数据库使用一个哈希函数,接受任意多个哈希键值作为输入,经过排序并哈希到有限数量的桶。每个桶都有一个唯一的数字 ID,称为哈希值。每个哈希值都映射到哈希键值(部门 10、 20、 30,等等)对应行所在块的数据库块地址。
在下面的例子中,可能存在的部门的数量是 100,所以 HASHKEYS 设置为 100。
CREATE CLUSTER employees_departments_cluster(department_id NUMBER(4))
SIZE 8192 HASHKEYS 100;
创建聚簇 employees_departments_cluster 后,可以在该簇中创建 employees 表和 departments 表。然后可以将数据如同索引化簇一样装载到哈希簇中。
另见:
-
"Overview of Indexed Clusters"
-
《Oracle Database Administrator’s Guide》了解如何创建和管理哈希簇
查询哈希簇
在散列簇的查询中,数据库确定如何哈希用户输入的键值。
例如,用户经常执行如下的查询, 为 p_id 输入不同的部门 ID 号:
SELECT *
FROM employees
WHERE department_id = :p_id;SELECT *
FROM departments
WHERE department_id = :p_id;SELECT *
FROM employees e, departments d
WHERE e.department_id = d.department_id
AND d.department_id = :p_id;
如果某个用户查询 department_id = 20 的雇员,那么数据库可能将此值哈希到 bucket 77。如果某个用户查询 department_id = 10 的雇员,那么数据库可能将此值哈希到 bucket 15。数据库使用内部生成的哈希值来定位包含请求的部门的雇员行所在的数据块。
下图将哈希簇段显示为一行水平排列的块。如图所示,查询只需单个 I/O就可以检索到数据。
图 2-6 从散列簇检索数据
Description of "Figure 2-6 Retrieving Data from a Hash Cluster"
哈希簇的一个限制是在非索引化簇键上的范围扫描不可用。假设在创建哈希簇时没有为哈希簇创建单独的索引。对部门 id 在 20 和 100 之间的查询不能使用哈希算法,因为它不能哈希 20 和 100 之间的每个可能的值。因为没有索引存在,数据库必须执行完全扫描。
另见:
"Index Range Scan"
哈希簇变体
单表哈希簇是哈希簇的一个优化版本,一次只支持一个表。哈希键和行之间存在一一映射。
当用户需要通过主键快速访问表时,单表哈希簇会很有用。例如,用户经常通过 employee_id 查找一个雇员表中的雇员记录。
排序哈希簇存储哈希函数的每个值对应的行,通过某种方式,数据库可以有效地把他们按已排定的顺序返回。数据库在内部执行优化的排序。对于需要总是按排定顺序来消费数据的应用程序,这种技术可能会更快的检索到数据。例如,应用程序可能总是按订单表的 order_date 列进行排序。
另见:
《Oracle Database Administrator’s Guide》了解如何创建单表簇和排序哈希簇
哈希簇存储
Oracle 数据库为哈希簇分配空间的方式不同于索引化簇。
在创建哈希簇的示例中,HASHKEYS 指定可能存在的部门数,而 SIZE 指定与每个部门相关联的数据的大小。则数据库将基于以下公式计算存储空间:
HASHKEYS * SIZE / database_block_size
因此,如果哈希簇创建示例中的块大小是 4096 字节,那么数据库至少要为该哈希簇分配 200 个数据块。
Oracle 数据库并不限制您可以向哈希簇中插入的哈希键值的数目。例如,即使 HASHKEYS 是 100,但这不会阻止你向部门表中插入 200 个不同的部门。但是,当(输入的) 哈希键值的数目超过哈希键(HASHKEYS 的设定) 数目时,哈希簇检索效率会降低。
为说明检索问题,假定图 2-6 中的块 100 内全是部门 20 的行。用户向部门表中插入一个新的部门,其 department_id 为 43。部门的数量超过 HASHKEYS 的值,因此数据库对 department_id 43 进行哈希处理得到哈希值 77,这与 department_id 20 的哈希值相同。多个输入的值被哈希为相同的输出值称为哈希冲突。
当用户为部门 43 往簇中插入行时,数据库不能将这些行存储在块 100 中,因为它已经满了。数据库将块 100 链接到一个新的溢出块,比如说块 200,并将插入的行存储在新块中。这两个块 100 和 200 现在都可以存储部门 20 或 43 的数据。如图 2-7 ,查询部门 20 或 43 现在需要两个 i/o 才能检索到数据: 块 100 和其相关联的块 200。您可以通过重新创建具有不同 HASHKEYS 值的表簇来解决这个问题。
图 2-7 当哈希冲突发生时,从哈希簇检索数据
Description of "Figure 2-7 Retrieving Data from a Hash Cluster When a Hash Collision Occurs"
另见:
《Oracle Database Administrator’s Guide》了解如何在哈希簇中管理空间
Attribute-Clustered 表的概述
attribute-clustered表是一种堆组织的表,它根据用户指定的聚簇指令在磁盘上紧密地存储数据。这些指令在单个或多个表中指定列。
指令如下:
-
CLUSTERING ... BY LINEAR ORDER 指令根据指定的列对表中的数据进行排序。
当查询限定聚类子句中指定的列的前缀时,可以考虑使用默认的聚簇语句 BY LINEAR ORDER。例如,如果对sh.sales的查询通常指定客户ID或客户ID和产品ID,那么可以使用线性列顺序cust_id、prod_id对表中的数据进行聚簇。
-
CLUSTERING ... BY INTERLEAVED ORDER指令使用一种特殊的算法(类似于Z-order函数)对一个或多个表中的数据进行排序,这种算法会减少多列 I/O。
当查询指定各种列组合时,考虑使用
BY INTERLEAVED ORDER
聚簇语句。例如,如果对sh.sales的查询在不同的订单中指定不同的维度,那么您可以根据这些维度中的列对 sales 表中的数据进行聚簇。
Attribute Clustering 仅适用于直接路径插入操作。对于传统的DML,建议不使用。
Attribute-Clustered 表的优点
属性集群表的主要优点是减少I/O,这可以显著降低表扫描的I/O成本和CPU成本。I/O减少要么与区域一起发生,要么通过对聚簇值在磁盘上进行更紧密的物理紧密来减少物理I/O。
attribute-clustered 表有如下优点:
-
您可以基于星型模式中的维度列对事实表进行聚簇。
在星型模式中,大多数查询限定维度表而不是事实表,因此按事实表列聚类是无效的。Oracle数据库支持对维度表中的列进行聚簇。
-
I/O的减少可以在几种不同情况下发生:
-
与 Oracle Exadata 存储索引、Oracle In-Memory min/max pruning 或区域映射一起使用时
-
在OLTP应用程序中,用于限定前缀并使用 attribute clustering 线性排序的查询
-
在聚类列的子集上使用 BY INTERLEAVED ORDER 进行聚簇
-
-
Attribute Clustering 可以提高数据压缩,从而间接提高表扫描成本。
当磁盘上的相同值彼此接近时,数据库可以更容易地压缩它们。
-
Oracle数据库不需要索引的存储和维护成本。
另见:
《Oracle Database Data Warehousing Guide》有关attribute-clustered的更多优点
连接 Attribute Clustered 表
基于连接列的 Attribute Clustering 称为join attribute clustering。与表簇不同,join attribute clustered 表不存储来自同一数据库块中的一组表的数据。
举个例子,考虑一个attribute-clustered 表sales和一个维度表products的连接。sales表只包含sales表中的行,但是行顺序是基于从products表连接的列的值。在数据移动、直接路径插入和 CREATE TABLE AS SELECT 操作期间执行适当的连接。相反,如果sales和products位于标准表簇中,则数据块将包含来自这两个表的行。
另见:
《Oracle Database Data Warehousing Guide》 以了解有关 join attribute clustering 的更多信息
I/O减少使用区域
区域是一组连续的数据块,用于存储相关列的最小值和最大值。当SQL语句包含存储在区域中的列上的谓词时,数据库将谓词值与存储在区域中的最小值和最大值进行比较,以确定在SQL执行期间读取哪些区域。
I/O减少能够满足数据库查询跳过不包含数据的表或索引块。这种减少可以显著降低表扫描的I/O和CPU成本。
区域映射
区域映射是一种独立的访问结构,它将数据块划分为多个区域。Oracle数据库将每个区域映射实现为一种物化视图类型。
每当在表上指定聚簇时,数据库都会在指定的聚簇列上自动创建区域映射。区域映射将列的最小值和最大值与attribute-clustered 表中的连续数据块关联起来。attribute-clustered 表使用区域映射来做到I/O缩减。
您可以创建不使用区域映射的 attribute-clustered 表。您还可以在不使用 attribute-clustered 表的情况下创建区域映射。例如,您可以在一个表上创建一个区域映射,该表的行在一组列上自然排序,例如股票交易表,该表的交易按时间排序。执行DDL语句来创建、删除和维护区域映射。
另见:
-
"Overview of Materialized Views"
-
《Oracle Database Data Warehousing Guide》学习更多关于zone maps
区域映射:对比
要对区域映射进行松散的类比,请考虑销售经理使用的类似于数据块的格子书架,。
每个格子都有收据(行)描述出售给客户的衬衫,并按装运日期排序。在这个类比中,区域地图就像一堆索引卡。每张卡片对应于格子的“区域”(连续范围),例如格子1-10。对于每个区域,卡片列出了存储在该区域的收据的最小和最大装运日期。
当有人想知道哪件衬衫在特定日期发出时,经理会翻看卡片,直到她找到包含请求日期的日期范围,标记格子区域,然后只在该区域中搜索格子以查找请求的收据。这样,经理就不用在书架上的每一个格子间里找收据了。
区域映射:样例
这个示例演示了区域映射如何删除谓词包含常量的查询中的数据。
假设您创建了以下 lineitem 表:
CREATE TABLE lineitem ( orderkey NUMBER , shipdate DATE ,receiptdate DATE ,destination VARCHAR2(50) ,quantity NUMBER );
这张 lineitem 表包含四个数据块,一个块里包含两条数据。表2-3 展示了表里的八条数据。
表 2-3 lineitem 表的数据块
Block | orderkey | shipdate | receiptdate | destination | quantity |
---|---|---|---|---|---|
1 |
1 |
1-1-2014 |
1-10-2014 |
San_Fran |
100 |
1 |
2 |
1-2-2014 |
1-10-2014 |
San_Fran |
200 |
2 |
3 |
1-3-2014 |
1-9-2014 |
San_Fran |
100 |
2 |
4 |
1-5-2014 |
1-10-2014 |
San_Diego |
100 |
3 |
5 |
1-10-2014 |
1-15-2014 |
San_Fran |
100 |
3 |
6 |
1-12-2014 |
1-16-2014 |
San_Fran |
200 |
4 |
7 |
1-13-2014 |
1-20-2014 |
San_Fran |
100 |
4 |
8 |
1-15-2014 |
1-30-2014 |
San_Jose |
100 |
您可以使用 CREATE MATERIALIZED ZONEMAP 语句在 lineitem 表上创建一个区域映射。每个区域包含2个块,存储着 orderkey、shipdate和receiptdate列的最小和最大值。表 2-4 显示了区域映射。
表 2-4 lineitem 表的区域映射
Block Range | min orderkey | max orderkey | min shipdate | max shipdate | min receiptdate | max receiptdate |
---|---|---|---|---|---|---|
1-2 |
1 |
4 |
1-1-2014 |
1-5-2014 |
1-9-2014 |
1-10-2014 |
3-4 |
5 |
8 |
1-10-2014 |
1-15-2014 |
1-15-2014 |
1-30-2014 |
当您执行以下查询时,数据库可以读取区域映射,然后只扫描block 1和block 2,因为日期1-3-2014 介于最小和最大日期之间:
SELECT * FROM lineitem WHERE shipdate = '1-3-2014';
另见
-
《Oracle Database Data Warehousing Guide》 学习如何使用 zone maps
-
《Oracle Database SQL Language Reference》 用于 CREATE MATERIALIZED ZONEMAP 语句的语法和语义
使用Linear Ordering 的 Attribute-Clustered 表
表的线性排序模式根据用户指定的属性按特定顺序将行划分为范围。Oracle数据库支持通过主外键关系连接的单个或多个表的线性排序。
例如,sales表将cust_id和prod_id列划分为范围,然后将这些范围聚集在磁盘上。当为表指定 BY LINEAR ORDER 命令时,当谓词指定前缀列或命令中的所有列时,可以显著减少I/O。
假设查询sales表通常指定客户ID或客户ID和产品ID的组合。您可以创建一个 attribute-clustered 表,以便此类查询可以得到I/O减少这个优点:
CREATE TABLE sales
(prod_id NOT NULL NUMBER
, cust_id NOT NULL NUMBER
, amount_sold NUMBER(10,2) ...
)
CLUSTERING BY LINEAR ORDER (cust_id, prod_id)YES ON LOAD YES ON DATA MOVEMENTWITH MATERIALIZED ZONEMAP;
同时限定cust_id和prod_id列或前缀cust_id的查询将使I/O缩减。仅限定prod_id的查询不会发生显著的I/O减少,因为prod_id是BY LINEAR ORDER子句的后缀。下面的示例展示了数据库如何在表扫描期间减少I/O。
例 2-8 只指定cust_id
应用程序发出以下查询:
SELECT * FROM sales WHERE cust_id = 100;
因为sales表是一个 BY LINEAR ORDER 的聚簇,数据库必须只读取包含cust_id值为100的区域。
例 2-9 指定 prod_id 和 cust_id
应用程序发出以下查询:
SELECT * FROM sales WHERE cust_id = 100 AND prod_id = 2300;
因为sales表是一个 BY LINEAR ORDER 的聚簇,数据库必须只读取包含cust_id值100和prod_id值2300的区域。
另见:
-
《Oracle Database Data Warehousing Guide》 学习如何让簇表使用 BY LINEAR ORDER
-
《Oracle Database SQL Language Reference》用于
BY LINEAR ORDER
字句的语法和语义
具有交错排序的 Attribute-Clustered 表
交错排序使用一种类似于Z-order 的技术。
交错排序使数据库能够基于聚簇列中的谓词的任何子集删除I/O。交错排序对于数据仓库中的维度层次结构非常有用。
与具有线性排序的 attribute-clustered 表一样,Oracle数据库支持通过主外键关系连接的单个或多个表的交错排序。attribute-clustered 表以外的表的列必须通过外键链接来连接attribute-clustered 表。
大型数据仓库经常在星型模式中组织数据。维度表使用父-子层次结构,并通过外键连接到事实表。通过交错顺序对事实表进行聚簇,数据库可以使用特殊的函数在表扫描期间跳过维度列中的值。
例 2-10 交叉排序的例子
假设数据仓库包含一个 sales 事实表及其两个维度表:customers 和 products。大多数查询在客户表层次结构(cust_state_province, cust_city)和产品层次结构(prod_category, prod_subcategory)上都有谓词。您可以对sales表使用交错排序,如下面的部分语句所示:
CREATE TABLE sales
(prod_id NUMBER NOT NULL
, cust_id NUMBER NOT NULL
, amount_sold NUMBER(10,2) ...
)
CLUSTERING salesJOIN products ON (sales.prod_id = products.prod_id)JOIN customers ON (sales.cust_id = customers.cust_id)BY INTERLEAVED ORDER(( products.prod_category, products.prod_subcategory),( customers.cust_state_province, customers.cust_city))
WITH MATERIALIZED ZONEMAP;
假设应用程序查询联接中的sales、products和customers表。查询指定谓词 customers.prod_category 和 customers_cust_state_province,如下:
SELECT cust_city, prod_sub_category, SUM(amount_sold)
FROM sales, products, customers
WHERE sales.prod_id = products.prod_id
AND sales.cust_id = customers.cust_id
AND customers.prod_category = 'Boys'
AND customers.cust_state_province = 'England - Norfolk'
GROUP BY cust_city, prod_sub_category;
在前面的查询中,prod_category 和 cust_state_province 列是 CREATE TABLE 示例中显示的聚簇定义的一部分。在扫描 sales 表的过程中,数据库可以查询区域映射,只访问该区域中的 rowids。
另见:
-
"Overview of Dimensions"
-
《Oracle Database Data Warehousing Guide》 学习如何让簇表使用交叉排序
-
《Oracle Database SQL Language Reference》用于
BY INTERLEAVED ORDER
子句的语法和语义
临时表概述
临时表用于存放只存在于某个事务或会话期间的数据。
临时表中的数据是会话私有的,这意味着每个会话只可以查看和修改自己的数据。
可以创建全局临时表或私有临时表。下表显示了它们之间的本质区别。
表 2-5 临时表特征
特征 | 全局 | 私有 |
---|---|---|
命名规则 | 与永久表相同 | 必须以ORA$PTT_开头 |
表定义的可见性 | 所有会话 | 只有创建表的会话 |
表定义的存储 | 磁盘 | 内存 |
类型 | 特定事务(COMMIT DELETE 行)或特定会话(COMMIT PRESERVE 行) | 事务特定的(COMMIT DROP 定义)或会话特定的(COMMIT PRESERVE 定义) |
数据库自动为某些类型的查询创建第三种临时表,称为 cursor-duration 临时表。
另见:
《Oracle Database SQL Tuning Guide》了解关于 cursor-duration 临时表
临时表的用途
临时表对于必须缓冲中间结果集的应用程序非常有用。
例如,一个计划应用程序使学生可以创建可选的学期课程计划。全局临时表中的一行表示一个计划。每个课程计划由临时表中的一行表示。在会话期间,课程计划数据是私有的。当某个学生确定了课程计划,应用程序会将其所选计划移入永久表。数据库自动删除位于全局临时表中的调度数据。
私有临时表对于动态报告应用程序非常有用。例如,客户资源管理(Customer Resource Management,CRM)应用程序可以无限期地作为同一个用户连接,同时有多个会话处于活动状态。每个会话为每个新事务创建一个名为 ORA$PTT_crm 的私有临时表。应用程序可以对每个会话使用相同的表名,但可以更改定义。数据和定义仅对会话可见。表定义一直保持到事务结束或表被手动删除为止。
临时表中的段分配
与永久表类似,全局临时表是数据字典中静态定义的持久对象。对于私有临时表,元数据仅存在于内存中,但可以驻留在磁盘上的临时表空间中。
对于全局和私有临时表,当会话第一次插入数据时,数据库分配临时段。在会话中加载数据之前,表将显示为空。对于特定于事务的临时表,数据库在事务结束时释放临时段。对于特定于会话的临时表,数据库在会话结束时释放临时段。
另见:
"Temporary Segments"
创建临时表
使用 CREATE ... TEMPORARY TABLE 语句创建一个临时表。
指定 GLOBAL TEMPORARY TABLE 或 PRIVATE TEMPORARY TABLE。在这两种情况下,ON COMMIT 子句指定表中的数据是特定于事务 (默认值),还是特定于会话。你是在为数据库本身创建一个临时表,而不是为每个 PL/SQL 存储过程。
您可以使用 CREATE INDEX 语句为全局(而非私有)临时表创建索引。这些索引也是临时的。在索引中的数据与临时表中的数据具有相同的会话或事务范围。您还可以在全局临时表上创建一个视图或触发器。
另见:
-
"Overview of Views"
-
"Overview of Triggers"
-
《Oracle Database Administrator’s Guide》了解如何创建和管理临时表
-
《Oracle Database SQL Language Reference》关于 CREATE ... TEMPORARY TABLE 的语法和语义
外部表概述
外部表访问外部数据源中的数据,如同此数据是在数据库中的表中一样。
数据可以是提供访问驱动程序的任何格式。您可以使用 SQL 、 PL/SQL、和 Java 查询外部数据。
外部表的目的
当Oracle数据库应用程序必须访问非关系数据时,外部表非常有用。
例如,一个基于 SQL 的应用程序,可能需要访问一个文本文件中的记录。记录的形式如下:
100,Steven,King,SKING,515.123.4567,17-JUN-03,AD_PRES,31944,150,90
101,Neena,Kochhar,NKOCHHAR,515.123.4568,21-SEP-05,AD_VP,17000,100,90
102,Lex,De Haan,LDEHAAN,515.123.4569,13-JAN-01,AD_VP,17000,100,90
您可以创建一个外部表,把这个文本文件复制到外部表定义中指定的位置,并使用 SQL 查询该文本文件中的记录。类似地,可以使用外部表对 JSON 文档或 LOB 进行只读访问。
在数据仓库环境中,外部表对于执行常见的提取、转换和加载(ETL)任务非常有用。例如,外部表使得数据加载阶段和数据转换阶段对接,这种技术消除了为进一步处理数据库中的数据而在数据库内存放中间数据的需要。
从Oracle数据库12c第2版(12.2)开始,您可以在虚拟或非虚拟列上对外部表进行分区。因此,您可以利用 partition pruning 和 partition-wise joins 所提供的性能改进。例如,可以使用分区外部表来分析存储在 Hadoop 分布式文件系统(HDFS)或NoSQL数据库上的大量非关系数据。
另见:
Overview of Partitions
外部表访问驱动程序
访问驱动程序是一个 API,它为数据库解释外部数据。访问驱动程序在数据库内运行,数据库使用该驱动程序来读取外部表中的数据。访问驱动程序和外部表层负责对数据文件中的数据进行转换,使它与外部表定义匹配。
下图表示对外部数据是如何被SQL访问的
图 2-8 外部表
Description of "Figure 2-8 External Tables"
Oracle为外部表提供以下访问驱动程序:
-
ORACLE_LOADER (默认)
允许通过 SQL*Loader 对外部文件进行只读访问。您不能使用 ORACLE_LOADER 驱动程序创建、 更新、或追加数据到外部文件。
-
ORACLE_DATAPUMP
使您能够卸载或加载外部数据。卸载操作从数据库读取数据并将数据插入由一个或多个外部文件表示的外部表中。创建外部文件后,无法更新或将追加数据到外部文件。加载操作读取外部表并将其数据加载到数据库中。
-
ORACLE_HDFS
允许提取存储在 Hadoop 分布式文件系统(HDFS)中的数据。
-
ORACLE_HIVE
启用对存储在 Apache Hive 数据库中的数据的访问。源数据可以存储在HDFS、HBASE、Cassandra或其他系统中。与其他访问驱动程序不同,您不能指定位置,因为 ORACLE_HIVE 从外部元数据存储中获取位置信息。
创建外部表
在数据库内部,创建一个外部表意味着在数据字典中创建元数据。与普通的表不同,外部表不描述存储在数据库中的数据,也不会描述数据在外部是如何存储的。外部表的元数据描述了外部表层必须如何提供数据给数据库。
CREATE TABLE ... ORGANIZATION EXTERNAL 语句包含两部分。外部表定义描述列类型。这个定义像一个视图,使您可以使用 SQL 查询外部数据,而不用将其加载到数据库。该语句的第二部分将外部数据映射到列。
外部表是只读的,除非它是使用 CREATE TABLE AS SELECT 和 ORACLE_DATAPUMP 访问驱动程序创建的。外部表有些限制,包括不支持索引列和列对象。
另见:
-
《Oracle Database Utilities》了解外部表
-
《Oracle Database Administrator's Guide》学习管理外部表、外部连接和目录对象
-
《Oracle Database SQL Language Reference》用于创建和查询外部表的信息
对象表概述
Oracle 对象类型是具有名称、 属性、和方法的用户定义类型。对象表是一种特殊的表,其中每一行表示一个对象。对象类型使得对现实世界中的实体(如客户和采购单等),作为对象在数据库中进行建模成为可能。
对象类型定义逻辑结构,但不会创建存储。下面的示例创建了一个名为 department_typ 的对象类型:
CREATE TYPE department_typ AS OBJECT( d_name VARCHAR2(100),d_address VARCHAR2(200) );
/
下面的示例 CREATE TABLE 语句创建一个名为 departments_obj_t 的对象表,其对象类型为 department_typ ,然后向表中插入一行。此表的属性(列)派生自该对象类型的定义。
CREATE TABLE departments_obj_t OF department_typ;
INSERT INTO departments_obj_t VALUES ('hr', '10 Main St, Sometown, CA');
与关系表列相似,对象表只能包含同种类型的行,即具有同种声明类型的对象实例。默认情况下,对象表中的每个行对象都有一个相关联的逻辑对象标识符 (OID),以便在该对象表中唯一地标识该对象。对象表的 OID 列是一个隐藏列。
另见:
-
《Oracle Database Object-Relational Developer's Guide》了解 Oracle 数据库中的对象-关系特性
-
《Oracle Database SQL Language Reference》了解 CREATE TYPE 的语法和语义