Oracle官方文档翻译《Database Concepts 23ai》第6章-分区、视图及其他模式对象
Oracle官方文档翻译《Database Concepts 23ai》第6章-分区、视图及其他模式对象
6 Partitions, Views, and Other Schema Objects(6 分区、视图及其他模式对象)
尽管表和索引是最重要且最常用的模式对象,但数据库还支持多种其他类型的模式对象,本章将讨论其中最常见的类型。
Overview of Partitions(分区概述)
在 Oracle 数据库中,分区功能允许将非常大的表和索引分解为更小、更易于管理的片段,这些片段称为分区。每个分区都是独立的对象,拥有自己的名称,还可选择性地拥有自己的存储特征。
用一个类比来理解分区:假设人力资源经理有一个大箱子,里面装着员工文件夹,每个文件夹都记录了员工的入职日期。查询通常会针对特定月份入职的员工。满足这类需求的一种方法是为员工入职日期创建索引,标明文件夹在大箱子中的分布位置;而分区策略则是使用多个小箱子,每个小箱子只装对应月份入职员工的文件夹。
使用小箱子有多个优势:当需要检索 6 月份入职员工的文件夹时,人力资源经理只需取出 6 月份的小箱子即可;此外,若某个小箱子暂时损坏,其他小箱子仍可正常使用;搬迁办公室也会更轻松,因为经理无需搬运一个沉重的大箱子,而是可以分批次搬运多个小箱子。
从应用程序的角度来看,分区表仅表现为一个模式对象,SQL 语句无需修改即可访问分区表。分区功能对多种数据库应用程序都很有用,尤其适用于管理大量数据的场景,其优势包括:
-
Increased availability(提高可用性)
某个分区不可用并不会导致整个对象不可用。查询优化器会自动从查询计划中排除未被引用的分区,因此当分区不可用时,查询不会受到影响。 -
Easier administration of schema objects(简化模式对象管理)
分区对象的各个片段既可统一管理,也可单独管理。DDL 语句可针对分区而非整个表或索引进行操作,因此能够拆分重建索引或表等资源密集型任务。例如,可以一次移动一个表分区,若过程中出现问题,只需重新执行该分区的移动操作,无需重新移动整个表;此外,删除分区可避免执行大量DELETE
语句。 -
Reduced contention for shared resources in OLTP systems(减少 OLTP 系统中共享资源的竞争)
在部分 OLTP 系统中,分区可减少对共享资源的竞争。例如,DML 操作会分散到多个段上,而非集中在一个段上。 -
Enhanced query performance in data warehouses(提升数据仓库中的查询性能)
在数据仓库中,分区可加快即席查询的处理速度。例如,包含一百万行数据的销售表可按季度进行分区。
相关参考:
- 《Oracle Database VLDB and Partitioning Guide》(《Oracle 数据库 VLDB 与分区指南》):了解分区的相关介绍。
Partition Characteristics(分区特征)
表或索引的每个分区必须具有相同的逻辑属性,例如列名、数据类型和约束。
所有分区在表中共享相同的列定义和约束定义,但每个分区可拥有独立的物理属性,例如所属的表空间。
Partition Key(分区键)
分区键是一组一个或多个列,用于确定分区表中的每行应归属到哪个分区。每行都会被明确分配到一个分区中。
例如,在销售表中,可将 time_id
列指定为范围分区的键。数据库会根据该列中的日期是否落在指定范围内,将行分配到相应分区。Oracle 数据库会通过分区键自动将插入、更新和删除操作定向到合适的分区。
Partitioning Strategies(分区策略)
Oracle 分区提供多种分区策略,用于控制数据库将数据放入分区的方式,基本策略包括范围分区、列表分区和哈希分区。
单级分区仅使用一种数据分布方式,例如仅使用列表分区或仅使用范围分区;在复合分区中,表先通过一种数据分布方式进行分区,然后每个分区再通过第二种数据分布方式进一步划分为子分区。例如,可对 channel_id
列使用列表分区,对 time_id
列使用范围子分区。
Example 6-1 Sample Row Set for Partitioned Table(示例 6-1:分区表的示例行集)
本分区示例假设你要向分区表 sales
中插入以下行数据:
PROD_ID | CUST_ID | TIME_ID | CHANNEL_ID | PROMO_ID | QUANTITY_SOLD | AMOUNT_SOLD |
---|---|---|---|---|---|---|
116 | 11393 | 05-JUN-99 | 2 | 999 | 1 | 12.18 |
40 | 100530 | 30-NOV-98 | 9 | 33 | 1 | 44.99 |
118 | 133 | 06-JUN-01 | 2 | 999 | 1 | 17.12 |
133 | 9450 | 01-DEC-00 | 2 | 999 | 1 | 31.28 |
36 | 4523 | 27-JAN-99 | 3 | 999 | 1 | 53.89 |
125 | 9417 | 04-FEB-98 | 3 | 999 | 1 | 16.86 |
30 | 170 | 23-FEB-01 | 2 | 999 | 1 | 8.8 |
24 | 11899 | 26-JUN-99 | 4 | 999 | 1 | 43.04 |
35 | 2606 | 17-FEB-00 | 3 | 999 | 1 | 54.94 |
45 | 9491 | 28-AUG-98 | 4 | 350 | 1 | 47.45 |
Range Partitioning(范围分区)
在范围分区中,数据库根据分区键值的范围将行映射到分区。范围分区是最常见的分区类型,通常与日期结合使用。
假设通过以下 SQL 语句创建分区表 time_range_sales
,并将 time_id
列作为分区键:
CREATE TABLE time_range_sales (
prod_id NUMBER(6),
cust_id NUMBER,
time_id DATE,
channel_id CHAR(1),
promo_id NUMBER(6),
quantity_sold NUMBER(3),
amount_sold NUMBER(10,2)
)
PARTITION BY RANGE (time_id)
(
PARTITION SALES_1998 VALUES LESS THAN (TO_DATE('01-JAN-1999','DD-MON-YYYY')),
PARTITION SALES_1999 VALUES LESS THAN (TO_DATE('01-JAN-2000','DD-MON-YYYY')),
PARTITION SALES_2000 VALUES LESS THAN (TO_DATE('01-JAN-2001','DD-MON-YYYY')),
PARTITION SALES_2001 VALUES LESS THAN (MAXVALUE)
);
之后,向 time_range_sales
表加载示例 6-1 中的行数据,代码会展示这四个分区中的行分布情况。数据库会根据 PARTITION BY RANGE
子句中指定的规则,依据 time_id
值为每行选择对应的分区。范围分区键值决定了指定分区的非包含性上限。
Interval Partitioning(间隔分区)
间隔分区是范围分区的扩展。
若插入的数据超出了现有范围分区的范围,Oracle 数据库会自动创建指定间隔的分区。例如,可创建一个销售历史表,将每个月的数据存储在独立的分区中。
间隔分区可避免显式创建范围分区,几乎所有按固定间隔创建新分区的范围分区表,都可使用间隔分区。除非需要创建不同间隔的范围分区,或始终要设置特定的分区属性,否则建议使用间隔分区。
使用间隔分区时,必须至少指定一个范围分区。范围分区键值决定了范围分区的上限(称为转换点),数据库会为值超出转换点的数据自动创建间隔分区。每个间隔分区的下限是前一个范围分区或间隔分区的包含性上限。因此,在示例 6-2 中,值 01-JAN-2011
会落在分区 p2
中。
数据库会为超出转换点的数据创建间隔分区。间隔分区通过指示数据库创建指定范围或间隔的分区,对范围分区进行了扩展。当向表中插入的数据超出所有现有范围分区时,数据库会自动创建分区。在示例 6-2 中,分区 p3
包含分区键 time_id
值大于或等于 01-JAN-2013
的行。
Example 6-2 Interval Partitioning(示例 6-2:间隔分区)
假设创建一个销售表,包含四个不同宽度的分区,并指定在转换点(2013 年 1 月 1 日)之后,数据库应按一个月的间隔创建分区。分区 p3
的上限即为转换点,p3
及其以下的所有分区属于范围部分,而其以上的所有分区属于间隔部分。
CREATE TABLE interval_sales
(
prod_id NUMBER(6),
cust_id NUMBER,
time_id DATE,
channel_id CHAR(1),
promo_id NUMBER(6),
quantity_sold NUMBER(3),
amount_sold NUMBER(10,2)
)
PARTITION BY RANGE (time_id) INTERVAL(NUMTOYMINTERVAL(1, 'MONTH'))
(
PARTITION p0 VALUES LESS THAN (TO_DATE('1-1-2010', 'DD-MM-YYYY')),
PARTITION p1 VALUES LESS THAN (TO_DATE('1-1-2011', 'DD-MM-YYYY')),
PARTITION p2 VALUES LESS THAN (TO_DATE('1-7-2012', 'DD-MM-YYYY')),
PARTITION p3 VALUES LESS THAN (TO_DATE('1-1-2013', 'DD-MM-YYYY'))
);
向表中插入一条 2014 年 10 月 10 日的销售记录:
SQL> INSERT INTO interval_sales VALUES (39,7602,'10-OCT-14',9,null,1,11.79);
1 row created.
查询 USER_TAB_PARTITIONS
视图会发现,数据库为这条 10 月 10 日的销售记录创建了一个新分区,因为该销售日期晚于转换点:
SQL> COL PNAME FORMAT a9
SQL> COL HIGH_VALUE FORMAT a40
SQL> SELECT PARTITION_NAME AS PNAME, HIGH_VALUE
2 FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'INTERVAL_SALES';
| PNAME | HIGH_VALUE |
|----------|---------------------------------------------------------------------------|
| P0 | TO_DATE(' 2010-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN') |
| P1 | TO_DATE(' 2011-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN') |
| P2 | TO_DATE(' 2012-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN') |
| P3 | TO_DATE(' 2013-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN') |
| SYS_P1598| TO_DATE(' 2014-11-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN') |
相关参考:
- 如需了解更多关于间隔分区的信息,请参见《Oracle Database VLDB and Partitioning Guide》(《Oracle 数据库 VLDB 和分区指南》)。
List Partitioning(列表分区)
在列表分区中,数据库将离散值列表作为每个分区的分区键,分区键由一个或多个列组成。
通过列表分区,可控制单行如何映射到特定分区。借助列表,当用于标识数据的键不便于排序时,仍可对相关数据集进行分组和组织。
Example 6-3 List Partitioning(示例 6-3:列表分区)
假设通过以下语句创建列表分区表 list_sales
,并将 channel_id
列作为分区键:
CREATE TABLE list_sales (
prod_id NUMBER(6),
cust_id NUMBER,
time_id DATE,
channel_id CHAR(1),
promo_id NUMBER(6),
quantity_sold NUMBER(3),
amount_sold NUMBER(10,2)
)
PARTITION BY LIST (channel_id)
(
PARTITION even_channels VALUES ('2','4'),
PARTITION odd_channels VALUES ('3','9')
);
之后,向表中加载示例 6-1 中的行数据,代码会展示这两个分区中的行分布情况。数据库会根据 PARTITION BY LIST
子句中指定的规则,依据 channel_id
值为每行选择对应的分区:channel_id
值为 2 或 4 的行存储在 EVEN_CHANNELS
分区中,channel_id
值为 3 或 9 的行存储在 ODD_CHANNELS
分区中。
Hash Partitioning(哈希分区)
在哈希分区中,数据库通过对用户指定的分区键应用哈希算法,将行映射到分区。
行的目标分区由数据库对行应用的内部哈希函数决定。当分区数量为 2 的幂时,哈希算法能使行在所有分区中大致均匀分布。
哈希分区有助于将大型表拆分,提升可管理性——无需管理一个大型表,而是管理多个较小的片段。单个哈希分区损坏不会影响其他分区,且可独立恢复。在具有高更新竞争的 OLTP 系统中,哈希分区也很有用,例如,一个段会被拆分为多个片段,每个片段都可被更新,避免单个段出现竞争问题。
假设通过以下语句创建哈希分区表 hash_sales
,并将 prod_id
列作为分区键:
CREATE TABLE hash_sales (
prod_id NUMBER(6),
cust_id NUMBER,
time_id DATE,
channel_id CHAR(1),
promo_id NUMBER(6),
quantity_sold NUMBER(3),
amount_sold NUMBER(10,2)
)
PARTITION BY HASH (prod_id)
PARTITIONS 2;
之后,向表中加载示例 6-1 中的行数据,代码会展示这两个分区中可能的行分布情况,这些分区的名称由系统生成。
插入行时,数据库会尝试将行随机且均匀地分布到各个分区中,无法指定行应放入哪个分区——数据库会应用哈希函数,函数的结果将决定行所属的分区。
相关参考:
- 《Oracle Database VLDB and Partitioning Guide》(《Oracle 数据库 VLDB 与分区指南》):了解如何创建分区。
- 《Oracle Database SQL Language Reference》(《Oracle 数据库 SQL 语言参考》):查看
CREATE TABLE ... PARTITION BY
语句的示例。
Reference Partitioning(引用分区)
在引用分区中,子表的分区策略完全通过与父表的外键关系定义。父表中的每个分区,在子表中都有且仅有一个对应的分区。父表将父记录存储在特定分区中,子表则将子记录存储在对应的分区中。
例如,orders
表是 line_items
表的父表,在 order_id
列上定义了主键和外键,这两个表通过引用方式进行分区。若数据库将订单 233 存储在 orders
表的 Q3_2015
分区中,则会将订单 233 的所有订单项存储在 line_items
表的 Q3_2015
分区中;若向 orders
表添加 Q4_2015
分区,数据库会自动向 line_items
表添加 Q4_2015
分区。
引用分区的优势
- 对父表和子表使用相同的分区策略,可避免重复所有分区键列,减少手动反规范化的开销,并节省存储空间。
- 对父表执行的维护操作会自动在子表上执行。例如,向主表添加分区时,数据库会自动将此操作传播到其下级表。
- 数据库会自动对父表和子表中的分区执行分区级连接,提升性能。
引用分区可与所有基本分区策略(包括间隔分区)结合使用,也可将引用分区表创建为复合分区表。
Example 6-4 Creating Reference-Partitioned Tables(示例 6-4:创建引用分区表)
本示例创建父表 orders
,该表按 order_date
列进行范围分区;引用分区子表 order_items
包含四个分区(Q1_2015
、Q2_2015
、Q3_2015
和 Q4_2015
),每个分区包含与父表对应分区中订单相关的 order_items
行。
CREATE TABLE orders
(
order_id NUMBER(12),
order_date DATE,
customer_id NUMBER(6),
order_mode VARCHAR2(8),
order_status NUMBER(2),
sales_rep_id NUMBER(6),
order_total NUMBER(8,2),
promotion_id NUMBER(6),
CONSTRAINT orders_pk PRIMARY KEY(order_id)
)
PARTITION BY RANGE(order_date)
(
PARTITION Q1_2015 VALUES LESS THAN (TO_DATE('01-APR-2015','DD-MON-YYYY')),
PARTITION Q2_2015 VALUES LESS THAN (TO_DATE('01-JUL-2015','DD-MON-YYYY')),
PARTITION Q3_2015 VALUES LESS THAN (TO_DATE('01-OCT-2015','DD-MON-YYYY')),
PARTITION Q4_2015 VALUES LESS THAN (TO_DATE('01-JAN-2006','DD-MON-YYYY'))
);
CREATE TABLE order_items
(
order_id NUMBER(12) NOT NULL,
line_item_id NUMBER(3) NOT NULL,
product_id NUMBER(6) NOT NULL,
unit_price NUMBER(8,2),
quantity NUMBER(8),
CONSTRAINT order_items_fk FOREIGN KEY(order_id) REFERENCES orders(order_id)
)
PARTITION BY REFERENCE(order_items_fk);
相关参考:
- 《Oracle Database VLDB and Partitioning Guide》(《Oracle 数据库 VLDB 与分区指南》):了解引用分区的相关概述。
Composite Partitioning(复合分区)
在复合分区中,表先通过一种数据分布方式进行分区,然后每个分区再通过第二种数据分布方式进一步划分为子分区。因此,复合分区结合了基本的数据分布方式,某个分区的所有子分区共同构成数据的一个逻辑子集。
复合分区的优势
- 根据 SQL 语句的不同,对一个或两个维度执行分区剪枝可能会提升性能。
- 查询可对任一维度执行全分区级连接或部分分区级连接。
- 可对单个表执行并行备份和恢复。
- 分区数量比单级分区更多,这对并行执行可能有利。
- 可实现滚动窗口以支持历史数据,同时若许多语句能从分区剪枝或分区级连接中受益,还可在另一个维度上进行分区。
- 可根据分区键的标识,以不同方式存储数据。例如,可决定将特定产品类型的数据以只读压缩格式存储,而其他产品类型的数据保持未压缩状态。
范围分区、列表分区和哈希分区均可用作复合分区表的子分区策略。下图以图形方式展示了“范围-哈希”和“范围-列表”两种复合分区模式:
Figure 6-1 Composite Range-List Partitioning
复合分区表中的每个子分区都作为独立的段(segment)存储。因此,子分区的属性可能与表的属性不同,也可能与其所属分区的属性不同。
相关参考:
- 《Oracle Database VLDB and Partitioning Guide》(《Oracle 数据库 VLDB 和分区指南》):了解更多关于复合分区的信息。
Partitioned Tables(分区表)
分区表由一个或多个分区组成,这些分区可单独管理,且能独立于其他分区运行。
表要么是分区表,要么是非分区表。即使分区表仅包含一个分区,它也与非分区表不同——非分区表无法添加分区。
Segments for Partitioned Tables(分区表的段)
分区表由一个或多个表分区段组成。
若创建名为 hash_products
的分区表,数据库不会为该表分配表段,而是将每个表分区的数据存储在其自身的分区段中,每个表分区段包含表数据的一部分。
当外部表被分区时,所有分区都存储在数据库外部;在混合分区表中,部分分区存储在段中,而其他分区存储在外部。例如,sales
表的部分分区可能存储在数据文件中,其他分区则存储在电子表格中。
Compression for Partitioned Tables(分区表的压缩)
堆组织表的部分或所有分区可采用压缩格式存储。
压缩能节省存储空间,并可能加快查询执行速度,因此在数据仓库(插入和更新操作较少)和 OLTP 环境中都很有用。
可在表空间、表或表分区级别声明表压缩属性:若在表空间级别声明,則在该表空间中创建的表默认启用压缩;可修改表的压缩属性,但此修改仅适用于插入到该表的新数据。因此,单个表或分区可能同时包含压缩块和未压缩块,这能确保数据大小不会因压缩而增加——若压缩可能导致块大小增加,数据库不会对该块应用压缩。
Partitioned Indexes(分区索引)
分区索引与分区表类似,是被分解为更小、更易于管理片段的索引。
全局索引的分区独立于其创建所基于的表,而本地索引会自动与表的分区方式关联。与分区表一样,分区索引能提升可管理性、可用性、性能和可扩展性。
下图展示了索引分区的可选方案。
Figure 6-2 Index Partitioning Options
Local Partitioned Indexes(本地分区索引)
在本地分区索引中,索引的分区列、分区数量和分区边界与所基于的表完全相同。
每个索引分区仅与基础表的一个分区相关联,因此索引分区中的所有键仅引用存储在单个表分区中的行。通过这种方式,数据库会自动同步索引分区与其关联的表分区,使每个表-索引对相互独立。
本地分区索引的优势
- 可用性提升:某个分区中数据失效或不可用的操作,仅会影响该分区,不会影响其他分区。
- 分区维护简化:移动表分区或数据从分区中过期时,仅需重建或维护关联的本地索引分区;而在全局索引中,所有索引分区都需重建或维护。
- 分区的时间点恢复:若对某个分区执行时间点恢复,索引可恢复到恢复时间点(参见《Oracle Database Backup and Recovery User’s Guide》),无需重建整个索引。
以下示例为哈希分区表创建本地分区索引:在“哈希分区”示例中,已展示了创建分区表 hash_sales
(以 prod_id
列为分区键)的语句,下面的语句将在 hash_sales
表的 time_id
列上创建本地分区索引:
CREATE INDEX hash_sales_idx ON hash_sales(time_id) LOCAL;
在示意图中,hash_products
表有两个分区,因此 hash_sales_idx
也有两个分区,每个索引分区关联不同的表分区:索引分区 SYS_P38
为表分区 SYS_P33
中的行建立索引,索引分区 SYS_P39
为表分区 SYS_P34
中的行建立索引。
Figure 6-3 Local Index Partitions
无法显式向本地索引添加分区,只有在向基础表添加分区时,才会自动向本地索引添加新分区;同样,也无法显式从本地索引删除分区,只有在从基础表删除分区时,才会自动删除本地索引分区。
与其他索引类似,可在分区表上创建位图索引,但有一个限制:位图索引必须是分区表的本地索引,不能是全局索引。全局位图索引仅支持在非分区表上创建。
Local Prefixed and Nonprefixed Indexes(本地前缀索引与本地非前缀索引)
本地分区索引分为本地前缀索引和本地非前缀索引,定义如下:
- 本地前缀索引:分区键位于索引定义的前导位置。在“范围分区”的
time_range_sales
示例中,表按time_id
列进行范围分区,若本地索引以time_id
列为首列,则该索引为本地前缀索引。 - 本地非前缀索引:分区键不在索引列列表的前导位置,甚至可能不在列表中。在“本地分区索引”的
hash_sales_idx
示例中,由于分区键product_id
不在索引列列表的前导位置,因此该索引为本地非前缀索引。
两种类型的索引都可利用分区剪枝(也称分区排除)——优化器通过排除无需考虑的分区来加快数据访问速度。查询能否排除分区取决于查询谓词:使用本地前缀索引的查询始终支持索引分区排除,而使用本地非前缀索引的查询则可能不支持。
Local Partitioned Index Storage(本地分区索引的存储)
与表分区类似,本地索引分区存储在其自身的段中,每个段包含总索引数据的一部分。因此,由四个分区组成的本地索引不会存储在单个索引段中,而是存储在四个独立的段中。
Global Partitioned Indexes(全局分区索引)
全局分区索引是一种 B 树索引,其分区独立于所基于的基础表。单个索引分区可指向任意数量的表分区,而在本地分区索引中,索引分区与表分区之间存在一对一的对应关系。
通常,全局索引适用于 OLTP 应用程序——这类应用程序对快速访问、数据完整性和可用性要求较高。在 OLTP 系统中,表可能按某个键(例如 employees.department_id
列)进行分区,但应用程序可能需要通过多种不同的键(例如 employee_id
或 job_id
)访问数据,此时全局索引会很有用。
例如,为“范围分区”中的 time_range_sales
表创建全局分区索引。在该表中,1998 年的销售数据存储在一个分区中,1999 年的销售数据存储在另一个分区中,以此类推。以下语句将在 channel_id
列上创建按范围分区的全局索引:
CREATE INDEX time_channel_sales_idx ON time_range_sales (channel_id)
GLOBAL PARTITION BY RANGE (channel_id)
(
PARTITION p1 VALUES LESS THAN (3),
PARTITION p2 VALUES LESS THAN (4),
PARTITION p3 VALUES LESS THAN (MAXVALUE)
);
如示意图所示,全局索引分区可包含指向多个表分区的条目:索引分区 p1
指向 channel_id
为 2 的行,索引分区 p2
指向 channel_id
为 3 的行,索引分区 p3
指向 channel_id
为 4 或 9 的行。
Figure 6-4 Global Partitioned Index
Partial Indexes for Partitioned Tables(分区表的部分索引)
部分索引是与关联分区表的索引属性相关联的索引。
这种关联性允许指定对哪些表分区建立索引,部分索引具有以下优势:
- 未建立索引的表分区不会占用不必要的索引存储空间。
- 加载和查询性能可能会提升。
在 Oracle 数据库 12c 之前,交换分区操作需要对关联的全局索引执行物理更新才能保持其可用性;从 Oracle 数据库 12c 开始,若分区维护操作涉及的分区不属于部分全局索引,则索引无需任何全局索引维护即可保持可用。 - 若在创建索引时仅为部分表分区建立索引,之后再为其他分区建立索引,可减少创建索引所需的排序空间。
可对表的各个分区开启或关闭索引功能:对于本地部分索引,所有关闭索引功能的表分区都没有可用的索引分区;对于全局索引(无论是否分区),都会排除所有关闭索引功能的分区中的数据。数据库不支持为强制执行唯一约束的索引创建部分索引。
下图Figure 6-5展示了与Figure 6-4中相同的全局索引,但该全局索引为部分索引:表分区 SALES_1998 和 SALES_2000 的索引功能已设置为 OFF,因此该部分全局索引不会为这两个分区建立索引。
Figure 6-5 Partial Global Partitioned Index
Using Object Store for Older Partitions(为旧分区使用对象存储)
对于只读分区,可使用低成本存储(如云中的对象存储)。过去十年间,数据量大幅增长;此外,在许多情况下,政府法规和政策要求长时间保留数据。Oracle 数据库用户针对超大型数据库采用了多种数据管理策略,主要目标包括:以最低成本存储大量数据、满足数据保留和保护的新法规要求、通过基于更多数据的更好分析创造业务机会。
本地部署的用户有多种解决方案可实现上述目标,Oracle 数据库现在提供的此功能,允许通过利用云中的低成本存储层(如对象存储),为云数据库制定类似的数据管理策略。
随着数据集的演进和增长,Oracle 数据库用户制定了多种数据管理策略,这些策略都属于信息生命周期管理(ILM)范畴。有助于实现 ILM 解决方案的数据库功能包括数据分区、高级行压缩、混合列压缩、自动数据优化等。
ILM 解决方案的一个重要方面是定义低成本存储层,这能以最低成本保留大量数据。Oracle ILM 策略支持自动数据压缩和将数据迁移到低成本存储层。本地部署的 Oracle 数据库用户使用 Oracle 存储解决方案(如 Oracle ZFS 存储设备或 Oracle Exadata 扩展(XT)存储服务器)作为低成本存储选项,用于存储访问频率低的旧数据或合规数据,也可选择第三方供应商提供的类似低成本存储选项。
使用对象存储可实现以下功能:
- 将旧分区和只读表空间存储在对象存储中。
- 以在线方式查询对象存储文件中的数据。
- 防止一个 PDB 未经授权访问另一个 PDB 拥有的对象存储文件。
- 在极少数需要修改只读数据的情况下,将数据从对象存储移回常规存储。
- 删除包含对象存储数据文件的表空间。
Moving Older Partitions and Read-Only Tablespaces to Object Store(将旧分区和只读表空间迁移到对象存储)
可制定基于时间的分区策略,并将只读表空间的数据文件迁移到低成本存储层(如对象存储)。
Accessing Objects in Object Storage(访问对象存储中的对象)
用户和 SQL 客户端访问对象存储中的表和分区数据时,过程完全透明。
Credential Management For Object Store Files(对象存储文件的凭据管理)
访问对象存储中的文件需要凭据。凭据是存储用户名和密码的数据库对象,数据经过加密后安全存储在创建凭据的 PDB 模式中。通过标准数据库身份验证确定用户是否可查询凭据对象。强烈建议每个 PDB 使用单独的凭据,以在多租户环境中实现 PDB 之间的隔离。
可指定将文件迁移到对象存储时使用的凭据对象,该凭据对象必须位于迁移数据文件的同一 PDB 中,且需拥有该 PDB 模式的访问权限。为方便使用,数据库支持按 PDB 设置名为 default_credential
的属性,若未显式指定凭据名称,将自动使用 default_credential
。
ALTER DATABASE PROPERTY SET default_credential = 'ADM.DEF_CRED_NAME';
ALTER DATABASE MOVE DATAFILE '+DATA_DG/orders_2022.dbf' TO
'https://objectstorage.example.com/oracle/orders_2022.dbf'
CREDENTIAL = 'ORD.ORD_CRED_NAME';
数据库还支持名为 default_bucket
的属性,这是将在其中创建对象存储文件的存储桶。Oracle 数据库支持 default_bucket
与 Oracle 管理的文件名结合使用,因此无需为每次文件迁移指定 URI。
Moving Datafiles Back From Object Storage Into Traditional Storage(将数据文件从对象存储移回传统存储)
若必须更新对象存储中的数据,需先将数据从对象存储移回传统存储。
在极少数需要修改已存储在对象存储中的只读数据的情况下,唯一的方法是将数据从对象存储移回传统存储。以下语句展示如何将数据文件从对象存储移回 ASM 文件系统(从对象存储复制文件会对性能产生影响):
ALTER DATABASE MOVE DATAFILE
'https://objectstorage.example.com/oracle/orders_2022.dbf' TO
'+DATA_DG/orders_2022.dbf';
ALTER TABLESPACE orders_2022 READ WRITE;
Deleting Object Store Data Files(删除对象存储数据文件)
可使用标准的 DROP TABLESPACE
命令删除包含数据文件的表空间。AND DATAFILES
子句用于从后端存储中删除数据文件,这会从对象存储中删除文件;若对象存储文件在对象存储中有多个块,则所有块以及清单都会被删除:
DROP TABLESPACE orders_2022 INCLUDING CONTENTS AND DATAFILES;
Overview of Sharded Tables(分片表概述)
在 Oracle 数据库中,分片功能允许你将大型表拆分为更易于管理的片段(称为“分片”),这些分片可存储在多个数据库中。
每个数据库托管在专用服务器上,拥有独立的本地资源(CPU、内存、闪存或磁盘)。在此类配置中,每个数据库都被称为一个“分片”。所有分片共同构成一个单一的逻辑数据库,该数据库被称为“分片数据库”(sharded database,简称 SDB)。
水平分区指的是将数据库表跨分片拆分,使每个分片包含的表具有相同的列,但行的子集不同。以这种方式拆分的表也被称为“分片表”。
下图展示了一个表如何跨三个分片进行水平分区:
Figure 6-6 Horizontal Partitioning of a Table Across Shards
分片基于“无共享”硬件基础设施,不存在单点故障——因为分片不共享 CPU、内存或存储设备等物理资源。在软件层面,分片同样松散耦合,不运行集群软件。
分片通常托管在专用服务器上,这些服务器可以是通用硬件,也可以是工程系统;分片可运行在单实例数据库或 Oracle RAC 数据库上;部署环境灵活,可位于本地数据中心、云端,或采用本地与云结合的混合配置。
从数据库管理员(DBA)的视角来看,分片数据库由多个可“统一管理”或“单独管理”的数据库组成;但从应用程序的视角来看,分片数据库完全等同于单一数据库——分片的数量、数据在分片中的分布情况,对数据库应用程序完全透明。
分片适用于采用分片数据库架构的自定义 OLTP 应用程序。使用分片的应用程序需满足两个核心条件:具备定义清晰的数据模型;采用明确的数据分布策略(一致性哈希、范围、列表或复合策略),且主要通过“分片键”访问数据。
分片键示例包括 customer_id
(客户 ID)、account_no
(账户号)或 country_id
(国家/地区 ID)。
Sharded Tables(分片表)
数据库表会跨分片拆分,每个分片包含的表具有相同的列,但行的子集不同。以这种方式拆分的表被称为“分片表”。
下图展示了左侧单个数据库中的一组大型表(称为“表族”),如何跨右侧三个分片进行水平分区——每个分片包含数据的一个子集(分别用红色、黄色和蓝色行标识):
Figure 6-7 Horizontal Partitioning of a Table Across Shards
分区通过表空间级别跨分片分布,分布依据为“分片键”。分片键支持的数据类型包括:
NUMBER
INTEGER
SMALLINT
RAW
(N)VARCHAR
(N)VARCHAR2
(N)CHAR
DATE
TIMESTAMP
分片表的每个分区驻留在独立的表空间中,且每个表空间关联到特定的分片。根据分片方法的不同,这种关联可由数据库自动建立,或由管理员手动定义。
尽管分片表的分区驻留在多个分片中,但对应用程序而言,该表的外观和行为与单个数据库中的分区表完全一致。应用程序发出的 SQL 语句无需引用分片,也不依赖分片的数量及其配置。
常见的 SQL 表分区语法可指定行应如何跨分片分区。例如,以下 SQL 语句创建一个分片表,根据分片键 cust_id
按水平分区方式跨分片拆分:
CREATE SHARDED TABLE customers (
cust_id NUMBER NOT NULL,
address VARCHAR2(250),
name VARCHAR2(50),
region VARCHAR2(20),
signup DATE,
class VARCHAR2(3),
CONSTRAINT cust_pk PRIMARY KEY(cust_id)
)
PARTITION BY CONSISTENT HASH (cust_id)
PARTITIONS AUTO TABLESPACE SET ts1;
该分片表通过“一致性哈希”进行分区——这是一种常用于可扩展分布式系统的特殊哈希分区类型。此技术会自动将表空间跨分片分布,以实现数据和工作负载的均匀分配。
注意:不支持在分片表上创建全局索引,但支持本地索引。
相关主题(Related Topics)
-
Sharding Architecture(分片架构)
Oracle 分片是一种基于数据在多个 PDB 间水平分区的数据库扩展技术,应用程序将 PDB 池视为单个逻辑数据库。 -
Oracle Globally Distributed Database Guide(《Oracle 全局分布式数据库指南》)
Overview of Views(视图概述)
视图是一个或多个表的逻辑表示,本质上是一个存储的查询。
视图的数据来源于其基于的表(称为基表),基表可以是表或其他视图。对视图执行的所有操作实际上都会影响基表,在大多数使用表的场景中,都可以使用视图。
注意:物化视图使用与标准视图不同的数据结构。
视图能根据不同用户的需求定制数据展示方式,通常用于以下场景:
- 增强表安全性:通过限制对表中预设行或列的访问,提供额外的表安全层级。例如,
staff
视图不显示基表employees
中的salary
(薪资)或commission_pct
(佣金比例)列。 - 隐藏数据复杂性:例如,单个视图可通过连接(多个表中相关列或行的集合)定义,但视图会隐藏数据实际来源于多个表的事实;查询也可能对表信息执行大量计算,因此用户无需了解如何执行连接或计算,即可查询视图。
- 以不同视角呈现基表数据:例如,可重命名视图的列,而不影响视图所基于的表。
- 使应用程序与基表定义变更隔离:例如,若视图的定义查询引用四列表中的三列,向该表添加第五列不会影响视图定义,所有使用该视图的应用程序也不会受到影响。
以下示例基于 hr.employees
表创建视图:hr.employees
表包含多个列和大量行,为了让用户仅查看其中五列或特定行,可创建如下视图:
CREATE VIEW staff AS
SELECT employee_id, last_name, job_id, manager_id, department_id
FROM employees;
与所有子查询一样,定义视图的查询不能包含 FOR UPDATE
子句。示意图展示了名为 staff
的视图,该视图仅显示基表中的五列。
Figure 6-8 View
Characteristics of Views(视图的特征)
与表不同,视图不分配存储空间,也不包含数据。视图由从其引用的基表中提取或派生数据的查询定义,由于视图基于其他对象,因此除了在数据字典中存储定义视图的查询外,无需其他存储。
视图依赖于其引用的对象,这种依赖关系由数据库自动处理。例如,若删除并重新创建视图的基表,数据库会判断新基表是否符合视图定义。
Data Manipulation in Views(视图中的数据操作)
由于视图派生自表,因此与表有许多相似之处:用户可查询视图,在一定限制条件下还可对视图执行 DML 操作。对视图执行的操作会影响视图某一基表中的数据,且需遵守基表的完整性约束和触发器。
以下示例创建 hr.employees
表的视图:
CREATE VIEW staff_dept_10 AS
SELECT employee_id, last_name, job_id, manager_id, department_id
FROM employees
WHERE department_id = 10
WITH CHECK OPTION CONSTRAINT staff_dept_10_cnst;
定义查询仅引用部门 10 的行。CHECK OPTION
为视图创建约束,确保对视图执行的 INSERT
和 UPDATE
语句不会导致视图无法选择的行产生。因此,可插入部门 10 员工的行,但不能插入部门 30 员工的行。
How Data Is Accessed in Views(视图中数据的访问方式)
Oracle 数据库将视图定义作为定义视图的查询文本存储在数据字典中。
当在 SQL 语句中引用视图时,Oracle 数据库会执行以下任务:
- 合并查询(尽可能):将针对视图的查询与定义视图及任何基础视图的查询合并。Oracle 数据库会像直接执行合并后的查询(未引用视图)一样优化该查询,因此无论列是在视图定义中引用,还是在用户针对视图的查询中引用,Oracle 数据库都可使用引用基表列上的所有索引。有时 Oracle 数据库无法将视图定义与用户查询合并,这种情况下,可能无法使用引用列上的所有索引。
- 在共享 SQL 区域中解析合并后的语句:仅当现有共享 SQL 区域中没有包含类似语句时,Oracle 数据库才会在新的共享 SQL 区域中解析引用视图的语句。因此,视图具有减少共享 SQL 相关内存使用的优势。
- 执行 SQL 语句
以下示例说明查询视图时的数据访问过程:假设基于 employees
表和 departments
表创建 employees_view
视图:
CREATE VIEW employees_view AS
SELECT employee_id, last_name, salary, location_id
FROM employees JOIN departments USING (department_id)
WHERE department_id = 10;
用户执行以下查询 employees_view
视图的语句:
SELECT last_name FROM employees_view WHERE employee_id = 200;
Oracle 数据库会合并视图和用户查询,构建以下查询并执行以检索数据:
SELECT last_name
FROM employees, departments
WHERE employees.department_id = departments.department_id
AND departments.department_id = 10
AND employees.employee_id = 200;
Updatable Join Views(可更新连接视图)
连接视图的 FROM
子句中包含多个表或视图。
以下示例中,staff_dept_10_30
视图连接 employees
表和 departments
表,仅包含部门 10 或 30 的员工:
CREATE VIEW staff_dept_10_30 AS
SELECT employee_id, last_name, job_id, e.department_id
FROM employees e, departments d
WHERE e.department_id IN (10, 30)
AND e.department_id = d.department_id;
可更新连接视图(也称为可修改连接视图)涉及两个或多个基表或视图,允许执行 DML 操作。可更新视图在 SELECT
语句的顶层 FROM
子句中包含多个表,且不受 WITH READ ONLY
子句限制。
要成为固有可更新视图,需满足多个条件。例如,一个通用规则是:对连接视图执行的 INSERT
、UPDATE
或 DELETE
操作一次只能修改一个基表。查询 USER_UPDATABLE_COLUMNS
数据字典视图可查看 staff_dept_10_30
视图是否可更新:
SQL> SELECT TABLE_NAME, COLUMN_NAME, UPDATABLE
2 FROM USER_UPDATABLE_COLUMNS
3 WHERE TABLE_NAME = 'STAFF_DEPT_10_30';
查询结果(格式已调整):
TABLE_NAME | COLUMN_NAME | UPDATABLE |
---|---|---|
STAFF_DEPT_10_30 | EMPLOYEE_ID | YES |
STAFF_DEPT_10_30 | LAST_NAME | YES |
STAFF_DEPT_10_30 | JOB_ID | YES |
STAFF_DEPT_10_30 | DEPARTMENT_ID | YES |
可更新连接视图的所有可更新列都必须映射到键保留表(key-preserved table)的列——键保留表是指基础表中的每行在查询输出中最多出现一次的表。在 staff_dept_10_30
视图中,department_id
是 departments
表的主键,因此 employees
表中的每行在结果集中最多出现一次,使 employees
表成为键保留表;而 departments
表不是键保留表,因为其每行在结果集中可能出现多次。
Object Views(对象视图)
与视图是虚拟表类似,对象视图是虚拟对象表。视图中的每行都是一个对象,即对象类型的实例,对象类型是用户定义的数据类型。
可像访问对象类型存储的数据一样检索、更新、插入和删除关系数据,也可定义包含对象数据类型(如对象、REF 和集合(嵌套表和 VARRAY))列的视图。
与关系视图类似,对象视图可仅向用户展示数据库管理员希望其看到的数据。例如,对象视图可展示 IT 程序员的相关数据,但省略薪资等敏感数据。以下示例创建 employee_type
对象,然后基于该对象创建视图 it_prog_view
:
CREATE TYPE employee_type AS OBJECT
(
employee_id NUMBER(6),
last_name VARCHAR2(25),
job_id VARCHAR2(10)
);
/
CREATE VIEW it_prog_view OF employee_type
WITH OBJECT IDENTIFIER (employee_id) AS
SELECT e.employee_id, e.last_name, e.job_id
FROM employees e
WHERE job_id = 'IT_PROG';
对象视图在原型设计或向面向对象应用程序过渡时非常有用,因为视图中的数据可从关系表中获取,并像表被定义为对象表一样访问。无需将现有表转换为不同的物理结构,即可运行面向对象的应用程序。
Overview of Materialized Views(物化视图概述)
物化视图是预先存储或“物化”为模式对象的查询结果。查询的 FROM
子句可引用表、视图或其他物化视图。
物化视图通常在复制中作为主表,在数据仓库中作为事实表,用于汇总、计算、复制和分发数据,适用于多种计算环境,例如:
- 数据仓库:物化视图可计算并存储由聚合函数(如求和、平均值)生成的数据。摘要(summary)是一种聚合视图,通过预先计算连接和聚合操作并将结果存储在表中,减少查询时间,物化视图等同于摘要;也可使用物化视图计算包含或不包含聚合的连接。
- 物化视图复制:通过 XStream 和 Oracle GoldenGate 实现,视图包含单个时间点的表完整副本或部分副本。物化视图在分布式站点复制数据,并同步多个站点执行的更新,这种复制方式适用于现场销售等数据库不总是连接到网络的环境。
- 移动计算环境:物化视图可将数据子集从中央服务器下载到移动客户端,定期从中央服务器刷新,并将客户端的更新传播到中央服务器。
在复制环境中,物化视图与不同数据库(称为主数据库)中的表共享数据,主站点上与物化视图关联的表称为主表。示意图展示了一个数据库中的物化视图基于另一个数据库中的主表,对主表的更新会复制到物化视图数据库。
Figure 6-9 Materialized View
Characteristics of Materialized Views(物化视图的特征)
物化视图与索引和非物化视图有一些共同特征:
- 与索引的相似性:包含实际数据并占用存储空间;基表数据变化时可刷新;用于查询重写操作时可提升 SQL 执行性能;其存在对 SQL 应用程序和用户透明。
- 与非物化视图的相似性:表示其他表和视图中的数据。与索引不同,用户可使用
SELECT
语句直接查询物化视图;根据所需的刷新类型,也可使用 DML 语句更新物化视图。
以下示例基于 sh
示例模式中的三个主表,创建并填充物化聚合视图:
CREATE MATERIALIZED VIEW sales_mv AS
SELECT t.calendar_year, p.prod_id, SUM(s.amount_sold) AS sum_sales
FROM times t, products p, sales s
WHERE t.time_id = s.time_id
AND p.prod_id = s.prod_id
GROUP BY t.calendar_year, p.prod_id;
以下示例删除 sales
表(sales_mv
的主表之一),然后查询 sales_mv
:由于行是与主表数据分开存储(物化)的,因此查询仍能选择数据:
SQL> DROP TABLE sales;
Table dropped.
SQL> SELECT * FROM sales_mv WHERE ROWNUM < 4;
查询结果(格式已调整):
CALENDAR_YEAR | PROD_ID | SUM_SALES |
---|---|---|
1998 | 13 | 936197.53 |
1998 | 26 | 567533.83 |
1998 | 27 | 107968.24 |
物化视图可以分区,可在分区表上定义物化视图,也可在物化视图上创建一个或多个索引。
Refresh Methods for Materialized Views(物化视图的刷新方法)
基表数据变化后,数据库通过刷新物化视图来维护其中的数据,刷新方法分为增量刷新和完全刷新。
Complete Refresh(完全刷新)
完全刷新会执行定义物化视图的查询。除非物化视图引用预构建表,或表被定义为 BUILD DEFERRED
,否则在初始创建物化视图时会执行完全刷新。完全刷新可能较慢,尤其是当数据库需要读取和处理大量数据时,但可在物化视图创建后的任何时间执行完全刷新。
Incremental Refresh(增量刷新)
增量刷新(也称为快速刷新)仅处理现有数据的变化,无需从头重建物化视图,仅处理变化可大幅缩短刷新时间。
可根据需求或按固定时间间隔刷新物化视图;此外,也可将物化视图配置为:当事务提交对基表的更改时,在与基表相同的数据库中自动刷新。
快速刷新主要有以下两种形式:
- 基于日志的刷新(Log-Based Refresh):物化视图日志或直接加载日志记录对基表的更改。物化视图日志是一种模式对象,记录对基表的更改,以便基于该基表定义的物化视图可执行增量刷新,每个物化视图日志关联一个基表。
- 分区更改跟踪(PCT)刷新:PCT 刷新仅在基表已分区时有效。PCT 刷新会删除物化视图中受影响分区或受影响数据部分中的所有数据,然后重新计算这些数据。数据库通过被修改的基表分区,识别视图中受影响的分区或数据部分。当基表执行分区维护操作时,PCT 刷新是唯一可用的增量刷新方法。
In-Place and Out-of-Place Refresh(就地刷新和异地刷新)
对于完全刷新和增量刷新,数据库都可对物化视图执行就地刷新(直接在视图上执行刷新语句)或异地刷新。
异地刷新会创建一个或多个外部表,在这些表上执行刷新语句,然后将物化视图或受影响的分区与外部表交换。这种技术在刷新期间能实现高可用性,尤其当刷新语句需要长时间完成时。
同步刷新是异地刷新的一种类型,同步刷新不修改基表的内容,而是使用同步刷新包中的 API,通过同时将更改应用到基表和物化视图来确保一致性。这种方法能使一组表及其上定义的物化视图始终保持同步。在数据仓库中,同步刷新方法非常适合以下场景:增量数据加载受到严格控制,且按固定间隔发生;表及其物化视图通常以相同方式分区,或其分区通过函数依赖关联。
Automatic Materialized Views(自动物化视图)
从 Oracle 数据库 21c 版本开始,可自动创建和维护物化视图。
Oracle 数据库可自动创建和管理物化视图,以优化查询性能。在数据库管理员很少干预或无需干预的情况下,后台任务会监控和分析工作负载特征,识别可提升 SQL 性能的物化视图候选。在候选物化视图对工作负载可见之前,会在后台(使用工作负载查询)衡量其性能收益。
Query Rewrite(查询重写)
查询重写将用户以主表表示的请求,转换为包含物化视图的语义等效请求。
当基表包含大量数据时,计算聚合或连接操作的成本高且耗时。由于物化视图包含预计算的聚合和连接结果,查询重写可使用物化视图快速回答查询。
查询转换器会透明地将请求重写为使用物化视图,无需用户干预,且 SQL 语句中无需引用物化视图。由于查询重写具有透明性,因此可添加或删除物化视图,而不会使应用程序代码中的 SQL 失效。
通常,将查询重写为使用物化视图而非详细表,能提升响应时间。示意图展示了数据库为原始查询和重写后的查询生成执行计划,并选择成本最低的计划。
Figure 6-10 Query Rewrite
Overview of Sequences(序列概述)
序列是一种模式对象,多个用户可通过它生成唯一整数。序列生成器提供了一种高度可扩展且性能良好的方法,用于为数值数据类型生成代理键。
Sequence Characteristics(序列特征)
序列定义包含序列的基本信息,包括序列名称以及序列是升序还是降序。此外,序列定义还包含以下信息:
- 数值之间的间隔;
- 数据库是否应在内存中缓存生成的序列数值集;
- 序列达到限制时是否应循环。
以下示例在 oe
示例模式中创建序列 customers_seq
,应用程序可使用该序列在向 customers
表添加行时提供客户 ID:
CREATE SEQUENCE customers_seq
START WITH 1000
INCREMENT BY 1
NOCACHE
NOCYCLE;
首次引用 customers_seq.nextval
会返回 1000,第二次返回 1001,后续每次引用返回的值都比前一次大 1。
Concurrent Access to Sequences(序列的并发访问)
同一个序列生成器可为多个表生成数值,能自动创建主键,并协调多个行或表之间的键。例如,一个序列可为 orders
表和 customers
表生成主键。
在多用户环境中,序列生成器生成唯一数值时,无需磁盘 I/O 或事务锁定的开销。例如,两个用户同时向 orders
表插入新行,通过使用序列为 order_id
列生成唯一数值,两个用户无需等待对方输入下一个可用的订单号,序列会自动为每个用户生成正确的值。
引用序列的每个用户都可访问其当前序列数值(即会话中最后生成的序列值)。用户可执行语句生成新的序列数值,或使用会话中最后生成的当前数值。会话中的语句生成序列数值后,该数值仅对该会话可用。若生成序列数值的语句所在事务最终回滚,则生成的单个序列数值可能会被跳过。
警告:若应用程序需要无间隔的数值集,则不能使用 Oracle 序列,必须通过自定义代码在数据库中实现活动序列化。
Overview of Dimensions(维度概述)
典型的数据仓库包含两个重要组件:维度和事实。
维度是用于指定业务问题的任意类别,例如时间、地理区域、产品、部门和分销渠道;事实是与特定维度值集相关联的事件或实体,例如销售量或利润。
多维查询示例包括:
- 按地理维度(从州到国家再到地区)的递增聚合级别,显示 2013 年和 2014 年所有产品的总销售额。
- 创建 2013 年和 2014 年南美洲地区各地区费用的交叉表分析,包含所有可能的小计。
- 列出 2014 年亚洲地区汽车产品销售额排名前 10 的销售代表,并对其佣金进行排名。
许多多维问题需要聚合数据和数据集比较,通常涉及时间、地理区域或预算。
创建维度可更广泛地使用查询重写功能,数据库通过透明地将查询重写为使用物化视图,可提升查询性能。
Hierarchical Structure of a Dimension(维度的层次结构)
维度表是一种逻辑结构,定义列对或列集之间的层次(父子)关系。例如,维度可指定在一行中,城市列的值隐含州列的值,州列的值隐含国家列的值。
在客户维度中,客户可汇总到城市、州、国家、子区域和区域。数据分析通常从维度层次结构的较高级别开始,若情况需要,再逐步向下钻取。
子级别中的每个值都关联到父级别中的一个且仅一个值,层次关系是从层次结构的一个级别到下一级别的函数依赖。
维度不分配数据存储,维度信息存储在维度表中,而事实信息存储在事实表中。
Creation of Dimensions(维度的创建)
使用 CREATE DIMENSION
SQL 语句创建维度,该语句指定:
- 多个
LEVEL
子句,每个子句标识维度中的一个列或列集; - 一个或多个
HIERARCHY
子句,指定相邻级别之间的父子关系; - 可选的
ATTRIBUTE
子句,每个子句标识与单个级别关联的额外列或列集。
以下语句用于在 sh
示例模式中创建 customers_dim
维度:
CREATE DIMENSION customers_dim
LEVEL customer IS (customers.cust_id)
LEVEL city IS (customers.cust_city)
LEVEL state IS (customers.cust_state_province)
LEVEL country IS (countries.country_id)
LEVEL subregion IS (countries.country_subregion)
LEVEL region IS (countries.country_region)
HIERARCHY geog_rollup (
customer CHILD OF city
city CHILD OF state
state CHILD OF country
country CHILD OF subregion
subregion CHILD OF region
JOIN KEY (customers.country_id) REFERENCES country
)
ATTRIBUTE customer DETERMINES (cust_first_name, cust_last_name, cust_gender, cust_marital_status, cust_year_of_birth, cust_income_level, cust_credit_limit)
ATTRIBUTE country DETERMINES (countries.country_name);
维度中的列可来自同一张表(非规范化)或多张表(完全或部分规范化)。例如,规范化的时间维度可包含日期表、月份表和年份表,通过连接条件将每个日期行连接到月份行,每个月份行连接到年份行;在完全非规范化的时间维度中,日期、月份和年份列位于同一张表中。无论规范化还是非规范化,列之间的层次关系都必须在 CREATE DIMENSION
语句中指定。
Overview of Synonyms(同义词概述)
同义词是模式对象的别名。例如,你可以为表或视图、序列、PL/SQL 程序单元、用户定义对象类型或另一个同义词创建同义词。由于同义词仅是一个别名,它无需额外存储空间,仅需在数据字典中存储其定义信息。
同义词能够简化数据库用户编写的 SQL 语句,还可用于隐藏底层模式对象的标识与位置。若底层对象需要重命名或迁移位置,只需重新定义同义词即可,基于该同义词的应用程序无需修改便能继续正常运行。
你可以创建私有同义词和公有同义词两类同义词:
- 私有同义词(Private Synonyms):位于特定用户的模式下,该用户可控制其对其他用户的可用性。
- 公有同义词(Public Synonyms):归名为
PUBLIC
的用户组所有,数据库中的所有用户均可访问。
Example 6-5 Public Synonym(示例 6-5:公有同义词)
假设数据库管理员为 hr.employees
表创建一个名为 people
的公有同义词,随后用户连接到 oe
模式,并统计该同义词所引用表的行数:
SQL> CREATE PUBLIC SYNONYM people FOR hr.employees;
Synonym created.
SQL> CONNECT oe
Enter password: password
Connected.
SQL> SELECT COUNT(*) FROM people;
COUNT(*)
----------
107
需谨慎使用公有同义词,因为它们会增加数据库整合的难度。例如,若另一位管理员尝试创建名为 people
的公有同义词,创建操作会失败——数据库中只能存在一个名为 people
的公有同义词。过度使用公有同义词还会导致应用程序之间出现命名空间冲突:
SQL> CREATE PUBLIC SYNONYM people FOR oe.customers;
CREATE PUBLIC SYNONYM people FOR oe.customers
ERROR at line 1:
ORA-00955: name is already used by an existing object
SQL> SELECT OWNER, SYNONYM_NAME, TABLE_OWNER, TABLE_NAME
2 FROM DBA_SYNONYMS
3 WHERE SYNONYM_NAME = 'PEOPLE';
查询结果(格式已调整)如下:
OWNER | SYNONYM_NAME | TABLE_OWNER | TABLE_NAME |
---|---|---|---|
PUBLIC | PEOPLE | HR | EMPLOYEES |
同义词本身不支持权限设置。当你为同义词授予对象权限时,实际是为其底层对象授予权限,同义词仅在 GRANT
语句中充当对象的别名。
相关参考:
- 《Oracle Database Administrator’s Guide》(《Oracle 数据库管理员指南》):了解如何管理同义词。
- 《Oracle Database SQL Language Reference》(《Oracle 数据库 SQL 语言参考》):了解
CREATE SYNONYM
语句的语法和语义。
更多推荐
所有评论(0)