PARTI-Oracle关系数据结构-分区、视图以及其他的对象

4. 分区、视图与其他对象

4.1. 分区概述

分区允许将非常大的表和索引分解成更小、更易于管理的部分,称为分区。每个分区是一个独立的对象,有自己的名称,并且可以选择拥有自己的存储特性。

为了说明分区的概念,假设一个人力资源经理有一个大盒子,里面装着员工文件夹。每个文件夹都列出了员工的雇用日期。经常有查询是针对特定月份雇用的员工。满足这类请求的一种方法是在员工雇用日期上创建一个索引,指明分散在整个盒子中的文件夹的位置。相比之下,分区策略使用许多较小的盒子,每个盒子包含特定月份雇用的员工文件夹。

使用较小的盒子有几个优点。当被要求检索6月份雇用的员工文件夹时,人力资源经理可以检索6月份的盒子。此外,如果任何小盒子暂时受损,其他小盒子仍然可用。搬家也变得更容易,因为经理不需要移动一个沉重的大盒子,而是可以移动几个小盒子。从应用程序的角度来看,只存在一个模式对象。DML语句不需要修改就可以访问分区表。分区对于许多不同类型的数据库应用程序都很有用,特别是那些管理大量数据的应用程序。好处包括:

  • 提高可用性
    一个分区的不可用并不会导致整个对象的不可用。查询优化器会自动从查询计划中移除未被引用的分区,因此当分区不可用时,查询不会受到影响。

  • 更简便的模式对象管理
    分区对象有可以集体或单独管理的部分。DDL语句可以操作分区,而不是整个表或索引。因此,您可以将重建索引或表等资源密集型任务分解。例如,您可以一次移动一个表分区。如果出现问题,那么只需要重新执行分区移动,而不需要重新移动整个表。此外,删除分区避免了执行大量的DELETE语句。

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

  • 在数据仓库中增强查询性能
    在数据仓库中,分区可以加速处理临时查询。例如,包含一百万行的销售表可以按季度分区。

4.1.1. 分区的特点

表或索引的每个分区必须具有相同的逻辑属性,例如列名、数据类型和约束。例如,表中的所有分区共享相同的列和约束定义,索引中的所有分区共享相同的索引列。然而,每个分区可以具有独立的物理属性,例如它所属的表空间。

4.1.1.1. 分区键

分区键是一组一个或多个列,它决定了分区表中每一行应该进入哪个分区。每一行都明确无误地被分配到一个单一的分区。在sale表中,可以指定time_id列为范围分区的键。数据库根据此列中的日期是否落在指定范围内来分配行到分区。Oracle数据库通过使用分区键自动将插入、更新和删除操作定向到适当的分区。

4.1.1..2. 分区策略

Oracle 分区提供了几种分区策略,这些策略控制数据库如何将数据放置到分区中。基本策略包括范围、列表和哈希分区。单级分区策略仅使用一种数据分布方法,例如,仅使用列表分区或仅使用范围分区。在复合分区中,表首先根据一种数据分布方法进行分区,然后每个分区进一步使用第二种数据分布方法划分为子分区。例如,你可以使用列表分区对 channel_id进行分区,并使用范围子分区对time_id进行分区。

范围分区:在范围分区中,数据库根据分区键值的范围将行映射到分区。范围分区是最常见的分区类型,通常与日期一起使用。

假设你想要填充示例4-1中显示的销售行到一个分区表中。

你使用示例 4-2 中的语句创建了一个名为 time_range_sales 的分区表。time_id 列是分区键。

Example 42 Range-Partitioned Table 
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)
);

之后,使用示例4-1中的行数据加载time_range_sales分区表。图4-1显示了四个分区中的行分布。数据库根据PARTITION BY RANGE子句中指定的规则,根据time_id值为每一行选择相应的分区。

范围分区键值决定了范围分区的高值,这被称为转换点。在图4-1中,SALES_1998分区包含分区键time_id值小于转换点01-JAN-1999的行。

数据库为超出该转换点的数据创建间隔分区。间隔分区通过指示数据库在插入到表中的数据超出所有范围分区时自动创建指定范围或间隔的分区,从而扩展了范围分区。在图4-1中,SALES_2001分区包含分区键time_id值大于或等于01-JAN-2001的行。

