6 Partitions, Views, and Other Schema Objects(6 分区、视图及其他模式对象)

虽然表和索引是最重要且最常用的模式对象,但数据库还支持许多其他类型的模式对象,本章将讨论其中最常见的一些。

  • Overview of Partitions(分区概述)
    在 Oracle 数据库中,分区(Partitioning)使您能够将超大型表和索引分解为更小、更易于管理的片段,这些片段称为分区(Partition)。每个分区都是一个独立的对象,拥有自己的名称,并可选择拥有自己的存储特性。

  • Overview of Sharded Tables(分片表概述)
    在 Oracle 数据库中,全球分布式数据库特性使您能够将任何表拆分为称为分片(Shard)的片段,这些片段可以存储在多个数据库中。

  • Overview of Views(视图概述)
    视图(View)是一个或多个表的逻辑表示。本质上,视图是一个存储的查询。

  • Overview of Materialized Views(物化视图概述)
    物化视图(Materialized View)是一个已提前作为模式对象存储或“物化”的查询结果。该查询的 FROM 子句可以命名表、视图或物化视图。

  • Overview of Sequences(序列概述)
    序列(Sequence)是一个模式对象,多个用户可以从该对象生成唯一的整数。序列生成器为数字数据类型提供了一种高度可扩展且性能良好的方法来生成代理键。

  • Overview of Dimensions(维度概述)
    典型的数据仓库有两个重要组件:维度和事实。

  • Overview of Synonyms(同义词概述)
    同义词(Synonym)是模式对象的别名。例如,您可以为表或视图、序列、PL/SQL 程序单元、用户定义的对象类型或另一个同义词创建同义词。因为同义词只是一个别名,所以除了它在数据字典中的定义外,不需要任何存储。

Overview of Partitions(分区概述)

在 Oracle 数据库中,分区(Partitioning)使您能够将超大型表和索引分解为更小、更易于管理的片段,这些片段称为分区(Partition)。每个分区都是一个独立的对象,拥有自己的名称,并可选择拥有自己的存储特性。

用一个类比来说明分区,假设一位 HR 经理有一个大箱子,里面装满了员工文件夹。每个文件夹都列出了员工的入职日期。经常会有查询要求查找在特定月份入职的员工。满足此类请求的一种方法是,在员工入职日期上创建一个索引,该索引指定了散落在箱子中各文件夹的位置。相比之下,分区策略则使用许多较小的箱子,每个箱子装的是在给定月份入职的员工文件夹。

使用较小的箱子有几个优点。当要求检索 6 月份入职的员工文件夹时,HR 经理可以直接取出 6 月的箱子。此外,如果任何一个小箱子暂时损坏,其他小箱子仍然可用。搬迁办公室也变得更容易,因为经理可以搬动几个小箱子,而不是搬动一个沉重的大箱子。

从应用程序的角度来看,只存在一个模式对象。SQL 语句无需修改即可访问分区表。分区对于许多不同类型的数据库应用程序都很有用,特别是那些管理大量数据的应用程序。其优点包括:

  • 提高可用性
    某个分区的不可用并不意味着整个对象的不可用。查询优化器(Query Optimizer)会自动从查询计划(Query Plan)中移除未引用的分区,因此当分区不可用时,查询不会受到影响。

  • 更易于管理模式对象
    分区对象具有可以整体或单独管理的片段。DDL 语句可以操作分区,而不是整个表或索引。因此,您可以分解诸如重建索引或表之类的资源密集型任务。例如,您可以一次移动一个表分区。如果出现问题,那么只需重做该分区的移动,而不是整个表的移动。此外,删除分区可以避免执行大量的 DELETE 语句。

  • 减少 OLTP 系统中对共享资源的争用
    在某些 OLTP 系统中,分区可以减少对共享资源的争用。例如,DML 分布在多个段上,而不是一个段上。

  • 增强数据仓库中的查询性能
    在数据仓库(Data Warehouse)中,分区可以加快即席查询的处理速度。例如,一个包含一百万行的销售表可以按季度进行分区。

  • Partition Characteristics(分区特征)
    表或索引的每个分区必须具有相同的逻辑属性,例如列名、数据类型和约束。

  • Partitioned Tables(分区表)
    分区表(Partitioned Table)由一个或多个分区组成,这些分区单独管理,并且可以独立于其他分区运行。

  • Partitioned Indexes(分区索引)
    分区索引(Partitioned Index)是一种索引,与分区表一样,它被划分为更小且更易于管理的片段。

  • Partial Indexes for Partitioned Tables(分区表的局部索引)
    局部索引(Partial Index)是一种与关联分区表的索引属性相关的索引。

  • Using Object Store for Older Partitions(对较旧分区使用对象存储)
    对于只读分区,您可以使用低成本存储,例如云中的对象存储。

另请参见
《Oracle AI Database VLDB and Partitioning Guide》(《Oracle AI 数据库 VLDB 与分区指南》),了解分区简介

Partition Characteristics(分区特征)

表或索引的每个分区必须具有相同的逻辑属性,例如列名、数据类型和约束。

例如,表中的所有分区共享相同的列和约束定义。但是,每个分区可以具有单独的物理属性,例如它所属的表空间。

  • Partition Key(分区键)
    分区键(Partition Key)是一组一列或多列,用于确定分区表中每一行应放入哪个分区。每一行都被明确地分配到一个分区。

  • Partitioning Strategies(分区策略)
    Oracle 分区提供了几种分区策略,用于控制数据库如何将数据放入分区。基本策略包括范围分区、列表分区和哈希分区。

Partition Key(分区键)

分区键(Partition Key)是一组一列或多列,用于确定分区表中每一行应放入哪个分区。每一行都被明确地分配到一个分区。

sales 表中,您可以将 time_id 列指定为范围分区的键。数据库根据此列中的日期是否落在指定范围内,将行分配给分区。Oracle AI Database 通过使用分区键,自动将插入、更新和删除操作定向到相应的分区。

Partitioning Strategies(分区策略)

Oracle 分区提供了几种分区策略,用于控制数据库如何将数据放入分区。基本策略包括范围分区、列表分区和哈希分区。

单层级分区(Single-Level Partitioning)仅使用一种数据分布方法,例如,仅列表分区或仅范围分区。在复合分区(Composite Partitioning)中,表首先按一种数据分布方法进行分区,然后每个分区再使用第二种数据分布方法进一步细分为子分区(Subpartition)。例如,您可以对 channel_id 使用列表分区,对 time_id 使用范围子分区。

示例 6-1 分区表的示例行集(Example 6-1 Sample Row Set for Partitioned Table)
此分区示例假设您要用以下行填充分区表 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(范围分区)
    在范围分区(Range Partitioning)中,数据库根据分区键值的范围将行映射到分区。范围分区是最常见的分区类型,通常与日期一起使用。

  • Interval Partitioning(间隔分区)
    间隔分区(Interval Partitioning)是范围分区的扩展。

  • List Partitioning(列表分区)
    在列表分区(List Partitioning)中,数据库使用一组离散值作为每个分区的分区键。分区键由一列或多列组成。

  • Hash Partitioning(哈希分区)
    在哈希分区(Hash Partitioning)中,数据库根据其应用于用户指定分区键的哈希算法,将行映射到分区。

  • Reference Partitioning(引用分区)
    在引用分区(Reference Partitioning)中,子表的分区策略完全通过与父表的外键关系来定义。对于父表中的每个分区,子表中恰好存在一个相应的分区。父表将父记录存储在特定的分区中,而子表将子记录存储在相应的分区中。

  • Composite Partitioning(复合分区)
    在复合分区(Composite Partitioning)中,表首先按一种数据分布方法进行分区,然后每个分区再使用第二种数据分布方法进一步细分为子分区。因此,复合分区结合了基本的数据分布方法。给定分区的所有子分区代表数据的一个逻辑子集。

Range Partitioning(范围分区)

在范围分区(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)
 );

之后,您将示例 6-1 中的行加载到 time_range_sales 中。代码显示了这四个分区中的行分布。数据库根据 PARTITION BY RANGE 子句中指定的规则,基于 time_id 值为每一行选择分区。范围分区键值决定了指定分区的非包含性高边界。

Interval Partitioning(间隔分区)

间隔分区(Interval Partitioning)是范围分区的扩展。

如果您插入的数据超出了现有范围分区,那么 Oracle AI Database 会自动创建指定间隔的分区。例如,您可以创建一个销售历史表,将每个月的数据存储在一个单独的分区中。

间隔分区使您能够避免显式创建范围分区。对于几乎所有已进行范围分区并使用固定间隔创建新分区的表,您都可以使用间隔分区。除非您使用不同的间隔创建范围分区,或者您总是设置特定的分区属性,否则请考虑使用间隔分区。

当按间隔进行分区时,您必须至少指定一个范围分区。范围分区键值决定了范围分区的高值,这称为转换点(Transition Point)。数据库会自动为值超出转换点的数据创建间隔分区。每个间隔分区的下边界是前一个范围分区或间隔分区的包含性上边界。因此,在示例 6-2 中,值 01-JAN-2011 在分区 p2 中。

数据库会为超出转换点的数据创建间隔分区。间隔分区(Interval Partition)通过指示数据库创建指定范围或间隔的分区来扩展范围分区。当插入到表中的数据超出所有现有范围分区时,数据库会自动创建分区。在示例 6-2 中,p3 分区包含分区键 time_id 值大于或等于 01-JAN-2013 的行。