列表分区:在列表分区中,数据库使用一组离散值作为每个分区的分区键。可以使用列表分区来控制个别行如何映射到特定的分区。通过使用列表,可以在用于标识它们的键不方便排序时,对相关数据集进行分组和组织。假设你使用示例4-3中的语句创建了一个名为list_sales的列表分区表。channel_id列是分区键。

Example 43 List-Partitioned Table 
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)
);

之后,使用示例4-1中的行数据加载表。图4-2显示了两个分区中的行分布。数据库根据PARTITION BY LIST子句中指定的规则,根据channel_id值为每一行选择相应的分区。channel_id值为2或4的行存储在 EVEN_CHANNELS分区中,而channel_id值为3或9的行存储在ODD_CHANNELS分区中。

哈希分区:在哈希分区中,数据库根据数据库对用户指定的分区键应用的哈希算法将行映射到分区。行的目的地由数据库应用的内部哈希函数确定。哈希算法旨在均匀地将行分布在设备上,以便每个分区包含大约相同数量的行。

哈希分区对于分割大型表以提高可管理性非常有用。不是管理一个大型表,而是管理几个较小的部分。单个哈希分区的丢失不会影响剩余的分区,并且可以独立恢复。哈希分区在具有高更新争用的OLTP在线事务处理)系统中也非常有用。

例如,一个段被分割成几个部分,每个部分都会进行更新,而不是一个单独的段经历争用。假设你使用示例4-4中的语句创建了名为hash_sales的分区表。prod_id列是分区键。

Example 44 Hash-Partitioned Table 
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;

之后,你使用示例4-1中的行数据加载表。图4-3显示了两个分区中可能的行分布。请注意,这些分区的名称是由系统生成的。

当你插入行时,数据库会尝试将它们随机且均匀地分布在各个分区中。你不能指定行放置到哪个分区。数据库应用哈希函数,其结果决定了包含该行的分区。如果你更改分区的数量,数据库会重新在所有分区上分布数据。

4.1.2. 分区表

分区表由一个或多个分区组成,这些分区可以单独管理,并且可以独立于其他分区运行。表要么是分区的,要么不是分区的。即使分区表只包含一个分区,这个表也与不能添加分区的非分区表不同。"分区特性"第4-2页提供了分区表的例子。

分区表由一个或多个表分区段组成。如果你创建了一个名为hash_products的分区表,那么不会为这个表分配表段。相反,数据库将每个表分区的数据存储在其自己的分区段中。每个表分区段包含表数据的一部分。堆组织表的一些或所有分区可以以压缩格式存储。压缩节省空间,并且可以加快查询执行速度。因此,压缩在数据仓库环境中很有用,其中插入和更新操作的数量很小,在OLTP环境中也是如此。表压缩的属性可以为表空间、表或表分区声明。如果在表空间级别声明,则默认情况下在表空间中创建的表会被压缩。你可以更改表的压缩属性,这种情况下,更改只适用于进入该表的新数据。因此,单个表或分区可能包含压缩和未压缩的块,这保证了数据大小不会因为压缩而增加。如果压缩可能会增加块的大小,那么数据库不会将其应用于该块。

4.1.3. 分区索引

分区索引是一种像分区表一样被分解成更小、更易于管理的部分的索引。全局索引与它们所创建的表独立分区,而本地索引则自动与表的分区方法相关联。像分区表一样,分区索引提高了可管理性、可用性、性能和可扩展性。下图显示了索引分区选项。

4.1.3.1. 本地分区索引

在本地分区索引中,索引是按照与其所对应的表相同的列、相同数量的分区以及相同的分区边界进行分区的。每个索引分区与底层表的一个分区精确关联,这样,一个索引分区中的所有键仅引用存储在单个表分区中的行。通过这种方式,数据库自动将索引分区与其关联的表分区同步,使得每个表-索引对独立。本地分区索引在数据仓库环境中很常见。本地索引提供以下优势:

  • 可用性增加,因为使数据无效或不可用的分区操作仅影响该分区。
  • 分区维护简化。当移动表分区或数据从分区中老化时,只需重建或维护相关的本地索引分区。在全局索引中,所有索引分区都必须重建或维护。
  • 如果对分区进行点时间恢复,则索引可以恢复到恢复时间(见第18-14页的“数据文件恢复”)。不需要重建整个索引。