示例 6-2 间隔分区(Example 6-2 Interval Partitioning)
假设您创建一个具有四个不同宽度分区的 sales 表。您指定在 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(' 2007-01-01 00:00:00', 'SYYYY-M
          M-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA

P1        TO_DATE(' 2008-01-01 00:00:00', 'SYYYY-M
          M-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA

P2        TO_DATE(' 2009-07-01 00:00:00', 'SYYYY-M
          M-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA

P3        TO_DATE(' 2010-01-01 00:00:00', 'SYYYY-M
          M-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA

SYS_P1598 TO_DATE(' 2014-11-01 00:00:00', 'SYYYY-M
          M-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA

另请参见
《Oracle AI Database VLDB and Partitioning Guide》(《Oracle AI 数据库 VLDB 与分区指南》),了解更多关于间隔分区的信息

List Partitioning(列表分区)

在列表分区(List Partitioning)中,数据库使用一组离散值作为每个分区的分区键。分区键由一列或多列组成。

您可以使用列表分区来控制各个行如何映射到特定的分区。通过使用列表,当用于识别它们的键不便于排序时,您可以对相关数据集进行分组和组织。

示例 6-3 列表分区(Example 6-3 List Partitioning)
假设您使用以下语句创建 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 值为 24 的行存储在 EVEN_CHANNELS 分区中,而 channel_id 值为 39 的行存储在 ODD_CHANNELS 分区中。

Hash Partitioning(哈希分区)

在哈希分区(Hash Partitioning)中,数据库根据其应用于用户指定分区键的哈希算法,将行映射到分区。

行的目的地由数据库应用于该行的内部哈希函数(Hash Function)决定。当分区数是 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 AI Database VLDB and Partitioning Guide》(《Oracle AI 数据库 VLDB 与分区指南》),了解如何创建分区
  • 《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE TABLE ... PARTITION BY 示例
Reference Partitioning(引用分区)

在引用分区(Reference Partitioning)中,子表的分区策略完全通过与父表的外键关系来定义。对于父表中的每个分区,子表中恰好存在一个相应的分区。父表将父记录存储在特定的分区中,而子表将子记录存储在相应的分区中。

例如,orders 表是 line_items 表的父表,并在 order_id 上定义了主键和外键。这些表通过引用进行分区。例如,如果数据库将订单 233 存储在 ordersQ3_2015 分区中,那么数据库会将订单 233 的所有行项目存储在 line_itemsQ3_2015 分区中。如果向 orders 添加了分区 Q4_2015,那么数据库会自动将 Q4_2015 添加到 line_items

引用分区的优点是:

  • 通过对父表和子表使用相同的分区策略,您可以避免复制所有分区键列。此策略减少了反规范化的手动开销,并节省了空间。
  • 对父表的维护操作会自动在子表上发生。例如,当您向主表添加分区时,数据库会自动将此添加操作传播到其后代表。
  • 数据库自动使用父表和子表中分区的分区级联接(Partition-Wise Joins),从而提高性能。

您可以将引用分区与所有基本分区策略(包括间隔分区)一起使用。您也可以将引用分区表创建为复合分区表。

示例 6-4 创建引用分区表(Example 6-4 Creating Reference-Partitioned Tables)
此示例创建了一个父表 orders,该表在 order_date 上进行范围分区。引用分区子表 order_items 创建了四个分区:Q1_2015Q2_2015Q3_2015Q4_2015,其中每个分区都包含与相应父分区中的订单相对应的 order_items 行。

CREATE TABLE orders
    ( order_id           NUMBER(12),
      order_date         DATE,
      order_mode         VARCHAR2(8),
      customer_id        NUMBER(6),
      order_status       NUMBER(2),
      order_total        NUMBER(8,2),
      sales_rep_id       NUMBER(6),
      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 AI Database VLDB and Partitioning Guide》(《Oracle AI 数据库 VLDB 与分区指南》),了解引用分区概述

Composite Partitioning(复合分区)

在复合分区(Composite Partitioning)中,表首先按一种数据分布方法进行分区,然后每个分区再使用第二种数据分布方法进一步细分为子分区。因此,复合分区结合了基本的数据分布方法。给定分区的所有子分区代表数据的一个逻辑子集。

复合分区提供了几个优点:

  • 根据 SQL 语句,在一个或两个维度上进行分区修剪(Partition Pruning)可以提高性能。
  • 查询可能能够在任一维度上使用完全或部分的分区级联接(Partition-Wise Join)。
  • 您可以对单个表执行并行的备份和恢复。
  • 分区的数量比单层级分区中的多,这可能有利于并行执行。
  • 您可以实施滚动窗口来支持历史数据,并且在许多语句可以从分区修剪或分区级联接中受益的情况下,仍然可以在另一个维度上进行分区。
  • 您可以根据分区键的标识,以不同的方式存储数据。例如,您可能决定将特定产品类型的数据以只读、压缩格式存储,而保持其他产品类型的数据不压缩。

范围、列表和哈希分区都有资格作为复合分区表的子分区策略。下图提供了范围-哈希和范围-列表复合分区的图形视图。

Figure 6-1 Composite Range-List Partitioning(图6-1 复合范围-列表分区)

数据库将复合分区表中的每个子分区存储为一个单独的段(Segment)。因此,子分区的属性可能不同于表的属性,也不同于子分区所属分区的属性。

另请参见
《Oracle AI Database VLDB and Partitioning Guide》(《Oracle AI 数据库 VLDB 与分区指南》),了解更多关于复合分区的信息

Partitioned Tables(分区表)

分区表(Partitioned Table)由一个或多个分区组成,这些分区单独管理,并且可以独立于其他分区运行。

一个表要么是分区的,要么是非分区的。即使一个分区表只包含一个分区,该表也不同于非分区表,非分区表不能向其添加分区。

  • Segments for Partitioned Tables(分区表的段)
    一个分区表由一个或多个表分区段组成。

  • Compression for Partitioned Tables(分区表的压缩)
    堆组织表的某些或所有分区可以以压缩格式存储。

另请参见

  • “Partition Characteristics(分区特征)”,了解分区表示例
  • “Overview of Index-Organized Tables(索引组织表概述)”,了解索引组织表的目的和特征,它也可以受益于分区,从而提供改进的可管理性、可用性和性能。
Segments for Partitioned Tables(分区表的段)

分区表由一个或多个表分区段组成。

如果您创建一个名为 hash_products 的分区表,则不会为此表分配表段(Table Segment)。相反,数据库将每个表分区的数据存储在其自己的分区段中。每个表分区段包含一部分表数据。

当外部表(External Table)被分区时,所有分区都驻留在数据库外部。在混合分区表中,一些分区存储在段中,而另一些则存储在外部。例如,sales 表的某些分区可能存储在数据文件中,而其他分区则存储在电子表格中。

另请参见

  • “Overview of External Tables(外部表概述)”
  • “Overview of Segments(段概述)”
    了解对象和段之间的关系
Compression for Partitioned Tables(分区表的压缩)

堆组织表的某些或所有分区可以以压缩格式存储。

压缩可以节省空间并加快查询执行速度。因此,压缩在插入和更新操作量较小的环境(如数据仓库)以及 OLTP 环境中都很有用。

您可以声明表压缩(Table Compression)的属性,用于表空间、表或表分区。如果在表空间级别声明,则在该表空间中创建的表默认是压缩的。您可以更改表的压缩属性,在这种情况下,更改仅适用于进入该表的新数据。因此,一个表或分区可能同时包含压缩和未压缩的块,这保证了数据大小不会因为压缩而增加。如果压缩可能会增加块的大小,则数据库不会对该块应用压缩。

另请参见

  • “Table Compression(表压缩)”,了解表压缩的类型,包括基本压缩、高级行压缩和混合列压缩
  • 《Oracle AI Database Data Warehousing Guide》(《Oracle AI 数据库数据仓库指南》),了解数据仓库中的表压缩

Partitioned Indexes(分区索引)

分区索引(Partitioned Index)是一种索引,与分区表一样,它被划分为更小且更易于管理的片段。

全局索引是独立于在其上创建它们的表进行分区的,而本地索引则自动链接到表的分区方法。与分区表一样,分区索引提高了可管理性、可用性、性能和可伸缩性。下图显示了索引分区选项。

Figure 6-2 Index Partitioning Options(图6-2 索引分区选项)

  • Local Partitioned Indexes(本地分区索引)
    在本地分区索引(Local Partitioned Index)中,索引在与表相同的列上进行分区,具有相同数量的分区和相同的分区边界。

  • Global Partitioned Indexes(全局分区索引)
    全局分区索引(Global Partitioned Index)是一个 B 树索引,它独立于在其上创建它的基础表进行分区。单个索引分区可以指向任何或所有表分区,而在本地分区索引中,索引分区和表分区之间存在一一对应的等价关系。

另请参见

  • “Introduction to Indexes(索引简介)”,了解唯一索引和非唯一索引之间的区别以及不同的索引类型
  • 《Oracle AI Database VLDB and Partitioning Guide》(《Oracle AI 数据库 VLDB 与分区指南》),了解更多关于分区索引以及如何决定使用哪种类型的信息
Local Partitioned Indexes(本地分区索引)

在本地分区索引(Local Partitioned Index)中,索引在与表相同的列上进行分区,具有相同数量的分区和相同的分区边界。

每个索引分区恰好与基础表的一个分区相关联,因此一个索引分区中的所有键仅指向存储在单个表分区中的行。通过这种方式,数据库会自动将索引分区与其关联的表分区同步,使每个表-索引对独立。

本地分区索引在数据仓库环境中很常见。本地索引提供以下优点:

  • 可用性得到提高,因为导致分区中数据无效或不可用的操作只影响该分区。
  • 分区维护得到简化。当移动表分区时,或者当数据从一个分区中老化出去时,只需重建或维护关联的本地索引分区。在全局索引中,必须重建或维护所有索引分区。
  • 如果对分区进行时间点恢复,则可以将索引恢复到恢复时间点(参见《Oracle AI Database Backup and Recovery User’s Guide》)。整个索引不需要重建。

“Hash Partitioning(哈希分区)” 中的示例显示了分区表 hash_sales 的创建语句,使用 prod_id 列作为分区键。以下示例在 hash_sales 表的 time_id 列上创建一个本地分区索引:

CREATE INDEX hash_sales_idx ON hash_sales(time_id) LOCAL;

在图 6-3 中,hash_products 表有两个分区,因此 hash_sales_idx 有两个分区。每个索引分区与不同的表分区相关联。索引分区 SYS_P38 索引表分区 SYS_P33 中的行,而索引分区 SYS_P39 索引表分区 SYS_P34 中的行。

Figure 6-3 Local Index Partitions(图6-3 本地索引分区)
在这里插入图片描述

您不能显式地向本地索引添加分区。相反,只有在向基础表添加分区时,才会将新分区添加到本地索引。同样,您也不能显式地从本地索引中删除分区。相反,只有在从基础表中删除分区时,才会删除本地索引分区。

与其他索引一样,您可以在分区表上创建位图索引(Bitmap Index)。唯一的限制是位图索引必须是分区表的本地索引——它们不能是全局索引。全局位图索引仅在非分区表上受支持。

  • Local Prefixed and Nonprefixed Indexes(本地前缀索引和非前缀索引)
    本地分区索引可以是前缀的或非前缀的。

  • Local Partitioned Index Storage(本地分区索引存储)
    与表分区一样,本地索引分区存储在其自己的段中。每个段包含总索引数据的一部分。因此,由四个分区组成的本地索引不存储在单个索引段中,而是存储在四个单独的段中。

Local Prefixed and Nonprefixed Indexes(本地前缀索引和非前缀索引)

本地分区索引可以是前缀的或非前缀的。

索引子类型定义如下:

  • 本地前缀索引
    在这种情况下,分区键位于索引定义的前导边缘。在"Range Partitioning(范围分区)"的 time_range_sales 示例中,表在 time_id 上按范围分区。此表上的本地前缀索引会将 time_id 作为其列表中的第一列。

  • 本地非前缀索引
    在这种情况下,分区键不在索引列列表的前导边缘,并且根本不需要在列表中。在"Local Partitioned Indexes(本地分区索引)"的 hash_sales_idx 示例中,索引是本地非前缀的,因为分区键 product_id 不在前导边缘。

两种类型的索引都可以利用分区消除(Partition Elimination)(也称为分区修剪(Partition Pruning)),当优化器通过将分区排除在考虑范围之外来加速数据访问时,就会发生这种情况。查询(Query)是否可以消除分区取决于查询谓词(Query Predicate)。使用本地前缀索引的查询总是允许索引分区消除,而使用本地非前缀索引的查询可能不允许。

另请参见
《Oracle AI Database VLDB and Partitioning Guide》(《Oracle AI 数据库 VLDB 与分区指南》),了解如何使用前缀和非前缀索引

Local Partitioned Index Storage(本地分区索引存储)

与表分区一样,本地索引分区存储在其自己的段中。每个段包含总索引数据的一部分。因此,由四个分区组成的本地索引不存储在单个索引段中,而是存储在四个单独的段中。

另请参见
《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE INDEX ... LOCAL 示例

Global Partitioned Indexes(全局分区索引)

全局分区索引(Global Partitioned Index)是一个 B 树索引,它独立于在其上创建它的基础表进行分区。单个索引分区可以指向任何或所有表分区,而在本地分区索引中,索引分区和表分区之间存在一一对应的等价关系。

通常,全局索引对于 OLTP 应用程序很有用,在 OLTP 中,快速访问、数据完整性和可用性非常重要。在 OLTP 系统中,表可能按一个键(例如,employees.department_id 列)进行分区,但应用程序可能需要使用许多不同的键(例如,按 employee_idjob_id)来访问数据。全局索引在这种情况下很有用。

例如,假设您在 “Range Partitioning(范围分区)” 的 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));

如图 6-4 所示,全局索引分区可以包含指向多个表分区的条目。索引分区 p1 指向 channel_id2 的行,索引分区 p2 指向 channel_id3 的行,索引分区 p3 指向 channel_id49 的行。

Figure 6-4 Global Partitioned Index(图6-4 全局分区索引)

另请参见

  • 《Oracle AI Database VLDB and Partitioning Guide》(《Oracle AI 数据库 VLDB 与分区指南》),了解如何管理全局分区索引
  • 《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE INDEXGLOBAL PARTITION 子句

Partial Indexes for Partitioned Tables(分区表的局部索引)

局部索引(Partial Index)是一种与关联分区表的索引属性相关的索引。

这种相关性使您能够指定哪些表分区被索引。局部索引提供以下优点:

  • 未被索引的表分区避免了消耗不必要的索引存储空间。
  • 可以提高加载和查询的性能。
    在 Oracle Database 12c 之前,交换分区操作需要对关联的全局索引进行物理更新以保持其可用。从 Oracle Database 12c 开始,如果分区维护操作中涉及的分区不是局部全局索引的一部分,则索引保持可用,无需任何全局索引维护。
  • 如果您在索引创建时仅对某些表分区创建索引,并且以后对其他分区创建索引,那么您可以减少索引创建所需的排序空间。

您可以为表的各个分区打开或关闭索引。局部本地索引不会为所有关闭了索引的表分区提供可用的索引分区。全局索引(无论是否分区)会排除所有关闭了索引的分区中的数据。数据库不支持对强制执行唯一约束的索引使用局部索引。

图 6-5 显示与图 6-4 相同的全局索引,除了全局索引是局部的。表分区 SALES_1998SALES_2000 的索引属性设置为 OFF,因此局部全局索引不会索引它们。

Figure 6-5 Partial Global Partitioned Index(图6-5 局部全局分区索引)

Using Object Store for Older Partitions(对较旧分区使用对象存储)

对于只读分区,您可以使用低成本存储,例如云中的对象存储。

在过去十年中,数据量急剧增长。此外,政府法规和政策在许多情况下强制要求长时间保留数据。Oracle 数据库客户针对超大型数据库使用各种数据管理策略。客户试图满足以下主要目标:
以尽可能低的成本存储海量数据;满足新的数据保留和保护法规要求;通过基于更多数据量进行更好的分析来改善业务机会。

本地部署客户有各种解决方案来实现上述目标。Oracle AI Database 现在为您提供了此特性,允许您通过利用低成本存储层(例如云中的对象存储)为云数据库制定类似的数据管理策略。

随着数据集的发展和增长,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(对象存储文件的凭据管理)
    访问对象存储中的文件需要凭据。

  • Moving Datafiles Back From Object Storage Into Traditional Storage(将数据文件从对象存储移回传统存储)
    如果必须更新对象存储中的数据,则必须先将数据移回传统存储。

  • Deleting Object Store Data Files(删除对象存储数据文件)
    您可以使用标准的 DROP TABLESPACE 命令删除包含数据文件的表空间。

Moving Older Partitions and Read-Only Tablespaces to Object Store(将较旧分区和只读表空间移动到对象存储)

您可以创建基于时间的分区策略,并将只读表空间的数据文件移动到成本较低的存储层,如对象存储。

假设您有一个名为 ‘orders’ 的表,该表在 DATE 列上进行了范围分区。每个分区都包含特定年份的行,并且存在 2022 年和 2023 年的现有分区。随着新年的临近,数据库管理员决定为 2024 年添加一个新分区。同时,数据库管理员决定将最旧的分区移动到低成本存储层。

下面的工作流示例表示表空间具有单个表的单个分区的情况。这不是限制。只读表空间可以包含一个或多个表的分区。除了使表空间只读外,您还可以选择使分区只读。作为最佳实践,您可以使表空间只读,并在将文件移动到对象存储之前等待一段定义明确的时间。这将确保任何更新只读数据的尝试都会被捕获并返回错误。当数据仍在 Exadata 或其他传统存储中时,将可变数据移动到另一个表空间要快得多。如果需要从对象存储复制此数据,则速度会慢得多。

create tablespace orders_2022 DATAFILE '+DATA_DG/orders_2022.dbf' size 100g;
create tablespace orders_2023 DATAFILE '+DATA_DG/orders_2023.dbf' size 100g;
create table orders
( prod_id         NUMBER NOT NULL,
  time_id         DATE NOT NULL,
  quantity_sold   NUMBER(10,2) NOT NULL,
  amount_sold     NUMBER(10,2) NOT NULL)
    partition by range (time_id)
    ( partition orders_2022 VALUES LESS THAN (TO_DATE('2023-01-01 00:00:00', 
'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
        TABLESPACE orders_2022,
      partition orders_2023 VALUES LESS THAN (TO_DATE('2024-01-01 00:00:00', 
'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
        TABLESPACE orders_2023)
  ENABLE ROW MOVEMENT;
create tablespace orders_2024 DATAFILE '+DATA_DG/orders_2024.dbf' size 100g;
alter table orders
  ADD partition orders_2024
  values less than (TO_DATE('2024-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 
'NLS_CALENDAR=GREGORIAN'))
  TABLESPACE orders_2024;
alter tablespace orders_2022 read only;
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';
Accessing Objects in Object Storage(访问对象存储中的对象)

从对象存储中的表和分区访问数据对用户和 SQL 客户端将是完全透明的。

数据库输入/输出子系统将在内部查询并提供存储在对象存储中的文件中的数据块。以下 SQL 将查询只读分区 orders_2022 中的行,该分区已在之前的示例中移动到对象存储。

select prod_id
from   orders
where  time_id < TO_DATE('2020-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 
'NLS_CALENDAR=GREGORIAN');

现有的索引(全局和本地)可以透明地工作。从客户端的角度来看,查询混合列压缩数据和 TDE 加密数据也可以透明地工作。

Credential Management For Object Store Files(对象存储文件的凭据管理)

访问对象存储中的文件需要凭据。

凭据(Credential)是存储用户名和密码的数据库对象。凭据数据经过加密,并安全地存储在创建它的 PDB 模式中。

当您计划将数据文件移动到对象存储时,必须定义一个默认凭据。此默认凭据用于访问对象存储中的数据文件。

选择具有最小必要权限的默认凭据。在默认凭据启用对对象存储的访问之后,标准的数据库身份验证和授权将确定哪些数据库用户可以查询或操作这些数据文件。

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';
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 数据库中,全球分布式数据库特性使您能够将任何表拆分为称为分片(Shard)的片段,这些片段可以存储在多个数据库中。

水平分区涉及跨分片拆分数据库表,使得每个分片包含具有相同列但不同行子集的表。以这种方式拆分的表也称为分片表(Sharded Table)。

下图显示了一个跨三个分片水平分区的表。

Figure 6-6 Horizontal Partitioning of a Table Across Shards(图6-6 跨分片的表水平分区)

  • Sharded Tables(分片表)
    数据库表跨分片拆分,使得每个分片包含具有相同列但不同行子集的表。以这种方式拆分的表称为分片表(Sharded Table)。

另请参见
《Oracle Globally Distributed AI Database》

Sharded Tables(分片表)

数据库表跨分片拆分,使得每个分片包含具有相同列但不同行子集的表。以这种方式拆分的表称为分片表(Sharded Table)。

下图显示了一组表(称为表族)如何跨一组分片 PDB 进行水平分区,使得每个分片包含一个数据子集,分别用红色、黄色和蓝色行表示。

Figure 6-7 Horizontal Partitioning of a Table Across Shards(图6-7 跨分片的表水平分区)

分区在表空间级别基于分片键(Sharding Key)跨分片分布。键的示例包括客户 ID、账号和国家/地区 ID。

分片表的每个分区驻留在单独的表空间中,每个表空间与特定的分片相关联。根据分片方法的不同,可以自动建立关联,也可以由管理员定义。

尽管分片表的分区驻留在多个分片中,但对于应用程序而言,该表的外观和行为与单个数据库中的分区表完全相同。应用程序发出的 SQL 语句永远不必引用分片,也不必依赖于分片的数量及其配置。

用于表分区的熟悉 SQL 语法指定了行应如何跨分片进行分区。例如,以下 SQL 语句创建了一个分片表,基于分片键 cust_id 跨分片水平分区表。

CREATE SHARDED TABLE customers 
( cust_id     NUMBER NOT NULL,
  name        VARCHAR2(50),
  address     VARCHAR2(250),
  region      VARCHAR2(20),
  class       VARCHAR2(3),
  signup      DATE,
  CONSTRAINT cust_pk PRIMARY KEY(cust_id)
)
PARTITION BY CONSISTENT HASH (cust_id)
PARTITIONS AUTO
TABLESPACE SET ts1;

上例中的分片表通过一致性哈希进行分区,这是一种常用于可伸缩分布式系统的特殊哈希分区类型。此技术自动跨分片分布表空间,以提供数据和负载的均匀分布。

注意:分片表上不支持全局索引,但支持本地索引。

Overview of Views(视图概述)

视图(View)是一个或多个表的逻辑表示。本质上,视图是一个存储的查询。

视图从它所基于的称为基表(Base Table)的表中获取数据。基表可以是表或其他视图。对视图执行的所有操作实际上都会影响基表。您可以在大多数使用表的地方使用视图。

注意:物化视图使用与标准视图不同的数据结构。

视图使您能够为不同类型的用户定制数据的呈现方式。视图通常用于:

  • 通过限制对表的一组预定义行或列的访问来提供额外的表安全级别
    例如,图 6-8 显示了 staff 视图如何不显示基表 employeessalarycommission_pct 列。

  • 隐藏数据复杂性
    例如,可以使用连接(Join)定义单个视图,连接是多个表中相关列或行的集合。但是,该视图隐藏了这些信息实际上源自多个表的事实。查询也可能对表信息执行大量的计算。因此,用户可以在不知道如何执行连接或计算的情况下查询视图。

  • 以不同于基表的角度呈现数据
    例如,可以重命名视图的列,而不会影响视图所基于的表。

  • 将应用程序与基表定义的更改隔离开来
    例如,如果视图的定义查询引用了四列表中的三列,并且向该表添加了第五列,则视图的定义不会受到影响,并且使用该视图的所有应用程序也不会受到影响。

对于视图使用的一个示例,考虑 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(图6-8 视图)

  • Characteristics of Views(视图的特征)
    与表不同,视图不分配存储空间,视图也不包含数据。相反,视图由一个查询定义,该查询从视图引用的基表中提取或派生数据。因为视图基于其他对象,所以除了在数据字典中存储定义视图的查询外,它不需要其他存储。

  • Updatable Join Views(可更新连接视图)
    连接视图(Join View)在其 FROM 子句中包含多个表或视图。

  • Object Views(对象视图)
    正如视图是虚拟表一样,对象视图(Object View)是虚拟对象表。视图中的每一行都是一个对象,它是对象类型(Object Type)的一个实例。对象类型是用户定义的数据类型。

另请参见

  • “Overview of Materialized Views(物化视图概述)”
  • 《Oracle AI Database Administrator’s Guide》(《Oracle AI 数据库管理员指南》),了解如何管理视图
  • 《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE VIEW 的语法和语义

Characteristics of Views(视图的特征)

与表不同,视图不分配存储空间,视图也不包含数据。相反,视图由一个查询定义,该查询从视图引用的基表中提取或派生数据。因为视图基于其他对象,所以除了在数据字典中存储定义视图的查询外,它不需要其他存储。

视图依赖于其引用的对象,这些依赖关系由数据库自动处理。例如,如果您删除并重新创建视图的基表,则数据库会确定新的基表是否可接受视图定义。

  • Data Manipulation in Views(视图中的数据操作)
    因为视图源自表,所以它们有许多相似之处。用户可以查询视图,并且在一些限制下,他们可以对视图执行 DML 操作。对视图执行的操作会影响视图的某个基表中的数据,并受基表的完整性约束和触发器的约束。

  • How Data Is Accessed in Views(视图中数据的访问方式)
    Oracle AI Database 将视图定义作为定义视图的查询文本存储在数据字典中。

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 创建带约束的视图,以便针对该视图发出的 INSERTUPDATE 语句不会导致视图无法选择的行。因此,可以插入部门 10 的员工行,但不能插入部门 30 的行。

另请参见
《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE VIEW 语句中的子查询限制

How Data Is Accessed in Views(视图中数据的访问方式)

Oracle AI Database 将视图定义作为定义视图的查询文本存储在数据字典中。

当您在 SQL 语句中引用视图时,Oracle AI Database 会执行以下任务:

  1. 将对视图的查询(只要可能)与定义视图及任何基础视图的查询合并
    Oracle AI Database 会优化合并后的查询,就像您在未引用视图的情况下发出查询一样。因此,Oracle AI Database 可以使用任何引用的基表列上的索引,无论这些列是在视图定义中还是在对视图的用户查询中被引用。
    有时 Oracle AI Database 无法将视图定义与用户查询合并。在这种情况下,Oracle AI Database 可能无法使用引用列上的所有索引。

  2. 在共享 SQL 区域中解析合并后的语句
    Oracle AI Database 仅在不存在包含类似语句的现有共享 SQL 区域时,才会在新的共享 SQL 区域中解析引用视图的语句。因此,视图提供了减少与共享 SQL 关联的内存使用的好处。

  3. 执行 SQL 语句

以下示例说明了查询视图时的数据访问。假设您基于 employeesdepartments 表创建 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 AI Database 合并视图和用户查询以构建以下查询,然后执行该查询以检索数据:

SELECT last_name
FROM   employees, departments
WHERE  employees.department_id = departments.department_id 
AND    departments.department_id = 10 
AND    employees.employee_id = 200;

另请参见

  • “Shared SQL Areas(共享 SQL 区域)”
  • “Overview of the Optimizer(优化器概述)”
  • 《Oracle AI Database SQL Tuning Guide》(《Oracle AI 数据库 SQL 调优指南》),了解查询优化

Updatable Join Views(可更新连接视图)

连接视图(Join View)在其 FROM 子句中包含多个表或视图。

在以下示例中,staff_dept_10_30 视图连接了 employeesdepartments 表,仅包括部门 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;

可更新连接视图(Updatable Join View),也称为可修改连接视图(Modifiable Join View),涉及两个或多个基表或视图,并允许 DML 操作。可更新视图在 SELECT 语句的顶级 FROM 子句中包含多个表,并且不受 WITH READ ONLY 子句的限制。

要成为本质上可更新的视图,视图必须满足几个条件。例如,一个通用规则是,对连接视图的 INSERTUPDATEDELETE 操作一次只能修改一个基表。以下对 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                    UPD
------------------------------ ------------------------------ ---
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

从 Oracle Database 21c 版本开始,不强制要求连接视图中的所有可更新列都必须映射到键保留表(Key-Preserved Table)的列。键保留表是这样一个表,其中的基础表的每一行在查询输出中最多出现一次。在 staff_dept_10_30 视图中,department_iddepartments 表的主键,因此 employees 表中的每一行在结果集中最多出现一次,这使得 employees 表成为键保留表。departments 表不是键保留表,因为它的每一行可能在结果集中出现多次。如果 UPDATE 操作只更新单个表中的列,并且更新是确定性的,即它只更新每一行一次,则可以更新非键保留表中的列。

另请参见
《Oracle AI Database Administrator’s Guide》(《Oracle AI 数据库管理员指南》),了解如何更新连接视图

Object Views(对象视图)

正如视图是虚拟表一样,对象视图(Object View)是虚拟对象表。视图中的每一行都是一个对象,它是对象类型(Object Type)的一个实例。对象类型是用户定义的数据类型。

您可以像检索、更新、插入和删除关系数据一样操作,就像它被存储为对象类型一样。您还可以定义具有对象数据类型列的视图,例如对象、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';

对象视图在原型设计或向面向对象的应用程序过渡时非常有用,因为视图中的数据可以从关系表中获取,并像表被定义为对象表一样被访问。您可以运行面向对象的应用程序,而无需将现有表转换为不同的物理结构。

另请参见

  • 《Oracle AI Database Object-Relational Developer’s Guide》(《Oracle AI 数据库对象关系开发人员指南》),了解对象类型和对象视图
  • 《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE TYPE 语句

Overview of Materialized Views(物化视图概述)

物化视图(Materialized View)是一个已提前作为模式对象存储或“物化”的查询结果。该查询的 FROM 子句可以命名表、视图或物化视图。

物化视图通常在复制中用作主表(Master Table),在数据仓库中用作事实表(Fact Table)。物化视图用于汇总、计算、复制和分发数据。它们适用于各种计算环境,例如:

  • 在数据仓库中,物化视图可以计算和存储由聚合函数(如求和与平均值)生成的数据。
    汇总(Summary)是一种聚合视图,它通过预先计算连接和聚合操作并将结果存储在表中来减少查询时间。物化视图等同于汇总。您也可以使用物化视图计算带或不带聚合的连接。

  • 在使用 XStream 和 Oracle GoldenGate 实现的物化视图复制中,该视图包含来自单个时间点的表的完整或部分副本。
    物化视图在分布式站点复制数据,并同步在多个站点执行的更新。这种复制形式适用于诸如现场销售等数据库并非始终连接到网络的环境。

  • 在移动计算环境中,物化视图可以将数据子集从中央服务器下载到移动客户端,并定期从中央服务器刷新,以及由客户端向中央服务器传播更新。

在复制环境中,物化视图与不同数据库(称为主数据库(Master Database))中的表共享数据。在主站点上与物化视图相关联的表是主表(Master Table)。图 6-9 说明了一个数据库中的物化视图基于另一个数据库中的主表。对主表的更新会复制到物化视图数据库。

Figure 6-9 Materialized View(图6-9 物化视图)

  • Characteristics of Materialized Views(物化视图的特征)
    物化视图共享索引和非物化视图的某些特征。

  • Refresh Methods for Materialized Views(物化视图的刷新方法)
    数据库通过在基表更改后刷新物化视图来维护物化视图中的数据。刷新方法可以是增量刷新或完全刷新。

  • Automatic Materialized Views(自动物化视图)
    从 Oracle Database 21c 版本开始,可以自动创建和维护物化视图。

  • Query Rewrite(查询重写)
    查询重写(Query Rewrite)将以主表形式编写的用户请求转换为包含物化视图的语义等效请求。

另请参见

  • 《Oracle AI Database Data Warehousing Guide》(《Oracle AI 数据库数据仓库指南》),了解更多关于汇总的信息
  • 《Oracle AI Database XStream Guide》(《Oracle AI 数据库 XStream 指南》),了解 XStream 简介
  • http://www.oracle.com/technetwork/middleware/goldengate/documentation/index.html,了解更多关于 Oracle GoldenGate 的信息
  • 《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE 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

物化视图可以被分区。您可以在分区表上定义物化视图,并在物化视图上定义一个或多个索引。

另请参见
《Oracle AI Database Data Warehousing Guide》(《Oracle AI 数据库数据仓库指南》),了解如何在数据仓库中使用物化视图

Refresh Methods for Materialized Views(物化视图的刷新方法)

数据库通过在基表更改后刷新物化视图来维护物化视图中的数据。刷新方法可以是增量刷新或完全刷新。

  • Complete Refresh(完全刷新)
    完全刷新(Complete Refresh)执行定义物化视图的查询。完全刷新在您最初创建物化视图时发生,除非物化视图引用了一个预构建表,或者您将表定义为 BUILD DEFERRED

  • Incremental Refresh(增量刷新)
    增量刷新(Incremental Refresh),也称为快速刷新(Fast Refresh),仅处理对现有数据的更改。此方法消除了从头开始重建物化视图的需要。仅处理更改可以带来非常快的刷新时间。

  • In-Place and Out-of-Place Refresh(就地刷新和非就地刷新)
    对于完全和增量方法,数据库可以就地刷新物化视图,即直接在视图上执行刷新语句;或者非就地刷新。

Complete Refresh(完全刷新)

完全刷新(Complete Refresh)执行定义物化视图的查询。完全刷新在您最初创建物化视图时发生,除非物化视图引用了一个预构建表,或者您将表定义为 BUILD DEFERRED

完全刷新可能会很慢,特别是当数据库必须读取和处理大量数据时。您可以在创建物化视图后的任何时间执行完全刷新。

Incremental Refresh(增量刷新)

增量刷新(Incremental Refresh),也称为快速刷新(Fast Refresh),仅处理对现有数据的更改。此方法消除了从头开始重建物化视图的需要。仅处理更改可以带来非常快的刷新时间。

您可以按需刷新物化视图,也可以定期刷新。或者,您可以将物化视图配置为与其基表位于同一数据库中,以便在事务提交对基表的更改时进行刷新。

快速刷新有以下几种形式:

  • 基于日志的刷新
    在这种刷新类型中,物化视图日志(Materialized View Log)或直接加载器日志(Direct Loader Log)记录了基表的更改。物化视图日志是一种模式对象,用于记录基表的更改,以便可以增量刷新在基表上定义的物化视图。每个物化视图日志都与一个基表关联。

  • 分区更改跟踪 (PCT) 刷新
    PCT 刷新仅在基表已分区时有效。PCT 刷新删除受影响的物化视图分区中的所有数据或受影响的部分数据,然后重新计算它们。数据库使用修改后的基表分区来识别视图中受影响的分区或部分数据。当对基表执行了分区维护操作时,PCT 刷新是唯一可用的增量刷新方法。

In-Place and Out-of-Place Refresh(就地刷新和非就地刷新)

对于完全和增量方法,数据库可以就地刷新物化视图,即直接在视图上执行刷新语句;或者非就地刷新。

非就地刷新会创建一个或多个外部表,在其上执行刷新语句,然后将物化视图或受影响的分区与外部表进行切换。此技术在刷新期间,尤其是当刷新语句需要很长时间才能完成时,可实现高可用性。

同步刷新(Synchronous Refresh)是一种非就地刷新。同步刷新不修改基表的内容,而是使用同步刷新包中的 API,通过同时将这些更改应用到基表和物化视图来确保一致性。此方法使一组表及其上定义的物化视图始终保持同步。在数据仓库中,同步刷新方法非常适合,原因如下:

  • 增量数据的加载受到严格控制,并定期进行。
  • 表及其物化视图通常以相同的方式进行分区,或者它们的分区通过函数依赖关系相关联。

另请参见
《Oracle AI Database Data Warehousing Guide》(《Oracle AI 数据库数据仓库指南》),了解如何刷新物化视图

Automatic Materialized Views(自动物化视图)

从 Oracle Database 21c 版本开始,可以自动创建和维护物化视图。

Oracle AI Database 可以自动创建和管理物化视图,以优化查询性能。在 DBA 很少或完全没有交互的情况下,后台任务会监控和分析工作负载特征,并识别物化视图将在哪些方面提高 SQL 性能。候选物化视图的性能优势会在后台(使用工作负载查询)进行衡量,然后才会对工作负载可见。

另请参见

  • 《Oracle AI Database Data Warehousing Guide》(《Oracle AI 数据库数据仓库指南》),了解更多信息
  • 《Oracle AI Database PL/SQL Packages and Types Reference》(《Oracle AI 数据库 PL/SQL 包和类型参考》),了解如何使用 DBMS_AUTO_MV 包来实现自动物化视图

Query Rewrite(查询重写)

查询重写(Query Rewrite)将以主表形式编写的用户请求转换为包含物化视图的语义等效请求。

当基表包含大量数据时,计算聚合或连接是昂贵且耗时的。因为物化视图包含预先计算的聚合和连接,查询重写可以使用物化视图快速回答查询。

查询转换器(Query Transformer)透明地重写请求以使用物化视图,无需用户干预,也无需在 SQL 语句中引用物化视图。因为查询重写是透明的,所以可以添加或删除物化视图,而不会使应用程序代码中的 SQL 失效。

通常,重写查询以使用物化视图而不是明细表会缩短响应时间。下图显示了数据库为原始查询和重写查询生成执行计划(Execution Plan),并选择成本最低的计划。

Figure 6-10 Query Rewrite(图6-10 查询重写)

另请参见

  • “Overview of the Optimizer(优化器概述)”,了解更多关于查询转换的信息
  • 《Oracle AI Database Data Warehousing Guide》(《Oracle AI 数据库数据仓库指南》),了解如何使用查询重写

Overview of Sequences(序列概述)

序列(Sequence)是一个模式对象,多个用户可以从该对象生成唯一的整数。序列生成器为数字数据类型提供了一种高度可扩展且性能良好的方法来生成代理键。

  • Sequence Characteristics(序列特征)
    序列定义表明了关于序列的一般信息,包括其名称以及序列是递增还是递减。

  • Concurrent Access to 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 的值。

另请参见

  • 《Oracle AI Database Get Started with Oracle AI Database Development》(《Oracle AI 数据库入门》),获取一个教程,向您展示如何创建序列
  • 《Oracle AI Database Administrator’s Guide》(《Oracle AI 数据库管理员指南》),了解如何在 SQL 语句中引用序列
  • 《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE SEQUENCE 的语法和语义

Concurrent Access to Sequences(序列的并发访问)

同一个序列生成器可以为多个表生成数字。

该生成器可以自动创建主键,并协调跨多行或多表的键。例如,序列可以为 orders 表和 customers 表生成主键。

序列生成器在多用户环境中非常有用,可以生成唯一数字,而没有磁盘 I/O 或事务锁定的开销。例如,两个用户同时向 orders 表插入新行。通过使用序列为 order_id 列生成唯一数字,两个用户都不必等待对方输入下一个可用的订单号。序列会自动为每个用户生成正确的值。

每个引用序列的用户都可以访问其当前序列号,这是会话(Session)中生成的最后一个序列号。用户可以发出语句来生成新的序列号,或者使用该会话最后生成的当前号码。在会话中的语句生成序列号后,该序列号仅对此会话可用。如果生成的单个序列号在一个事务中被使用但该事务最终被回滚,则这些序列号可能会被跳过。

警告:如果您的应用程序需要一组无间隔的数字,则不能使用 Oracle 序列。您必须使用自己开发的代码来序列化数据库中的活动。

另请参见
“Data Concurrency and Consistency(数据并发与一致性)”,了解会话如何同时访问数据

Overview of Dimensions(维度概述)

典型的数据仓库有两个重要组件:维度和事实。

维度(Dimension)是用于指定业务问题的任何类别,例如时间、地理位置、产品、部门和分销渠道。事实(Fact)是与一组特定维度值相关联的事件或实体,例如,销售的单位数量或利润。

多维请求的示例包括:

  • 显示 2013 年和 2014 年,在不断增加的地理位置维度聚合级别上(从州到国家再到地区),所有产品的总销售额。
  • 创建一个关于我们运营的交叉表分析,显示 2013 年和 2014 年南美洲各区域的费用。包括所有可能的小计。
  • 根据 2014 年汽车产品的销售收入,列出亚洲前 10 名销售代表,并对他们的佣金进行排名。

许多多维问题都需要聚合数据和数据集比较,通常跨越时间、地理位置或预算。

创建维度可以更广泛地使用查询重写特性。通过透明地重写查询以使用物化视图,数据库可以提高查询性能。

  • Hierarchical Structure of a Dimension(维度的层次结构)
    维度表(Dimension Table)是一个逻辑结构,用于定义成对列或列集之间的层次(父/子)关系。

  • Creation of Dimensions(维度的创建)
    您可以使用 CREATE DIMENSION SQL 语句创建维度。

另请参见
《Oracle AI Database Data Warehousing Guide》(《Oracle AI 数据库数据仓库指南》),了解更多关于维度的信息

Hierarchical Structure of a Dimension(维度的层次结构)

维度表(Dimension Table)是一个逻辑结构,用于定义成对列或列集之间的层次(父/子)关系。

例如,维度可以表明,在一行中,city 列暗示了 state 列的值,而 state 列暗示了 country 列的值。

在客户维度中,客户可以向上汇总到城市、州、国家、子区域和区域。数据分析通常从维度层次结构中的较高级别开始,并在情况需要时逐渐向下钻取。

子级别的每个值都与父级别的一个且只有一个值相关联。层次关系是从层次结构的一个级别到下一个级别的函数依赖关系。

维度没有分配给它的数据存储。维度信息存储在维度表中,而事实信息存储在事实表中。

另请参见

  • 《Oracle AI Database Data Warehousing Guide》(《Oracle AI 数据库数据仓库指南》),了解维度

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          CHILD OF 
      state         CHILD OF 
      country       CHILD OF 
      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 语句中指定列之间的层次关系。

另请参见
《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE DIMENSION 的语法和语义

Overview of Synonyms(同义词概述)

同义词(Synonym)是模式对象的别名。例如,您可以为表或视图、序列、PL/SQL 程序单元、用户定义的对象类型或另一个同义词创建同义词。因为同义词只是一个别名,所以除了它在数据字典中的定义外,不需要任何存储。

同义词可以简化数据库用户的 SQL 语句。同义词对于隐藏基础模式对象的标识和位置也很有用。如果必须重命名或移动基础对象,则只需重新定义同义词。基于该同义词的应用程序无需修改即可继续工作。

您可以创建私有同义词和公共同义词。私有同义词在特定用户的模式中,该用户控制其对其他用户的可用性。公共同义词由名为 PUBLIC 的用户组拥有,并且每个数据库用户都可以访问。

示例 6-5 公共同义词(Example 6-5 Public Synonym)
假设数据库管理员为 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 AI Database Administrator’s Guide》(《Oracle AI 数据库管理员指南》),了解如何管理同义词
  • 《Oracle AI Database SQL Language Reference》(《Oracle AI 数据库 SQL 语言参考》),了解 CREATE SYNONYM 的语法和语义
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