示例4-4显示了使用prod_id列作为分区键创建分区hash_sales表的语句。示例4-5在hash_sales表的time_id列上创建了一个本地分区索引。

Example 45 Local Partitioned Index 
CREATE INDEX hash_sales_idx ON hash_sales(time_id) LOCAL;

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

不能显式地向本地索引添加分区。相反,只有在向底层表添加分区时,才会向本地索引添加新分区。同样,不能显式地从本地索引中删除分区。相反,只有在从底层表删除分区时,本地索引分区才会被删除。

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

本地前缀和非前缀索引

本地分区索引分为以下子类别:

■ 本地前缀索引
在这种情况下,分区键位于索引定义的前端。在第4-3页的示例4-2中,表是根据time_id的范围进行分区的。在这张表上的本地前缀索引将把time_id作为其列列表中的第一列。

■ 本地非前缀索引
在这种情况下,分区键不在索引列列表的前端,甚至可能根本不在列表中。在第4-8页的示例4-5中,索引是本地非前缀的,因为分区键product_id不在前端。

两种类型的索引都可以利用分区消除(也称为分区修剪),这是当优化器通过排除某些分区来加速数据访问时发生的情况。一个查询能否消除分区取决于查询谓词。使用本地前缀索引的查询总是允许进行索引分区消除,而使用本地非前缀索引的查询可能不允许。

在Oracle数据库中,分区消除是一种性能优化技术,它允许数据库查询在执行时跳过不包含相关数据的分区,从而减少I/O操作和提高查询效率。本地前缀索引由于其分区键位于索引定义的前端,因此优化器更容易确定哪些分区可能包含查询所需的数据,从而更容易实现分区消除。相反,本地非前缀索引的分区键不在索引定义的前端,优化器可能无法直接从索引中确定哪些分区是相关的,因此在某些情况下可能无法实现分区消除。

本地分区索引的存储方式类似于表分区,每个本地索引分区都存储在自己的段中。每个段包含索引数据的一部分。因此,由四个分区组成的本地索引不是存储在单个索引段中,而是存储在四个独立的段中。

4.1.3.2. 全局分区索引

全局分区索引是一个B树索引,它的分区是独立于它所创建的底层表进行的。单个索引分区可以指向任何或所有表分区,而在本地分区索引中,索引分区和表分区之间存在一对一的关系。

一般来说,全局索引对于OLTP(在线事务处理)应用程序很有用,其中快速访问、数据完整性和可用性很重要。在OLTP系统中,一个表可能按一个键进行分区,例如,employees.department_id列,但应用程序可能需要通过许多不同的键来访问数据,例如,通过employee_id或job_id。在这种情况下,全局索引可能会很有用。

您可以按范围或哈希来分区全局索引。如果按范围分区,则数据库会根据您在列列表中指定的表列的值范围来分区全局索引。如果按哈希分区,则数据库会使用分区键列中的值的哈希函数来分配行到分区。例如,假设您在示例4-2中的time_range_sales表上创建了一个全局分区索引。在这个表中,1998年的销售行存储在一个分区中,1999年的销售行存储在另一个分区中,依此类推。示例4-6创建了一个按channel_id列范围分区的全局索引。

Example 46 Global Partitioned Index 
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)
);

如图4-5所示,全局索引分区可以包含指向多个表分区的条目。索引分区p1指向具有channel_id为2的行,索引分区p2指向具有channel_id为3的行,索引分区p3指向具有channel_id为4或9的行。

4.1.4 分区索引组织表

您可以按范围、列表或哈希对索引组织表(IOT)进行分区。分区对于提高IOT的可管理性、可用性和性能很有用。此外,使用IOT的数据容器可以利用分区其存储数据的能力。

请注意分区IOT的以下特性:
■ 分区列必须是主键列的子集。
■ 次级索引可以本地分区和全局分区。
■ 溢出数据段总是与表分区等分。

Oracle数据库支持在分区和非分区的索引组织表上创建位图索引。在索引组织表上创建位图索引需要一个映射表。

4.2. 视图概述

视图是对一个或多个表的逻辑表示。本质上,视图是一个存储的查询。视图从它所基于的表中获取数据,这些表被称为基表。基表可以是表或其他视图。对视图执行的所有操作实际上都会影响到基表。您可以在大多数使用表的地方使用视图。

注意:物化视图使用与标准视图不同的数据结构。详见第4-16页的“物化视图概述”。
视图使您能够根据不同类型用户的需求定制数据的展示。视图通常用于:

■ 通过限制对表的预设行或列的访问,提供额外的表安全级别
例如,图4-6显示了staff视图不显示基表employees的salary或commission_pct列。

■ 隐藏数据复杂性
例如,可以定义一个包含连接的单个视图,这是多个表中相关列或行的集合。然而,视图隐藏了这些信息实际上来自多个表的事实。查询还可能对表信息进行广泛的计算。因此,用户可以在不知道如何执行连接或计算的情况下查询视图。

■ 以不同于基表的视角展示数据
例如,可以重命名视图的列,而不会影响视图所基于的表。

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

作为使用视图的一个例子,考虑hr.employees表,它包含多个列和众多行。为了只允许用户查看这些列中的五列或特定的行,您可以如下创建视图:

CREATE VIEW staff AS 
SELECT employee_id, last_name, job_id, manager_id, department_id 
FROM employees;

与所有子查询一样,定义视图的查询不能包含FOR UPDATE子句。图4-6以图形方式展示了名为staff的视图。请注意,该视图只显示了基表中的五列。

4.2.1. 视图特点

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

视图对其引用的对象有依赖性,这些依赖性由数据库自动处理。例如,如果您删除并重新创建视图的基表,数据库将确定新的基表是否符合视图定义的要求。

4.2.1.1. 在视图中的数据操作

由于视图是从表派生出来的,它们有很多相似之处。例如,视图可以包含多达1000列,就像表一样。用户可以查询视图,并且在某些限制下,他们可以在视图上执行数据操作语言(DML)。对视图执行的操作可能会影响视图的某些基表中的数据,并受到基表的完整性约束和触发器的约束控制。

由于视图是从表派生出来的,它们有很多相似之处。例如,视图可以包含多达1000列,就像表一样。用户可以查询视图,并且在某些限制下,他们可以在视图上执行数据操作语言(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的行。

4.2.1.2. 视图中数据的访问方式

Oracle数据库将视图定义存储在数据字典中,作为定义视图的查询文本。当您在SQL语句中引用视图时,Oracle数据库执行以下任务:

  1. 合并查询(尽可能)针对视图的查询与定义视图和任何底层视图的查询,Oracle数据库优化合并后的查询,就像您在不引用视图的情况下发出查询一样。因此,无论列是否在视图定义或用户对视图的查询中引用,Oracle数据库都可以使用任何引用基表列上的索引。有时Oracle数据库无法将视图定义与用户查询合并。在这种情况下,Oracle数据库可能无法使用引用列上的所有索引。

  2. 在共享SQL区域解析合并后的语句
    如果没有任何现有的共享SQL区域包含类似的语句,Oracle数据库只有在引用视图的语句中解析一个新的共享SQL区域。因此,视图提供了与共享SQL相关的减少内存使用的好处。

  3. 执行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;

某个用户在employee_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;

4.2.2. 可更新的连接视图

连接视图被定义为在其FROM子句中包含多个表或视图的视图。在示例4-7中,staff_dept_10_30视图连接了employees和departments表,只包括部门10或30中的员工。

Example 47 Join View 
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数据字典视图的查询显示,示例4-7中创建的视图是可更新的:

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 

连接视图的所有可更新列必须映射到保留键的表的列。在连接查询中,保留键的表是这样一个表:在查询的输出中,底层表的每一行最多出现一次。在示例4-7中,department_id是departments表的主键,因此employees表中的每一行在结果集中最多出现一次,使employees表成为保留键的表。departments表不是保留键的表,因为它的每一行可能在结果集中出现多次。

4.2.3. 对象视图

就像视图是虚拟表一样,对象视图是虚拟对象表。视图中的每一行是一个对象,它是对象类型实例。对象类型是用户定义的数据类型。

您可以像存储为对象类型一样检索、更新、插入和删除关系数据。您还可以定义列是对象数据类型的视图,例如对象、REFs(引用)和集合(嵌套表和VARRAYs)。像关系视图一样,对象视图可以只展示您希望用户看到的数据。例如,一个对象视图可以展示关于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';

对象视图在原型设计或过渡到面向对象应用程序时非常有用,因为视图中的数据可以来自关系表,并可以像访问对象表一样访问。您可以在不将现有表转换为不同物理结构的情况下运行面向对象的应用程序。

4.3. 物化视图概述

物化视图是查询结果,这些结果已经提前存储或“物化”为模式对象。查询的FROM子句可以命名表、视图和物化视图。这些对象统称为主表(复制术语)或细节表(数据仓库术语)。

物化视图用于汇总、计算、复制和分发数据。它们适用于各种计算环境,例如:

■ 在数据仓库中,您可以使用物化视图来计算和存储由聚合函数(如总和和平均值)生成的数据。摘要是一个聚合视图,它通过预先计算连接和聚合操作并将结果存储在表中来减少查询时间。物化视图等同于摘要(见第17-17页的“数据仓库架构(基础)”)。您还可以使用物化视图来计算有或没有聚合的连接。如果兼容性设置为Oracle9i或更高版本,则物化视图可用于包含过滤选择的查询。

■ 在物化视图复制中,视图包含来自单个时间点的表的完整或部分副本。物化视图在分布式站点复制数据,并同步在多个站点执行的更新。这种复制形式适用于数据库不总是连接到网络的环境,例如现场销售。

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

在复制环境中,物化视图与不同数据库中的表共享数据,该数据库称为主数据库。与物化视图关联的主站点上的表是主表。图4-7展示了一个数据库中的物化视图基于另一个数据库中的主表。对主表的更新会复制到物化视图数据库。

4.3.1. 物化视图的特点

物化视图与非物化视图和索引有一些共同的特点。物化视图与索引相似之处在于:

■ 它们包含实际的数据并且需要占用存储空间。
■ 当它们所依赖的主表中的数据发生变化时,它们可以被刷新。
■ 当用于查询改写操作时,它们可以提高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

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

4.3.2. 物化视图的刷新方法

数据库通过在对主表进行更改后刷新物化视图来维护其中的数据。刷新方法可以是增量的,即快速刷新,或者是完全刷新。当物化视图最初被定义为BUILD IMMEDIATE时,会发生完全刷新,除非物化视图引用了一个预建表。刷新涉及执行定义物化视图的查询。这个过程可能会很慢,特别是当数据库必须读取和处理大量数据时。
快速刷新消除了从头开始重建物化视图的需要。因此,仅处理更改可以实现非常快的刷新时间。物化视图可以根据需求进行刷新,或者在规律的时间间隔内进行刷新。或者,当事务将其更改提交到主表时,与主表位于同一数据库的物化视图也可以进行刷新。对于使用快速刷新方法的物化视图,物化视图日志或直接加载器日志会记录对主表的更改。

物化视图日志是一个模式对象,它记录对主表数据的更改,以便在主表上定义的物化视图可以进行增量刷新。每个物化视图日志都与一个单独的主表相关联。物化视图日志位于其主表所在的同一个数据库和模式中。

4.3.3. 查询改写

查询改写是一种优化技术,它将用户用主表术语编写的请求转换为包含物化视图的语义等效请求。当基表包含大量数据时,计算聚合或连接是昂贵且耗时的。因为物化视图包含预计算的聚合和连接,查询改写可以使用物化视图快速回答问题。

优化器查询转换器透明地改写请求以使用物化视图,不需要用户干预,SQL语句中也不需要引用物化视图。由于查询改写是透明的,可以在不使应用程序代码中的SQL失效的情况下添加或删除物化视图。

通常情况下,将查询改写为使用物化视图而不是详细表可以提高响应时间。图4-8显示了数据库为原始和改写后的查询生成执行计划,并选择成本最低的计划。

4.4. 序列概述

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

4.4.1. 序列特点

序列定义指示一般信息,例如:

■ 序列的名称
■ 序列是递增还是递减
■ 数字之间的间隔
■ 数据库是否应该在内存中缓存生成的序列号集
■ 当达到限制时,序列是否应该循环

以下示例在示例模式oe中创建了名为customers_seq的序列。应用程序可以使用此序列在向customers表添加行时提供客户ID号码。

CREATE SEQUENCE customers_seq START WITH
1000
INCREMENT BY 1 NOCACHE NOCYCLE;

对序列customers_seq的第一次引用customers_seq.nextval将返回值1000。第二次引用将返回值1001。每次后续的引用都会返回比前一次引用大1的值。这意味着每次调用customers_seq.nextval时,都会按指定的增量(在此例中为1)递增生成一个新的唯一整数值。

4.4.2. 并发访问序列

同一个序列生成器可以为多个表生成数字。通过这种方式,数据库可以自动生成主键,并在多行或多表之间协调键。例如,一个序列可以为订单表和客户表生成主键。

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

每个引用序列的用户都可以访问到他或她当前的序列号,即会话中最后生成的序列号。用户可以发出语句生成一个新的序列号,或者使用会话最后生成的当前数字。在会话中生成序列号的语句之后,它只对这一会话可用。如果生成并在最终回滚的事务中使用了个别序列号,那么这些序列号可以被跳过。

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

4.4. 维度概述

典型的数据仓库有两个重要组成部分:维度和事实。维度是用于指定业务问题的任何类别,例如时间、地理、产品、部门和分销渠道。事实是与特定一组维度值相关联的事件或实体,例如销售单位或利润。

多维请求的例子包括以下内容:

■ 显示2010年和2011年所有产品在地理维度上从州到国家再到地区的逐级汇总的总销售额。
■ 创建一个交叉表格分析,显示2010年和2011年我们在南美地区的运营费用,包括所有可能的小计。
■ 根据2011年汽车产品的销售收入,列出亚洲前10名销售代表,并对他们的佣金进行排名。

许多多维问题需要聚合数据和数据集的比较,通常涉及时间、地理或预算。

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

4.4.1. 维度层次结构

维度表是一个逻辑结构,它定义了列对或列集之间的层次关系。维度本身没有分配数据存储。维度信息存储在维度表中,而事实信息存储在事实表中。

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

每个子级水平的值与父级水平的唯一值相关联。层次关系是从一个层次结构的一层到下一层的功能性依赖。

维度是通过SQL语句创建的。CREATE DIMENSION语句指定了:

■ 多个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 CHILD OF 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语句中指定。

4.5. 同义词概述

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

同义词可以简化数据库用户的SQL语句。同义词也有助于隐藏底层模式对象的身份和位置。如果底层对象必须重命名或移动时,只需要重新定义同义词。基于同义词的应用程序可以不经修改继续工作。您可以创建私有同义词和公共同义词。私有同义词位于特定用户的模式中,用户可以控制其他人对其的访问权限。公共同义词归名为PUBLIC的用户组所有,每个数据库用户都可以访问。在示例4-9中,数据库管理员为hr.employees表创建了一个名为people的公共同义词。然后用户连接到oe模式,并统计通过同义词引用的表中的行数。

Example 48 Public Synonym 
SQL> CREATE PUBLIC SYNONYM people FOR hr.employees;
Synonym created.
SQL> CONNECT oe Enter password: password Connected.
SQL> SELECT COUNT(*) FROM people; 
COUNT(*)
---------
107

谨慎使用公共同义词,因为它们会使数据库整合变得更加困难。正如示例4-9所示,如果另一位管理员尝试创建公共同义词people,那么创建将失败,因为数据库中只能存在一个名为people的公共同义词。公共同义词的过度使用会导致应用程序之间的命名空间冲突。

Example 49 Public Synonym
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语句中,同义词仅作为对象的别名。

posted @   脆皮老弟  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示