赞
踩
目录
本章介绍了模式对象,并讨论了表,这些表对象是最常见的模式对象类型。
本章包括以下几节:
数据库模式是数据库所有对象的集合,称为模式对象。您可以使用SQL创建和操作模式对象。
本节包含以下主题:
1.1.1. 模式对象类型
KingbaseES SQL提供许多类型的模式对象。模式对象的主要类型如下表所示:
对象 | 描述 |
---|---|
表 | 表按行存储数据。表是关系数据库中最重要的模式对象。 |
索引 | 索引提供指向存储在表的指定列中数据值的指针,实现对行进行直接、快速访问。KingbaseES数据库支持几种类型的索引。 |
分区 | 分区是大表和索引的一部分。每个分区都有自己的名称,并且可以选择拥有自己的存储特性。 |
视图 | 视图是对一个或多个表或其他视图中数据查询。可以将它们视为存储查询。视图实际上不包含数据。 |
序列 | 序列自动生成有序的整数值,可以由多个用户共享使用。通常,使用序列来生成主键值 |
同义词 | 同义词是另一个模式对象的别名对象。因为同义词只是一个别名,所以它不需要存储。除了在数据字典中的定义外,它不需要存储。 |
PL/SQL程序和包 | PL/SQL是SQL的过程扩展。PL/SQL子程序是一个命名PL/SQL的块,可以用一组参数调用。PL/SQL包对逻辑相关的PL/SQL类型、变量和子程序进行分组。 |
1.1.2. 模式对象存储
模式对象将数据存储在文件中。其他模式对象,如视图和序列,仅包含元数据。
KingbaseES数据库在表空间中逻辑地存储模式对象。模式和表空间之间没有对应关系,表空间可以包含来自不同模式的对象,而一个模式的对象可以存储在不同的表空间中。 KingbaseES中表或索引等对象都被记录在表空间中,并被拆分成若干个段存储。一个段对应了一个物理文件,并只存储一个关系的部分数据。段内部会被划分为若干个数据块进行数据的管理和组织。
1.1.3. 模式对象依赖关系
一些模式对象引用其他对象,从而产生模式对象依赖关系。例如,视图包含引用表或视图的视图,而PL/SQL子程序调用其他子程序。 如果对 象A的定义引用对象B,那么A是B依赖对象,B是A的被引用对象。
KingbaseES数据库提供了一种自动机制来确保依赖对象对其引用对象始终是最新的。创建依赖对象时,数据库会跟踪依赖对象以及它的引用对象之间的依赖关系。当被引用对象以可能影响依赖对象的方式更改时,数据库会标记依赖对象为无效。 例如,如果用户删除了一个表,则基于已删除表的视图都不可用。在依赖对象可用之前,必须根据引用对象的新定义重新编译无效的依赖对象。当引用无效的依赖对象时,会自动进行重新编译。
1.1.4. 模式和管理员
当创建数据库时,会创建一个管理员账号(如SYSTEM)。
管理帐户有很高的特权,仅用于授权的DBA执行如启动和停止数据库、管理内存和存储、创建和管理数据库用户等。 SYS系统模式在创建数据库时自动创建,系统模式中的表仅由数据库自行维护,绝不能被任何用户修改。系统模式存储显示管理信息的其他表和视图,以及各种KingbaseES数据库选项和工具使用的内部表和视图。不要使用系统模式来存储非管理性用户的表。
表是KingbaseES数据库中最基本的操作对象。一个表描述了一个实体对象,它记录了实体对象的重要信息。KingbaseES数据库表对象类型是关系表,关系表由简单的列组成,是最常见的表类型。关系表有以下特征:
堆组织表不以任何特定顺序存储行。CREATE TABLE语句默认创建的是堆组织表。
外部表是只读表,其元数据存储在数据库中,但它的数据存储在数据库外部。
表分为普通表和临时表。普通表定义和数据跨会话持续存在。临时表定义与普通表定义以相同的方式持续存在,但数据仅在事务或会话期间存在。临时表对于一些必须临时保存结果集的应用程序很有用,可能是因为临时结果集是通过运行多个操作产生的。
本节包含以下主题:
1.2.1. 列
表定义包括表名和列。列描述的表对应的实体属性。例如,PRODUCTS表中PRODUCTNAME列是指产品实体的产品名称属性。通常,在创建表时,给每个列定义一个列名、一个数据库类型和精度。 例如,PRODUCTNAME的数据类型为VARCHAR(40),表示此列最多只能存储40个字符的字符串数据。
1.2.1.1. 虚拟列
表可以包含一个虚拟列,与非虚拟列不同,它不会占用磁盘空间。数据库通过计算一组用户指定的表达式或函数,根据需求导出虚拟列中的值。例如,订单明细表中的金额可以是单价列和折扣列的函数值。
1.2.1.2. 隐藏列
隐藏列是用户指定的列,其值只有在通过显式指定列名时才可见。 您可以在不影响 现有应用程序的情况下向表中添加一个隐藏列,并在必要时使列可见。例如,下面的示例创建具有隐藏列的ORDERS表,然后使隐藏列可见:
CREATE TABLE ORDERS (ORDERID INT,PRICE NUMERIC(10,2) INVISIBLE); ALTER TABLE ORDERS MODIFY (PRICE VISIBLE);
1.2.2. 行
行是表中记录对应列信息的集合。例如,订单表中的一行描述了订单实体对象的属性:订单编号、客户编号、订购日期等。创建表后,就可以使用SQL插入、查询、删除和更新行。
1.2.3. 示例:CREATE TABLE和ALTER TABLE语句
示例1:创建表
使用CREATE TABLE语句创建订单表和客户表,并且为每个列指定列名和数据类型。
CREATE TABLE ORDERS ( ORDERID INT4 NOT NULL, CUSTOMERID CHARACTER(5) NOT NULL, EMPLOYEEID INT4 NOT NULL, ORDERDATE DATE , REQUIREDDATE DATE, SHIPPEDDATE DATE, SHIPVIA INT4, FREIGHT NUMERIC(10,2) DEFAULT 0, SHIPNAME VARCHAR(40), SHIPADDRESS VARCHAR(60) , SHIPCITY VARCHAR(15), SHIPREGION VARCHAR(15), SHIPPOSTALCODE VARCHAR(10), SHIPCOUNTRY VARCHAR(15) ); CREATE TABLE CUSTOMERS ( CUSTOMERID VARCHAR(10) CONSTRAINT PK_CUSTOMERS PRIMARY KEY, COMPANYNAME VARCHAR(30) NOT NULL, CITY VARCHAR(20) );
示例2:修改表
使用ALTER TABLE语句,将完整性约束添加到订单表中。 完整性约束强制执行约束规则,防止无效数据进入表中。
ALTER TABLE ORDERS ADD CONSTRAINT PK_ORDERS PRIMARY KEY(ORDERID); ALTER TABLE ORDERS ADD CONSTRAINT FK_ORDERS_CUSTOMERS FOREIGN KEY(CUSTOMERID) REFERENCES CUSTOMERS(CUSTOMERID);
示例3:查询表
查询了订单表的部分数据,列举了表部分列的10行记录。
ORDERID |CUSTOMERID |EMPLOYEEID |ORDERDATE |REQUIREDDATE |SHIPPEDDATE |SHIPREGION | --------|-----------|-----------|-----------|-------------|------------|--------------| 10248 |VINET |5 |1996-07-04 |1996-08-01 |1996-07-16 | | 10249 |TOMSP |6 |1996-07-05 |1996-08-16 |1996-07-10 | | 10250 |HANAR |4 |1996-07-08 |1996-08-05 |1996-07-12 |RJ | 10251 |VICTE |3 |1996-07-08 |1996-08-05 |1996-07-15 | | 10252 |SUPRD |4 |1996-07-09 |1996-08-06 |1996-07-11 | | 10253 |HANAR |3 |1996-07-10 |1996-07-24 |1996-07-16 |RJ | 10254 |CHOPS |5 |1996-07-11 |1996-08-08 |1996-07-23 | | 10255 |RICSU |9 |1996-07-12 |1996-08-09 |1996-07-15 | | 10256 |WELLI |3 |1996-07-15 |1996-08-12 |1996-07-17 |SP | 10257 |HILAA |4 |1996-07-16 |1996-08-13 |1996-07-22 |Táchira |
前面的示例输出说明了表、列和行的以下一些重要特性:
表中一行描述了一个员工的属性:订单编号、客户编号、订购日期等。 例如,输出中的第一行显示了编号为10248的订单记录。
列描述订单的属性。示例中,ORDERID列是主键,每个订单都由订单ID唯一标识。不同订单都一定是不同的订单编号。
非主键列可以有相同值。例中,订单10250和10252的员工号相同,都为4。
外键列是指引用同一表或不同表中的主键或唯一键。在本例中,CUSTOMERID中的值对应于客户表的CUSTOMERID列。
字段是行与列的交集。它只能有一个值。例如,订单10249的订购日期为1996-07-05。
字段可以没有值。在这种情况下,字段为空值。例如,订单号10248记录的SHIPREGION列的值为NULL。除非在此列上定义了NOT NULL或主键完整性约束,否则允许列空值,如果定义了该约束,就不允许空值插入任何行。
1.2.4. 数据类型
每个列都有一个数据类型,它有特定的存储格式、约束和有效的值范围。数据类型将固定的属性与值关联在一起。这些属性导致KingbaseES数据库将以不同的方式处理不同数据类型的值。 例如,您可以将NUMBER数据类型的值相乘,但不能将RAW数据类型的值相乘。
创建表时,必须为其每个列指定数据类型。 随后插入列中的每个值都采用已定义的列数据类型。KingbaseES数据库提供了多种内置数据类型。 最常用的数据类型分为以下几类: 字符数据类型、数值数据类型和日期时间数据类型。其他重要内置数据类别包括RAW、大对象类型和集合。 PL/SQL有常量和变量的数据类型,包括BOOLEAN、引用类型、复合类型(records)和用户自定义的类型。 部分数据类型支持按指定模板进行格式化。
1.2.4.1. 字符数据类型
字符数据类型将数字、字母或文字数据存储在字符串中。 最常见的字符数据类型是VARCHAR2,这是存储字符数据最有效的类型。字符值对应的字符编码,通常称为字符集。数据库字符集是在数据库创建时建立的。KingbaseES字符集有GBK、UTF-8、EUC_CN等。
字符数据类型的长度定义以字符数或字节数来限制大小。将字符串按字节来处理称为字节语义,将字符串按字符来处理称为字符语义。KingbaseES数据库中可以通过参数 CHAR_DEFAULT_TYPE设置字符串类型的长度单位为CHAR或BYTE,默认为字符。
VARCHAR2是可变长度的字符数据类型。字符串是一个固定的数据值,例如,‘人大金仓‘,‘KES‘,和‘123’都是字符串,11是数字字符串。字符串被封装在单个引号中,以便数据库将它们的模式对象名称区分开来。 当您使用VARCHAR2列创建表时,您可以指定字符串最大长度。KingbaseES数据库将列中的每个值存储为可变长度字段,除非它的值超过最大长度限制. 例如VARCHAR2(25 char)的数据类型,这意味着存储在列中的值最多有25个字符。如果在VARCHAR2(25 char)列值输入10个字符,则实际列只存储10个字符,而不是25个。 使用VARCHAR2可以减少空间消耗。
与VARCHAR2相反,CHAR是存储固定长度的字符串。当您创建带有CHAR字符类型列的表时,需要固定字符串长度,CHAR字符串长度默认为1个字符。数据库使用空格将值填充到指定的长度。
KingbaseES数据库中,NCHAR、NVARCHAR2与CHAR、VARCHAR2的含义相同。
1.2.4.2. 数值数据类型
KingbaseES数据库数字类型存储定点数和浮点数、零和无穷大。 一些数字类型还存储作为操作未定义结果的值,称为“非数字”或NaN。
KingbaseES数据库以可变长度格式存储数字数据。 每个值都以科学计数法存储,其中一个字节用于存储指数。数据库最多使用20个字节来存储尾数,尾数是浮点数的一部分,包含其有效数字。
1.2.4.2.1. 数字类型
NUMBER数据类型存储定点数和浮点数。在KingbaseES数据库可以存储几乎任意数量级的数字。这些数据能在不同操作系统中运行的KingbaseES数据库之间进行移植。所以对于必须存储数字数据的大多数情况,都建议使用NUMBER数据类型。
您可以使用NUMBER(p,s)指定一个定点数,其中p和s指的是以下含义:
精度
精度指定总位数。如果没有指定精度,则列存储的值与应用程序提供的值完全一致,而不进行任何舍入。
标度
标度指定从小数点到最低有效数字的位数。正数精度将小数点右边的数字计数到最低有效位(包括最低有效位)。负数精度将小数点左边的数字计数到最低有效位(不包括最低有效位)。 如果指定一个没有指定标度,如NUMBER(6)中所示,则标度为0。
1.2.4.2.2. 浮点数类型
KingbaseES数据库专门为浮点数提供数字数据类型有:FLOAT和DOUBLE。这些类型支持NUMBER数据类型提供的所有基本功能。但是NUMBER类型使用十进制精度计算,BFLOAT和DOUBLE使用二进制精度计算,这使得算术计算更快并且降低了存储需求。
FLOAT和DOUBLE是近似数值数据类型。它们存储小数值的近似数,而不是精确数。例如,值0.1不能用FLOAT和DOUBLE精确表示。它们经常用于科学计算。这两种类型的区别类似于Java和XMLSchema中的数据类型FLOAT和DOUBLE。
1.2.4.3. 日期时间数据类型
日期时间数据类型为DATE、TIMESTAMP和DATETIME。KingbaseES数据库为时间戳提供全面的时区支持。
1.2.4.3.1. DATE数据类型
DATE类型存储日期和时间。虽然日期时间可以用字符或数字数据类型表示,但DATE具有特殊的关联属性。日期存储在固定长度字段中,对应于世纪、年、月、日、小时、分钟和秒。 数据库根据指定的格式显示日期,格式模型是描述字符串中日期时间格式的字符文字。KingbaseES数据库以24小时格式存储时间(HH24:MI:SS),如果没有输入时间部分,那么默认情况下,DATE字段中的时间为00:00:00。
1.2.4.3.2. TIMESTAMP数据类型
TIMESTAMP和DATATIME数据类型是DATE数据类型的扩展。除了存储在DATE数据类型中的信息外,TIMESTAMP还存储分秒。 TIMESTAMP数据类型用于存储精确的时间值。 TIMESTAMP WITH TIME ZONE和TIMESTAMP WITH LOCAL TIME ZONE 包含时区信息。当用户选择时区数据时,该值将被调整到用户会话指定的时区。这种数据类型对于需要使用跨地理区域的日期信息的业务程序很有用。
1.2.4.4. 格式化模型
格式化模型是描述存储在字符串中的日期时间或数字数据格式的字符文字。格式化模型不会改变数据库内部值的含义。当您将字符串转换为日期或数字时,格式模型将确定数据库如何显示字符串。在SQL中,您可以使用格式化模型作为TO_CHAR和TO_DATE函数的参数来格式化要从数据库返回的值或格式化要存储在数据库中的值。
下面的示例使用TO_CHAR函数查询,将FREIGHT列转换为数字格式模型“¥fm9990.90”:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, ORDERDATE DATE, FREIGHT NUMBER(6,2) ); INSERT INTO ORDERS VALUES(10248,'1996-06-05',32.38),(10249,'1996-07-15',11.61),(10250,'1996-07-24',65.83); SELECT ORDERID, TO_CHAR(FREIGHT, '¥fm9990.90') AS FREIGHT FROM ORDERS; ORDERID |FREIGHT | --------|--------| 10248 |¥32.38 | 10249 |¥11.61 | 10250 |¥65.83 |
下面的示例使用TO_DATE函数查询,将字符串类型格式化为“yyyy-mm-dd hh24:mi:ss”:
SELECT TO_DATE('2000/01/02 10:08:18.822447', 'yyyy-mm-dd hh24:mi:ss'); TO_DATE | ----------------------| 2000-01-02 10:08:18 |
1.2.5. 完整性约束
完整性约束是一个命名规则,用于限制表中一个或多个列的值。数据完整性规则防止无效的数据进入表中。此外,当存在某些依赖项时,约束可以阻止删除表的操作。
如果启用了约束,则数据库会在输入或更新数据时对其进行数据检查。KingbaseES数据库不允许输入不符合约束的数据。 如果禁用了约束,则KingbaseES数据库允许不符合约束的数据进入数据库。例如,在示例7-1-1 CREATE TABLE语句中,ORDERID列指定为主键约束。 约束子句标识约束的列和条件。主键约束确保指定的列不包含空 值且唯一。如果尝试插入没有ORDERID的订单记录就会报错。您可以在创建表时或创建表完成之后创建和修改约束。 如果需要,也可以暂时禁用约束。 数据库中约束存储在数据字典中。
1.2.6. 表储存
1.2.6.1. 堆组织表
默认情况下,表为堆组织表,这意味着数据库将行存储在最合适的位置,而不是按照用户指定的顺序。 因此,堆组织表是行的无序集合。当用户添加行时,数据库将这些行储存在数据段中的第一个可用空间中。 不保证按插入顺序检索行。表中所有行的列顺序相同。 数据库通常按照CREATE TABLE语句中列出的顺序存储列,但不能完全保证按此顺序。如果将新列添加到表中,则新列将成为存储的最后一列。
一个表可以有一个虚拟列,与普通列不同,它不会占用磁盘空间。数据库可通过计算一组用户指定的表达式或函数来根据需求导出虚拟列中的值。您可以给虚拟列建索引,收集它们的统计信息,并创建完整性约束。因此,虚拟列很像非虚拟列。
1.2.6.2. 行存储
KingbaseES中管理数据的最小单元叫做数据块,或者称为页面。一个数据块是最小的IO单元。数据库中一个数据块的大小通常是8KB
数据库将数据按行存储在数据块中,单个数据行不允许跨数据块存储,对于长度大于数据块用于存储数据的空间的1/4时会尝试压缩数据行中的数据或将数据行拆分成多个物理行存储在另外的表中。这种存储方式称为TOAST,存储这类数据的表称为TOAST表。
1.2.6.3. 空值存储
空值null在列中没有值。 null表示缺少、未知或不适用的数据。在KingbaseES数据库中,元祖头上会标记一行中是否有null值。如果一行中没有null值,将不占用任何物理存储;如果有,元祖头上将使用一个最小分配单位是字节的位图来标识值为null的列,每个列占用一位。例如一行包含10列的数据,如果其中包含null值,存储上将额外分配2个字节的位图来标记null值。
1.2.7. 表压缩
数据库使用表压缩来减少表所需的存储量。KingbaseES数据库超过四分之一页面的行,默认启动压缩。压缩可以节省磁盘空间,减少数据库缓冲区缓存中的内存使用,在某些情况下加快查询 执行速度。表压缩对应用程序是透明的。
临时表用于存放只存在于事务或会话期间的数据。临时表中的数据对会话是私有的,每个会话只能看到和修改自己的数据。
您可以创建全局(GLOBAL)临时表或本地(LOCAL)临时表。 下表列举了它们之间的区别:
全局 | 本地 | |
---|---|---|
定义 | CREATE GLOBAL TEMPORARY TABLE | CREATE LOCAL TEMPORARY TABLE |
存储 | 表定义是持久的,表数据是临时的 | 表定义和表数据都是临时的,会在会话退出后被删除 |
所属模式 | 创建在用户指定模式下 | 在临时模式下,用户不可以指定 |
约束限制 | 不支持外键引约用束 | 支持外键引用约束 |
ON COMMIT类型 | 事务级(DELETE ROWS)和会话级(PRESERVE ROWS) | 事务级(DELETE ROWS)会话级(PRESERVE ROWS)和事务结束后删除(DROP) |
1.3.1. 临时表的用途
临时表对于必须临时缓存结果集的应用程序很有用。例如,每个员工创建本地临时表,插入本季度的所有订单销售记录,进行统计分析,计算分析后再数据填入普通表中。在会话操作期间,临时表的数据是私有的,各个会话之间数据彼此不影响。表中的数据都可以在事务结束之后删除,或者连接退出之后删除。数据都存在在临时表空间中,临时表都不记录日志。
1.3.2. 临时表的创建
创建临时表使用CREATE TEMPORARY/TEMP TABLE语句。将 GLOBAL 或 LOCAL 写在 TEMPORARY 或 TEMP 的前面,指定全局临时表或本地临时表。创建临时表时若不指定GLOBAL或LOCAL,则默认值为LOCAL。
临时表在一个事务块结束时的行为由 ON COMMIT 控制。三种选项是:
PRESERVE ROWS
在事务结束时不采取特殊的动作,是本地临时表采用的默认行为。
DELETE ROWS
在每一个事务块结束时将删除临时表中的所有行。实质上,在每一次提交时会完成一次自动的。在分区表上使用时,不会将其级联到其分区。这是全局临时表的默认行为。
DROP
在当前事务块结束时将删除临时表。在分区表上使用时,此操作将删除其分区,在具有继承子级的表上使用时,将删除依赖子级。仅本地临时表支持在当前事务块结束时将删除临时表。全局临时表不支持ON COMMIT DROP 选项。
临时表可以创建索引,这些索引也是临时的。临时表上的索引使用temp_tablespaces参数指定位置。
外部表访问外部数据源中的数据,就好像这些数据在数据库中的表中一样。数据源可以是文件类型或者是其他数据库。您可以使用SQL和PL/SQL程序查询外部表。
1.4.1. 外部表的用途
外部表用于当应用程序必须访问数据库外部数据的业务环境。例如,在数据仓库的环境中,执行ETL任务时使用外部表可以方便流程设计和运行。KingBaseES支持文件外部表和数据库外部表。您可以创建一个文件外部表,将文本文件复制到外部表定义中指定的位置,然后使用SQL查询文本文件中的数据。您也可以创建一个外部服务器,数据库外部表通过外部服务器访问其他数据库的数据。
1.4.2. 外部表的创建
创建外部表使用CREATE FOREIGN TABLE语句。外部表定义描述列类型,定义SERVER是文件或者外部数据库服务器。使用文件外部表则需要在SERVER定义中说明文件的位置和格式等,外部数据库服务器则需要说明服务器的连接信息。在数据库内部,创建外部表会在数据字典中创建元数据。与普通表不同,外部表不用管数据是如何外部存储的,但外部表定义描述了外部表层如何向数据库提供数据。如果指定了 PARTITION OF 子句,则该表被创建为具有指定边界的 parent_table 的分区。例如,下面的示例创建外部表films
,通过服务器myserver
访问它:
create extension kingbase_fdw; CREATE SERVER myserver FOREIGN DATA WRAPPER kingbase_fdw OPTIONS (host 'foo', dbname 'foodb', port '54321'); CREATE FOREIGN TABLE films ( code char(5) NOT NULL, title varchar(40) NOT NULL, did integer NOT NULL, date_prod date, kind varchar(10), len interval hour to minute ) SERVER myserver;
索引是模式对象,可以加速对数据行的访问。
本章包括以下几节:
索引是一种与表相关联的数据结构,是为了提高数据检索的性能而建立的。KingbaseES索引为表数据提供快速存取路径。索引适用于一范围的行查询或指定行的查询。索引可建立在一个表的一列或多列上。一旦建立,将由 KingbaseES 数据库自动维护和使用,对用户是完全透明的。系统的优化器会根据统计信息确定是否使用索引来提高系统性能。索引逻辑和物理上都独立于与其相关联的表数据。因此,您可以删除或创建索引,但不会对索引表产生影响。
本节包含以下主题:
2.1.1. 索引的优缺点
使用索引不需要改变任何SQL语句的写法。索引是对单行数据的快速访问路径,它只影响执行的速度。给定一个已索引的数据值,索引将直接指向该数据行的位置。 当一个索引存在于表的一个或多个列上时,数据库在某些情况下可以从表行中检索一组随机分布的行。索引是减少磁盘 I/O 的手段之一。如果一个表没有索引,数据库必须执行全表扫描来查找值。例如,如果没有索引,查询ORDERS 表ORDERID为10523的位置,数据库需要搜索每个表块中的每一行来找到该值。当数据量增加时,这种方式性能会很慢。
索引的缺点如下:
手动创建索引通常需要对数据模型、应用程序和数据分布有深入的了解。
随着数据的变化,您必须重新考虑以前关于索引的决策。索引可能不再有用,或者可能需要新的索引。
索引占用磁盘空间。
当索引数据上发生DML时,数据库必须更新索引,这会造成性能开销。
通常,在下列情况下可以考虑在某列上创建索引:
要索引的列经常被查询,并只返回表中的行的总数的一小部分。
在索引的列或列集上存在引用完整性约束。索引可以避免当你更新父表主键、合并父表、从父表删除行时可能引起的全表锁定。
要在表上设置唯一键约束,并且您想手动指定索引和所有索引选项。
2.1.2. 键和列
键是一个列集或表达式,您可以在它上面创建索引。虽然这两个术语通常互换使用,但索引和键还是不同的。索引是存储在数据库中的一种结构,用户使用SQL语句来管理它们,而键严格来讲只是一个逻辑概念。 例如,下面的语句在ORDERS表的EMPLOYEEID列上创建索引,EMPLOYEEID列是索引键。索引被命名为INDEX_EMPLOYEEID:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4, ORDERDATE DATE ); CREATE INDEX INDEX_EMPLOYEEID ON ORDERS(EMPLOYEEID);
2.1.3. 组合索引
组合索引,也称为连接索引,是在表中的多个列上的索引。组合索引中的列应该以在检索数据的查询中效率最高的顺序出现,但在表定义中的顺序不需要与和组合索引列顺序一致。 若 WHERE 子句引用了组合索引中的所有列或前导列,组合索引可以加快SELECT 语句的数据检索速度。所以,在组合索引定义中所使用的列顺序很重要。一般最常被访问的列放在前面。
例如,假定应用程序经常查询ORDERS表中的ORDERID、CUSTOMERID和订购时间列的数据。相对于表的总行数来说,CUSTOMERID不同值的基数很大。可以按以下的列顺序创建索引:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4, ORDERDATE DATE ); CREATE INDEX INDEX_ORDERS ON ORDERS (CUSTOMERID,ORDERID,ORDERDATE);
如果查询这个三列,或者包含CUSTOMERID列都会使用此索引。不访问 CUSTOMERID 列的查询,将不会使用索引。
当满足以下任一条件时,同一张表上可以存在多个列序相同的索引:
索引的类型不同。
例如,您可以在相同的列上创建位图和B树索引。
索引使用不同的分区方案。
例如,您可以创建本地分区的索引和全局分区的索引。
索引具有不同的唯一性属性。
例如,您可以在同一列集上创建惟一索引和非惟一索引。
如果同一列集中的索引在类型或分区模式上没有差异,那么这些索引必须使用不同的列排列。例如,下面的 SQL 语句指定的排列都是有效的:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4, ORDERDATE DATE ); CREATE INDEX INDEX1_ORDERS ON ORDERS (CUSTOMERID,ORDERDATE); CREATE INDEX INDEX2_ORDERS ON ORDERS (ORDERDATE,CUSTOMERID);
2.1.4. 唯一索引和非唯一索引
索引分为唯一索引或者非唯一索引。唯一索引保证在表的键列或键列集上没有重复值的行。例如,ORDERS表中所有行的ORDERID都不同。非唯一索引允许在索引的列或列集中有重复的值。例如,ORDERS表的CUSTOMERID列中可能包含多个相同值。
2.1.5. 索引类型
KingbaseES数据库提供了几种索引类型,来增强数据检索性能。B树索引是标准索引类型,它适用于主键索引和过滤性强的索引。在组合索引中使用时,B树索引可以按多个索引列以排序方式检索数据。
B树索引的子类型如下表所示:
B-Tree索引子类 | 描述 |
---|---|
降序索引 | 这种类型的索引将存储在一个特定的列或多列中的数据按降序排序。 |
组合索引 | 这种类型的索引将数据按降序存储在一个或多个特定的列上。 |
唯一索引 | 这种类型的索引来强制列值的唯一性,或者是多个列组合值的唯一性。 |
非B树索引的子类型如下表所示:
类型 | 描述 |
---|---|
HASH索引 | HASH索引理想情况下仅需一次检索即可定位数据,但Hash引索只能处理简单等值比较。 |
GIN索引 | GIN索引是倒排索引,它适合于包含多个组合值的查询,例如数组。 |
GINST索引 | GINST索引适用于多维数据类型集和合数据类型,可以用来做位置搜索。 |
BRIN索引 | 块范围索引,存储表的连续数据库区间以及对应的数据取值范围。 |
2.1.6. 索引的维护
创建索引之后,数据库将自动维护和使用索引。
索引自动将数据变化更新到其底层表。包括添加、更新和删除行等操作,都不需要用户维护索引。 即使插入行,索引数据的检索性能也几乎保持不变。但是,表上的索引过多会降低DML性能,因为DML操作会导致数据库更新索引。
2.1.7. 索引存储
KingbaseES数据库将索引数据存储在索引表空间的索引段中。 索引的表空间,是所有者的默认表空间,或在 CREATE INDEX 语句中明确指定的表空间。为了便于管理,您可以在一个独立于表所在表空间的单独表空间中存储索引。例如,您可以选择把包含索引的表空间放在低容量的高速存储上加速访问。
平衡树,简称B树,是最常见的数据库索引类型。一个B树索引是被划分为多个范围的已排序的值列表。通过索引键与一行或行范围关联起来,B树可以对多种类型的查询提供优秀的检索性能,包括精确匹配和范围搜索等。
2.2.1. B树索引中不同类型的块
B-树索引中可能出现的几种不同类型的块: 1. meta : 索引中的第一个块,存储索引的结构版本、root位置等元信息 2. root : 逻辑上没有父节点的节点,在多层索引中存储导航信息,在单层索引中存储实际索引值 3. 非root的中间块 : 多层索引中存储导航信息的非root块 4. leaf : 存储实际索引值的块 索引的树形结构由2、3、4类型的块组成,每种块中也存储和兄弟节点间的导航信息。
2.2.2. 索引扫描
在索引扫描中,数据库使用语句中涉及的有索引的列,通过遍历其索引来检索行。数据库扫描索引,将使用 n 个 I/O 就能找到其要查找的值,其中 n 即是 B-树索引的高度。这是数据库索引背后的基本原理。如果 SQL 语句仅需要被索引的列值,那么数据库只需直接从索引中读取值,而不用读取表数据。如果该语句同时还需要访问除索引列之外的列,那么数据库会使用索引来查找表中的行数据。通常,为检索表数据,数据库以交替方式先读取索引块,然后读取相应的表块。
2.2.2.1. 全索引扫描
在全索引扫描中,数据库顺序读取整个索引。如果在 SQL 语句中的谓词(WHERE 子句) 引用了一个索引列,或者在某些情况下未指定任何谓词,此时可能使用全索引扫描。全扫描可以消除排序,因为数据本身就是基于索引键排过序的。
例如,假设应用程序运行以下查询:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, FREIGHT NUMBER(6,2) ); CREATE INDEX INDEX_ORDERS ON ORDERS (ORDERID,CUSTOMERID,FREIGHT); INSERT INTO ORDERS VALUES(10248,'VINET',102.01),(10249,'TOMSP',90),(10250,'HANAR',209.41),(10251,'VICTE',190.99); SELECT ORDERID, CUSTOMERID, FREIGHT FROM ORDERS WHERE FREIGHT > 100 ORDER BY ORDERID,CUSTOMERID;
其中,CUSTOMERID,CUSTOMERNAME和FREIGHT是一个组合索引。KingbaseES 数据库会执行全索引扫描,按CUSTOMERID和CUSTOMERNAME排序的顺序读取索引数据,并基于FREIGHT > 100的条件进行筛选。通过这种方式,数据库只需扫描满足FREIGHT > 100的条件的数据所属的表数据页,而不用扫描那些不满足条件的表数据页,并避免了对该数据进行排序。
全索引扫描可能这样读取索引条目,如下所示:
10248,VINET,102.01,tid 10250,HANAR,209.41,tid 10251,VICTE,190.99,tid . . .
2.2.2.2. 仅索引扫描
仅索引扫描也是一种全索引扫描,区别在于数据库仅访问索引本身的数据,而无需再访问表数据。
当满足以下两个条件时,仅索引扫描将会被启用:
索引必须包含查询所需的所有列。
对于表做过ANALYZE操作。
例如,应用程序发出以下查询,仅索引扫描将会被启用:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, FREIGHT NUMBER(6,2) ); CREATE INDEX INDEX_ORDERS ON ORDERS (ORDERID,CUSTOMERID); INSERT INTO ORDERS VALUES(10248,'VINET',102.01),(10249,'TOMSP',90),(10250,'HANAR',209.41),(10251,'VICTE',190.99); SELECT ORDERID,CUSTOMERID FROM ORDERS WHERE ORDERID < 10250;
其中,CUSTOMERID和CUSTOMERNAME是一个组合索引,目标列中只有索引相关列,仅索引扫描只需读取索引条目,就可以获取所需的信息。
仅索引扫描可能这样读取索引条目,如下所示:
10248,VINET,tid 10250,HANAR,tid 10251,VICTE,tid . . .
2.2.2.3. 范围索引扫描
范围索引扫描也是全索引扫描的一种,区别在于仅针对指定范围建立索引。
通常,数据库使用范围索引扫描来访问带有范围选择谓词的数据。
例如,针对 ORDERS 表,建立一个CUSTOMERID在[100,400]这个范围的范围索引:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, FREIGHT NUMBER(6,2) ); CREATE INDEX RANGE_CUSTOMER_ID_IDX ON ORDERS(CUSTOMERID) WHERE CUSTOMERID >= 100 AND CUSTOMERID <= 400;
当用户查询CUSTOMERID在这个范围中的数据时,范围索引扫描会被使用。
explain SELECT CUSTOMERID, CUSTOMERID, FREIGHT FROM ORDERS WHERE CUSTOMERID >= 200 AND CUSTOMERID <= 300; QUERY PLAN | ---------------------------------------------------------------------------------------| Index Scan using range_customer_id_idx on orders (cost=0.15..10.17 rows=101 width=15) | Index Cond: ((customerid >= 200) AND (customerid <= 300)) |
2.2.2.4. 表达式索引扫描
表达式索引扫描也是全索引扫描的一种,区别在于仅针对表达式建立索引。
例如,针对 ORDERS 表,建立一个表达式索引:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, FREIGHT NUMBER(6,2) ); INSERT INTO ORDERS VALUES(10248,'VINET',102.01),(10249,'TOMSP',90),(10250,'HANAR',209.41),(10251,'VICTE',190.99); CREATE INDEX EXP_IDX ON ORDERS((MOD(FREIGHT,3) = 0));
当用户查询含有这个表达式谓词时,表达式/函数索引扫描可能会被使用。
set enable_seqscan = off; explain SELECT * FROM ORDERS WHERE MOD(FREIGHT,3) = 0; QUERY PLAN | -------------------------------------------------------------------------| Index Scan using exp_idx on orders (cost=0.29..514.78 rows=70 width=15) | Index Cond: ((mod(freight, 3) = 0) = true) |
2.2.3. 升序和降序索引
对于升序索引,数据库按升序排列的顺序存储数据。默认情况下,字符数据按每个字符中包含的二进制值排序,数值数据按从小到大排序,日期数据从早到晚排序。
例如,下面是一个升序索引的例子:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, ORDERDATE DATE ); CREATE INDEX INDEX_ORDERDATE ON ORDERS(ORDERDATE);
数据库对ORDERS表按ORDERDATE列进行升序排序。按 ORDERDATE列升序顺序加载索引。使用此索引,数据库检索已排序的ORDERDATE值。
通过在 CREATE INDEX 语句中指定 DESC 关键字,可以创建一个降序索引。在这种情况下,索引在指定的一列或多列上按降序顺序存储数据。降序索引的默认搜索顺序是从最高值到最低值。 当查询需要按一些列升序而另一些列降序排序时,可以使用降序索引。例如,在CUSTOMERID列和ORDERDATE列上创建一个复合索引,如下所示:
CREATE INDEX INDEX_ORDERS ON ORDERS(CUSTOMERID ASC, ORDERDATE DESC);
对ORDERS表查询,要求按CUSTOMERID按字母升序排序序而ORDERDATE以日期类型降序排序,则数据库可以使用此索引检索数据并避免额外的排序步骤,如下所示:
SELECT CUSTOMERID, ORDERDATE FROM ORDERS ORDER BY CUSTOMERID ASC,ORDERDATE DESC;
2.2.4. 索引压缩
当索引长度超过页面可存储数据空间的1/16时,数据库会透明的尝试对索引中包含的存储模式为EXTENDED和MAIN的列做压缩。
函数索引是基于函数的索引计算函数或涉及一个或多个列的表达式的值。用于生成索引的函数可以是算术表达式,或一个包含 SQL 函数、用户定义 PL/SQL 函数、包函数,或 C 调用的表达式。例如,函数可以将列值全部小写。
2.3.1. 函数索引的使用
函数索引对于在 WHERE 子句中包含函数计算的语句是有效的。仅当在查询中包含该函数时,数据库才使用基于函数的索引。当数据库处理 INSERT 和 UPDATE 语句时,它仍然必须计算函数才能完成对语句的处理。
示例1:创建函数索引
CREATE TABLE ORDER_DETAILS ( ORDERID INT4 CONSTRAINT PK_ORDER_DETAILS PRIMARY KEY, UNITPRICE NUMBER(6,2), QUANTITY INT4, DISCOUNT NUMBER(6,2) ); INSERT INTO ORDER_DETAILS VALUES(10462,4.80,1,0.0),(10281,7.30,1,0.0),(10850,2.50,4,0.15),(10420,4.80,2,0.1); CREATE INDEX INDEX_PRICE ON ORDER_DETAILS ((UNITPRICE*QUANTITY*(1-DISCOUNT)), UNITPRICE, QUANTITY, DISCOUNT);
数据库在处理以下查询时可以使用此函数索引:
SELECT ORDERID, UNITPRICE, QUANTITY, DISCOUNT,UNITPRICE*QUANTITY*(1-DISCOUNT) AS PRICE FROM ORDER_DETAILS WHERE PRICE <100 ORDER BY PRICE ; orderid|unitprice|quantity|discount|price | -------+---------+--------+--------+------+ 10462| 4.80| 1| 0.00|4.8000| 10281| 7.30| 1| 0.00|7.3000| 10850| 2.50| 4| 0.15|8.5000| 10420| 4.80| 2| 0.10|8.6400|
示例2:基于 SQL 函数 UPPER的索引
基于 SQL 函数 UPPER(column_name) 或 LOWER(column_name)定义的索引,经常用于大小写无关的数据检索。例如,CUSTOMERS表的CITY列包含混合大小写字符。可以在CUSTOMERS表上创建了如下索引:
CREATE TABLE CUSTOMERS ( CUSTOMERID VARCHAR(10)CONSTRAINT PK_CUSTOMERS PRIMARY KEY, COMPANYNAME VARCHAR(30) NOT NULL, CITY VARCHAR(20) ); INSERT INTO CUSTOMERS VALUES('RDJC','Bejing Renda Jingcang','Beijing'); INSERT INTO CUSTOMERS VALUES('BJRDJC','Bejing Renda Jingcang','BeiJing');
查询CITY列时,可以使用此索引:
SELECT CUSTOMERID,UPPER(CITY) AS CITY FROM CUSTOMERS WHERE UPPER(CITY) = 'BEIJING'; CUSTOMERID |CITY | ------------|------------| RDJC |BEIJING | BJRDJC |BEIJING |
示例3:为表中的特定行建立索引
函数索引对于只在一个表中的特定行上建立索引也是有用的。例如,TERRITORY表中的REGIONID列有4个值。可以编写一个函数,让REGIONID不为1的其它任何行都返回一个空值。创建索引如下所示:
CREATE TABLE TERRITORY ( TERRITORYID VARCHAR(10)CONSTRAINT PK_TERRITORY PRIMARY KEY, TERRITORYNAME VARCHAR(30), REGIONID INT4 NOT NULL ); CREATE INDEX INDEX_REGIONID ON TERRITORY ( CASE REGIONID WHEN '1' THEN '1' END );
2.3.2. 函数索引的优化
对于在 WHERE 子句中包含表达式的查询,优化器可以在一个基于函数的索引上使用索引范围扫描。当谓词具有高度选择性,即选择相对较少的行时,范围扫描访问路径尤其有用。示例7-2-1 中,如果索引是基于表达式 ``UNITPRICE*QUANTITY*(1-DISCOUNT)``来创建的,优化器可以使用索引范围扫描。虚拟列可用于快速访问由表达式生成的数据。例如,您可以为表达式``UNITPRICE*QUANTITY*(1-DISCOUNT)``定义虚拟列PRICE,并在该 PRICE列上创建索引。优化器通过分析在SQL语句中的表达式来执行表达式匹配,然后比较的语句表达式目录树和函数索引。
虽然表和索引是最重要和最常用的模式对象,但数据库支持许多其他类型的模式对象,其中最常见的模式对象将在本章中讨论。
本章包括以下几节:
在KingbaseES数据库中,分区能够将大表和索引分解成更小、更易于管理子分区。每个分区是一个独立的对象,具有其自己的名称和存储特征。
下面用一个例子来解释分区的概念,假定我们正在为一个大型的冰激凌公司构建数据库。该公司每天测量最高温度以及每个区域的冰激凌销售情况。销售表结构如下:
CREATE TABLE MEASUREMENT ( CITY_ID INT NOT NULL, LOGDATE DATE NOT NULL, PEAKTEMP INT, UNITSALES INT );
大部分查询只会访问上周的、上月的或者上季度的数据,因为这个表的主要用途是为管理层准备在线报告。为了减少需要被存放的旧数据量,我们决定只保留最近3年的数据。在每个月的开始我们将去除掉最早的那个月的数据。在这种情况下我们可以使用分区技术来帮助我们满足对MEASUREMENT表的所有不同需求。
从应用程序的角度来看,分区表是一个表对象,不需要修改SQL语句就可以访问该分区表。分区对许多类型的应用程序都很有用,特别是那些管理大量数据的应用程序。其优点如下:
可用性增强
其中某个分区不可用并不意味着整个表不可用。部分分区未被引用时,优化器自动从执行计划中删除未引用的分区,而查询不会受影响。
更轻松的管理模式对象
分区表具有多个分片,可以将其作为一个整体来管理,也可以单独管理各个分片。DDL 语句可以单独处理分区,而不是整个表或索引。因此,您可以分解大量占用资源的任务,如重建索引或表。例如,您可以一次只修改一个表分区。如果发生了问题,则只需重新修改该分区,而不是整个表。而且,删除一个分区可以避免执行许多 DELETE语句。
在 OLTP 系统中减少对共享资源的争用
在一些 OLTP 系统中,分区可以减少对共享资源的争用。例如,DML被分散到很多文件中,而不只是一个文件。
在数据仓库中增强的查询性能
在数据仓库中,分区可以加快带条件的范围查询的速度,优化灵活选择条件的查询。
3.1.1. 分区特征
每个表或索引的分区必须具有相同的逻辑属性,如列名称、数据类型和约束。例如,在一个表中的所有分区都共享相同的列和约束定义,并在索引中的所有分区都共享相同的索引列。但是,每个分区可以有单独的物理属性,即所属的表空间可以不同。
3.1.1.1. 分区键
分区键是一个列或列集,来确定分区表中的每一行应在的分区。每个行会被准确地分配到某个分区。例如,在MEASUREMENT表中,您可以指定 CITY_ID 列作为列表分区的键。基于此列来判断是否属于某一列表,数据库将行分配到所属分区。通过使用分区键,KingbaseES 数据库将插入、更新、和删除操作自动地分配到对应的分区。
3.1.1.2. 分区类型
KingbaseES 分区提供了几个分区策略,来控制数据库如何将数据放置到分区。基本类型有范围分区、列表分区和哈希分区等。 单一分区只使用一种数据分区方法,例如,仅使用列表分区,或仅使用范围分区。在组合分区中,表先按一种数据分区方法分区,然后每个分区使用第二种数据分区进一步分成子分区。
3.1.1.2.1. 范围分区
在范围分区中,数据库基于分区键的值范围将行映射到各个分区。范围分区是最常见的分区类型,通常与日期一起使用。
示例2:创建范围分区表
分区表MEASUREMENT_RANGE,LOGDATE列是分区键:
CREATE TABLE MEASUREMENT_RANGE ( CITY_ID INT NOT NULL, LOGDATE DATE NOT NULL, PEAKTEMP INT, UNITSALES INT ) PARTITION BY RANGE (LOGDATE) ( PARTITION Q1 VALUES LESS THAN ('2006-04-01'), PARTITION Q2 VALUES LESS THAN ('2006-07-01'), PARTITION Q3 VALUES LESS THAN ('2006-10-01'), PARTITION Q4 VALUES LESS THAN ('2007-01-01') );
数据库根据 PARTITION BY RANGE 子句中指定的范围分区列,基于LOGDATE值为每个行选择适当的分区。范围分区键值指定分区的上限,示例中使用LOGDATE列将分区按日期范围划分为四个季度。
3.1.1.2.2. 间隔分区
间隔分区是范围分区的扩展。
如果插入的数据超过现有的范围分区,那么KingbaseES数据库将自动创建指定间隔的分区。例如,MEASUREMENT表可以将每个月的数据存储在一个单独的分区中。
间隔分区使您能够避免显式地创建范围分区。您可以对每个范围分区的表使用区间分区,并对新分区使用固定的区间。在按区间进行分区时,必须指定至少一个范围分区。范围分区键值确定各个范围分区的高值。数据库会自动为超过范围的数据分区。每个区间划分的下边界是上一个区间划分或区间划分的包容上边界。
示例3:创建间隔分区表
间隔分区表MEASUREMENT_INTERVAL,按照LOGDATE日期列,每三个月为一个间隔自动创建分区。
CREATE TABLE MEASUREMENT_INTERVAL ( CITY_ID INT NOT NULL, LOGDATE DATE NOT NULL, PEAKTEMP INT, UNITSALES INT ) PARTITION BY RANGE (LOGDATE) INTERVAL ('3 MONTH'::INTERVAL) ( PARTITION P1 VALUES LESS THAN ('2006-04-01') );
数据库根据INTERVAL子句中指定的间隔规则自动分区。超过日期'2006-04-01'的数据,数据库自动为其创建新的分区。
3.1.1.2.3. 列表分区
在列表分区中,数据库使用一些具体值的列表作为每个分区的分区键。分区键由一个或多个列组成。 你可以使用列表分区来控制单个行如何映射到特定的分区。当用来区分数据集的键不方便排序时,可以通过使用列表来分组和组织相关的数据集。
示例4:创建列表分区表
列表分区表MEASUREMENT_LIST,以CITY_ID列为分区键,按照城市编号分为四个区:
CREATE TABLE MEASUREMENT_LIST ( CITY_ID INT NOT NULL, LOGDATE DATE NOT NULL, PEAKTEMP INT, UNITSALES INT ) PARTITION BY LIST (CITY_ID) ( PARTITION CITY1 VALUES ('1'), PARTITION CITY2 VALUES ('2'), PARTITION CITY3 VALUES ('3'), PARTITION CITY4 VALUES ('4') );
数据库根据 PARTITION BY LIST 子句中指定的规则,基于CITY_ID为每个行选择适当的分区。按照城市编号分为四个区,例如,CITY_ID值2的行将存储在CITY2分区中。
3.1.1.2.4. 哈希分区
在哈希分区中,基于用户所指定的将在分区键上应用的哈希算法,数据库将行映射到各个分区。通过为每个分区指定模数和余数来对表进行分区。每个分区所持有的行都满足:分区键的值除以 为其指定的模数将产生为其指定的余数。哈希分区可用于划分大表,以提高可管理性。例如,哈希分区适合用于在更新频繁的 OLTP 系统中,一个表被分为几个部分,每个分区都可以被更新,而不像单表更新容易产生锁冲突。
示例5:创建哈希分区表
哈希分区表MEASUREMENT_HASH,以CITY_ID列为HASH分区键:
CREATE TABLE MEASUREMENT_HASH ( CITY_ID INT NOT NULL, LOGDATE DATE NOT NULL, PEAKTEMP INT, UNITSALES INT ) PARTITION BY HASH(CITY_ID);
数据库根据PARTITION BY HASH子句中指定HASH键分布数据。示例中,这些分区的名称将由系统自动生成的。不能指定某数据行被放置在哪一个分区,数据库应用哈希算法,确定数据的分区分配。
3.1.1.2.5. 组合分区
在组合分区中,用一种数据分区方法对表进行分区,然后使用另一种数据分区方法将每个分区进一步细分为子分区。因此,组合分区结合了基本的数据分区类型。
组合分区提供了以下几个优点:
根据 SQL 语句,对一个或两个维度进行分区修改可提高性能。
查询可以在任意维度上使用全连接或部分分区连接。
可以对单个表执行并行备份和恢复。
分区的数量大于单级分区,有利于并行执行。
如果应用通过分区修改或分区连接可以提高性能,那么您可以实现一个滚动窗口来支持历史数据,并且仍然在另一个维度上进行分区。
您可以根据分区键的标识来以不同的方式存储数据。
范围、列表和哈希分区都可以作为组合分区表的子分区策略。数据库将组合分区表中的每个子分区存储为单独的文件。因此,子分区物理存储属性可能与表的属性不同,也可能与子分区所属的分区不同。
3.1.2. 分区表
分区表包含一个或多个分区,它们可以单独进行管理,并且可以独立于其他分区进行操作。
表分为分区表和非分区表两张类型。即使分区表只包含一个分区,此表也是与一个未分区表不同的,不能将分区加入到未分区表中。
3.1.2.1. 表分区文件
分区表由一个或多个表文件组成。数据库为每一个子分区表分配文件,将每个表分区的数据存储在其自己的文件中。每个表分区文件包含表数据的一部分。
3.1.3. 分区索引
与分区表类似,分区索引被分解成更小、更易于管理的索引分区。在KingbaseES数据库中,分区表上创建的索引可分为全局分区索引和本地分区索引。 全局分区索引在分区表的所有分区数据基础上创建的索引。本地分区索引是指在每个表分区上单独创建的索引,是一种局部索引,也是一种分区索引,某一个索引分区只能索引到一个表分区。与分区表类似,分区索引提高了可管理性、可用性、性能、和可扩展性。
3.1.3.1. 本地分区索引
在本地分区索引中,索引基于表上相同的列来分区,与表分区具有相同分区数目和相同的分区边界。
每个索引分区仅与底层表的一个分区相关联,所以一个索引分区中的所有键都只引用存储在某个单一表分区中的行。通过这种方式,数据库会自动同步索引分区及其关联的表分区,使每个表和索引相对保持独立。
本地分区索引在数据仓库环境中很常见,提供了以下优点:
使分区中的数据不可用的操作只会影响当前分区,这有助于提高可用性。
简化了分区维护。当移动一个表分区,或某个分区的数据老化时,只须重建或维持相关联的索引分区。而在全局索引中所有索引分区必须被全部重建或维护。
如果分区发生时间点恢复,则可以将本地分区索引单独恢复,不需要重建整个索引。
示例6:创建本地分区索引
使用示例7-3-4的MEASUREMENT_RANGE表,在CITY_ID列上创建一个本地分区索引:
CREATE INDEX INDEX_LOCAL_MEASUREMENT_RANGE ON MEASUREMENT_RANGE(CITY_ID) LOCAL;
在示例1中,MEASUREMENT_RANGE表有四个分区,创建的本地分区索引也有四个,系统自动命名分区索引为Q1_CITY_ID_IDX、Q2_CITY_ID_IDX、Q3_CITY_ID_IDX和Q4_CITY_ID_IDX。每个索引分区与其对应的表分区相关联。例如,分区索引Q1_CITY_ID_IDX对表分区Q1中的行建立索引,而分区索引Q2_CITY_ID_IDX对表分区Q2中的行建立索引。
当新的分区创建时,对应的本地分区索引也会自动被创建。相同地,删除一个表分区时,对应的本地分区索引会被自动删除。分区和本地分区索引之间对应的关系由数据库自行维护,不能手动干预。
3.1.3.1.1. 本地前缀索引和局部非前缀索引
分区索引可以带前缀,也可以不带前缀。
索引子类型定义如下:
本地前缀索引
在这种情况下,分区键处于索引定义的前导部分。例如,示例1中的范围分区表MEASUREMENT_RANGE,LOGDATE列是范围分区列。如果以LOGDATE作为其索引列列表中的第一列,则索引本地局部前缀索引。
本地非前缀索引
在这种情况下,分区键不是索引列列表的前导部分,甚至根本不必在该列表中。例如,在示例4创建的本地分区索引中,由于分区键LOGDATE不属于前导列,所以该索引是局部非前缀索引。
这两种类型的索引都可以充分利用分区消除,此时,优化器将不考虑无关的分区,以加快数据访问速度。查询是否可以消除分区取决于查询谓词,使用本地前缀索引的查询允许索引分区消除,而使用一个本地非前缀索引的查询可能不行。
3.1.3.1.2. 本地分区索引存储
与表分区类似,局部索引分区被存储在其自己的文件中。每个文件包含整个索引数据的一部分。因此,由四个分区组成的局部索引,不是存储在一个单一索引文件中,而是在四个单独的文件中。
3.1.3.2. 全局分区索引
全局分区索引指向任意或所有的表分区,而在一个本地分区索引中,索引分区与分区表之间却存在一对一的配对关系。KingbaseES数据库支持全局分区索引的创建、修改、删除、维护,vacuum/analyze 以及在查询中支持使用全局分区索引,支持的索引类型为B树索引。不支持在非根节点分区表上创建全局索引,不支持分区和主表列顺序不一致的情形。
当KingbaseES数据库创建全局分区索引时,首先尝试创建本地分区索引,当不满足本地索引的条件(唯一索引的索引列不包括全部分区列或者分区条件为表达式)时会创建全局索引。全局索引支持条件索引,不支持全局分区索引。全局索引不支持排他约束。启用分区表上的主键或唯一约束时,先尝试创建本地索引,不满足时则创建全局唯一索引。
示例7-3-5:创建全局分区索引
使用示例7-3-1的MEASUREMENT_RANGE表,在CITY_ID列上创建一个全局分区唯一索引:
CREATE UNIQUE INDEX INDEX_GLOBAL_MEASUREMENT_RANGE ON MEASUREMENT_RANGE(CITY_ID) GLOBAL;
示例7-3-5创建的索引,是在MEASUREMENT_RANGE表所有分区上创建的唯一索引,包括所有数据行。
视图是一个或多个表的逻辑表示形式。视图在本质上是一个数据存储的查询。
视图的数据来自它所依赖基表,基表可以是表或其他视图。在视图上执行的所有操作实际上都指向基表。所以在大多数需要使用表的地方,您也可以使用视图。
视图使您能够为不同类型的用户定制数据表示形式。视图通常用于以下场景:
通过限制对一组预定义的表行或表列的访问,提供一个额外的表安全级别
隐藏数据复杂性。例如,一个单一视图的定义可以包含一个联接,它是多个表中的相关列或行的集合。但视图隐藏了这个事实,即此信息实际上来自于几个表。查询还可能会对表的信息执行大量计算。因此,用户可以查询视图,但不需要知道如何执行联接或计算。
以一个不同于基表的角度来呈现数据。例如,可以重命名视图的列名,而不会影响基表。
隔离应用程序和对基表定义的更改。例如,如果视图的查询定义引用了一个四列表的其中三列,若添加第五列到该表,则视图的定义不会受到影响,并且使用该视图的所有应用程序也不会受到影响。
例如,只允许用户查看ORDERS表的某些列数据,可以创建一个视图如下所示:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4, ORDERDATE DATE ); CREATE VIEW V_ORDERS AS SELECT ORDERID, CUSTOMERID,EMPLOYEEID, ORDERDATE FROM ORDERS;
与所有子查询一样,定义视图的查询中不能包含FOR UPDATE子句。示例中创建的V_ORDERS视图只引用了基表中的四列。
3.2.1. 视图特征
与表不同,视图既不分配存储空间,也不包含数据。而是通过定义的一个查询,从它所引用的基表中提取或派生出数据。视图基于其他对象,除了只需要在数据字典中存储定义视图的查询,它不需要其他存储。 视图对其所引用的对象存在的依赖关系,由数据库自动处理。例如,如果您删除并重新创建了视图的基表,则数据库会确定新的基表是否仍然符合该视图的定义。
3.2.1.1. 视图的数据操作
由于视图是从表派生的,所以他们有很多相似之处。用户可以查询视图,在某些限制条件下他们也可以在视图上执行DML。在视图上执行的操作会影响到视图的基表中的数据,并遵从基表的完整性约束和触发器。
示例7-3-6: 创建视图
基于ORDERS表创建一个视图:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4, ORDERDATE DATE ); INSERT INTO ORDERS VALUES(10249,'TOMSP',1,'1996-06-05'),(10264,'FOLKO',6,'1996-07-15'),(10271,'SPLIR',4,'1996-07-24'); CREATE VIEW V_EMP_ORDERS AS SELECT ORDERID, CUSTOMERID, EMPLOYEEID, ORDERDATE FROM ORDERS WHERE EMPLOYEEID =6 WITH LOCAL CHECK OPTION;
示例7-3-6的视图查询定义只引用了员工编号为6的行。定义中有 WITH CHECK OPTION,如果对该视图发出的 INSERT 和 UPDATE 语句所生成的新行不符合该视图的选择条件,则该语句将不会成功。如果新行的EMPLOYEEID不为6,数据库则拒绝该行的INSERT或UPDATE操作
3.2.1.2. 视图的数据访问
数据库将视图定义存储在数据字典中,存的是定义视图查询的文本。 当在 SQL 语句中引用视图时,数据库将执行以下任务:
1.将SQL查询,与定义该视图的查询进行合并,KingbaseES数据库会优化合并查询,就好像您发出的查询并没有引用视图一样。因此,无论在视图定义中或在针对该视图的用户查询中是否引用了该列,数据库可以使用任何被引用基表上的索引。有时,数据库不能将视图定义与SQL查询合并。在这种情况下,数据库可能不会使用所有引用列上的索引。
2.在SQL缓存区中,解析合并后的SQL语句不存在,数据库就会SQL缓存区中解析这个引用了视图的语句。
3.执行 SQL 语句
示例7-3-7 :视图的数据访问
下面的示例演示了当查询视图时的数据访问。创建基于ORDERS表和CUSTOMERS表的连接视图:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4, ORDERDATE DATE ); INSERT INTO ORDERS VALUES(101,'RDJC',1,'1999-03-20'); CREATE TABLE CUSTOMERS ( CUSTOMERID VARCHAR(10) CONSTRAINT PK_CUSTOMERS PRIMARY KEY, COMPANYNAME VARCHAR(30) NOT NULL, CITY VARCHAR(20) ); INSERT INTO CUSTOMERS VALUES('RDJC','Bejing Renda Jingcang','BEIJING'); CREATE VIEW ORDERS_QRY AS SELECT ORDERS.ORDERID, ORDERS.CUSTOMERID, ORDERS.EMPLOYEEID, ORDERS.ORDERDATE, CUSTOMERS.COMPANYNAME, CUSTOMERS.CITY FROM CUSTOMERS JOIN ORDERS ON CUSTOMERS.CUSTOMERID = ORDERS.CUSTOMERID;
对视图ORDERS_QRY执行以下查询:
SELECT ORDERID,ORDERDATE,COMPANYNAME FROM ORDERS_QRY WHERE CUSTOMERID='RDJC';
数据库将视图和表进行连接,构建如下面的查询,然后执行此查询以检索数据:
SELECT ORDERID,ORDERDATE,ORDERS_QRY.COMPANYNAME FROM CUSTOMERS JOIN ORDERS_QRY ON CUSTOMERS.CUSTOMERID = ORDERS_QRY.CUSTOMERID AND CUSTOMERS.CUSTOMERID='RDJC';
物化视图是查询结果被提前存储或"物化"的模式对象。定义的查询 FROM 子句中可以是命名表、视图、和物化视图。
物化视图通常用作复制中的主表和数据仓库中的事实表。物化视图用于汇总、计算、复制、和分发数据。它们适用于如下各种计算环境:
在数据仓库中,可以使用物化视图来计算和存储由聚合函数(如求和或平均值)所生成的数据。汇总是一个聚合的视图,它通过预先计算联接和聚合操作,并将结果存储在一个表中,来减少查询时间。物化视图相当于汇总,您也可以使用物化视图来计算聚合联接。
物化视图包含从某个表的单一时间点的完整或部分拷贝。物化视图在分布式站点上复制数据,并将在多个站点上执行的更新同步。
在移动的计算环境中,可以使用物化视图将数据子集从中央服务器下载到移动客户端,从中央服务器定期刷新客户端,并定期将客户端更新传输回中央服务器。
3.3.1. 物化视图的特征
物化视图与索引和普通视图具有某些共同特性。
物化视图在以下几方面与索引类似:
它们包含实际数据,并且占用存储空间。
当其主表中数据更改时,他们可以被刷新。
当用于查询重写操作时,可以提高 SQL 的执行性能。
它们的存在对 SQL 应用程序和用户是透明的。
物化视图也类似于非物化视图,因为它也呈现其他的表和视图中的数据。与索引不同的是,用户可以直接使用 SELECT 语句查询物化视图。取决于所需的刷新类型,物化视图也可以用 DML 语句进行更新。
示例7-3-8:创建物化视图
创建一个基于ORDERS的物化视图,存取EMPLOYEEID为6的数据行:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4 NOT NULL, ORDERDATE DATE ); INSERT INTO ORDERS VALUES(10249,'TOMSP',1,'1996-06-05'),(10264,'FOLKO',6,'1996-07-15'),(10271,'SPLIR',4,'1996-07-24'),(10272,'RATTC',2,'1996-07-09'); CREATE MATERIALIZED VIEW MV_ORDERS AS SELECT ORDERID, CUSTOMERID, EMPLOYEEID, ORDERDATE FROM ORDERS WHERE EMPLOYEEID =6;
然后,删除物化视图MV_ORDERS的主表ORDERS,再查询MV_ORDERS。查询有结果,是因为物化视图的数据与主表中的数据是分开存储的。
DROP TABLE ORDERS; SELECT * FROM MV_ORDERS; ORDERID |CUSTOMERID |EMPLOYEEID |ORDERDATE | --------|-----------|-----------|-----------| 10264 |FOLKO |6 |1996-07-15 |
物化视图可以进行分区。您可以在分区表上定义一个物化视图,并可以在该物化视图上创建一个或多个索引。
3.3.2. 物化视图的刷新方法
在主表中的数据更改后,数据库通过刷新物化视图来对其进行维护。KingbaseES目前支持完全刷新。
完全刷新执行会完全替换一个物化视图的内容,旧的内容会被抛弃。创建物化视图时,WITH [ NO ] DATA 这个子句指定物化视图是否在创建时被填充。如果不是,该物化视图将被标记为不可 扫描并且在 REFRESH MATERIALIZED VIEW 被使用前不能被查询。一次完整的刷新可能很慢,尤其是在读取和处理大量数据的情况下。在创建物化视图之后,您可以在任何时间执行一次完整的刷新。例如,刷新示例7-3-8的物化视图MV_ORDERS:
REFRESH MATERIALIZED VIEW MV_ORDERS;
刷新时如果指定了 WITH DATA(默认值),查询将被执行以提供新的数据,并且会让物化视图将处于可扫描的状态。如果指定了WITH NO DATA,则不会生成新数据并且会让物化视图处于一种不可扫描的状态。您还可以指定CONCURRENTLY选项,对物化视图的刷新不阻塞在该物化视图上的并发选择。如果没有这个选项,一次影响很多行的刷新将使用更少的资源并且更快结束,但是可能会阻塞其他尝试从物化视图中读取的连接。这个选项在只有少量行被影响的情况下可能会更快。只有当物化视图上有至少一个 UNIQUE 索引(只用列名并且包括所有行)时,才允许这个选项。也就是说,该索引不能建立在任何表达式上或者包括 WHERE子句。当物化视图还未被填充时,这个选项不能被使用。即使带有这个选项,对于任意一个物化视图一次也只能运行一个REFRESH。
序列是一种模式对象,用户可以用它来生成有序的整数。序列生成器提供可扩展性高的和性能良好的方法,来为数字数据类型生成代理键。
3.4.1. 序列特征
序列定义包括序列的所有信息,如下列所示:
起始值、最大值和最小值
增量值
是否缓存序列号集合
是否循环
示例7-3-9:创建序列
下面的示例创建序列ORDERS_ORDERID_SEQ。当向ORDERS表中添加行时,应用程序可以使用此序列生成ORDERID。
CREATE SEQUENCE ORDERS_ORDERID_SEQ INCREMENT BY 1 MINVALUE 1 MAXVALUE 9999999 START WITH 1 NOCACHE NOCYCLE;
使用NEXTVAL函数访问下一个序列值,如下所示:
SELECT NEXTVAL('ORDERS_ORDERID_SEQ'); NEXTVAL --------- 1
对ORDERS_ORDERID_SEQ的第一次引用会返回起始值1。第二次引用返回值2,每次增量为1。
3.4.2. 序列的并发访问
同一序列可以为多个表生成编号。以这种方式,数据库可以自动生成主键,并可以跨多个行或多个表。例如,示例7-3-9创建的序列可以为ORDERS表和ORDER_DETAILS表生成主键。
序列可用于在多用户环境中生成唯一编号,不会引起磁盘 I/O 开销或事务锁定。例如,两个用户同时向ORDERS表中插入新行。通过使用序列来为ORDERID 列生成唯一编号,任何一个用户都不必等待别人输入订单号。
序列自动为每个用户生成正确的值。每个引用序列的用户都有权访问当前序列号,即在该会话中生成的最后一个序列值。用户可以发出一个语句生成一个新的序列号,或使用该会话最后生成的当前序列号。单个序列号可以跳过,如果个别序列号在某个事务中被生成使用,而最终被回滚,则它们会被跳过。
同义词是一个模式对象的别名。例如,您可以为一个表或视图、序列、PL/SQL 程序单元、用户定义的对象类型、或另一个同义词等创建同义词。因为同义词只是一个别名,因此除了要在数据字典存储其定义之外,不需要其它存储。同义词可以为数据库用户简化 SQL 语句。同义词也可以用于隐藏底层模式对象的标识和位置。如果必须重命名或移动底层对象,仅需要重新定义同义词。基于同义词的应用程序,可以减少大量因对象名改变的修改工作。
KingbaseES支持创建私有同义词和公共同义词,通过PUBLIC参数来指定。公有同义词创建在PUBLIC模式下,所有用 户查询公有同义词时候,可以不用指定模式。创建私有同义词必须指定模式,私有同义词只有其所有者对其可用性具有控制权。同义词的名称不能与同一模式中任何其他同义词同名。
示例7-3-10:创建同义词
为ORDERS表创建了一个叫做ORDERINFO的公共同义词。然后通过ORDERINFO查询表行数:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4, ORDERDATE DATE ); INSERT INTO ORDERS VALUES(10249,'TOMSP',1,'1996-06-05'),(10250,'FOLKO',6,'1996-07-15'),(10251,'SPLIR',4,'1996-07-24'); CREATE PUBLIC SYNONYM ORDERINFO FOR ORDERS; SELECT COUNT(*) FROM ORDERINFO; COUNT | ------| 3 |
同义词和引用对象之间没有强依赖关系,数据库不检查引用对象是否存在,同义词也无状态可言,一直是有效。例如,删除引用对象ORDERS表后,同义词ORDERINFO依然有效,如下所示:
SELECT * FROM SYS_SYNONYM WHERE SYNNAME='ORDERINFO'; SYNNAME |SYNNAMESPACE |SYNOWNER |REFOBJNSPNAME |REFOBJNAME | ----------|-------------|---------|--------------|-----------| ORDERINFO |2200 |10 |PUBLIC |ORDERS | DROP TABLE ORDERS; SELECT status FROM ALL_OBJECTS WHERE OBJECT_NAME= 'ORDERINFO'; STATUS -------- VALID
同义词本身不是安全可控的,过度使用公共同义词会导致应用程序之间的命名空间冲突,使数据库整合更困难。当您在一个同义词上授予对象权限时,你其实是在底层对象上授予权限。同义词在 GRANT 语句中只作为对象的别名。
本章解释了完整性约束如何执行与数据库关联的业务规则,并防止无效信息进入表。
本章包括以下几节:
保持数据的数据完整性十分重要,它由数据库管理员或应用程序开发人员来确定,以遵从业务规则。 业务规则会指定一些条件和关系,它们要么必须始终为真,要么必须始终为假。
4.1.1. 保证数据完整性的方式
在设计数据库应用程序时,开发人员有多种方式用于保证存储在数据库中的数据完整性。 这些方式包括如下:
通过数据库触发器存储过程, 强制实施业务规则
使用存储过程完全控制数据访问
在数据库应用程序的代码中执行业务规则
使用数据库完整性约束,它们是定义在列级或对象级上的,用来限制数据库中的值的规则
4.1.2. 完整性约束的优势
完整性约束是一个模式对象, 它使用 SQL 来创建和删除。相对于其他强制数据完整性的方法,使用完整性约束的优点如下:
容易声明
在您定义或更改表时, 使用 SQL 语句定义完整性约束, 无需任何额外的编程。 SQL语句易于编写,也易于排除编程错误。
集中化
完整性约束定义在表上,并存储在数据字典中。因此,由所有应用程序输入的数据都必须遵守相同的完整性约束。如果约束规则在表级发生了更改, 应用程序也不需要变更。甚至在数据库检查SQL语句之前, 应用程序就可以利用数据字典中的元数据信息及时告知用户已修改的约束规则。
灵活性
当加载大量数据时, 您可以暂时禁用完整性约束, 以避免性能开销。当数据加载完成后, 您可以重新启用完整性约束。
KingbaseES数据库可以在表级或列级应用完整性约束。作为列或属性定义的一部分而指定的约束, 称为行内规范约束。 作为表定义的一部分而指定的约束称为行外规范约束。有好几种类型的完整性约束定义中都使用键这个术语。键是某种类型的完整性约束的定义中包含的列或列集。键描述关系数据库中的表与列之间的关系。键中的单个值称为键值。下表描述了约束的类型,除 NOT NULL 必须指定为行内规范,其它每一个都可以指定为行内或行外规范。KingbaseES完整性约束的类型如下表所示:
约束类型 | 描述 |
---|---|
NOT NULL | 非空约束,是否允许在指定的列插入或更新包含空值的行。 |
Unique key | 唯一约束,在相同的列、或多个列的组合中,不允许多个行具有相同的值,但允许一些值为空。 |
Primary key | 主键约束,合并NOT NULL约束和唯一性约束。 在相同的列、或多个列的组合中,不允许多个行具有相同的值,也不允许为空。 |
Foreign key | 外键约束,指定一个列作为外键,在外和键主键或唯一键之间建立关系,也称为引用键。 |
Check | 检查约束,要求某个数据库值服从指定的条件。 |
4.2.1. NOT NULL 约束
NOT NULL 约束要求表中的列不包含空值。 默认情况下,一个表中的所有列都允许空值。NOT NULL 约束主要用于不能缺少值的列。
例如,ORDERS表在EMPLOYEEID有非空约束。试图插入一个没有EMPLOYEEID的订单号会生成一个错误:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4 NOT NULL, ORDERDATE DATE ); INSERT INTO ORDERS(ORDERID, CUSTOMERID)VALUES(1,1); ERROR: null value in column "EMPLOYEEID" violates not-null constraint Detail: Failing row contains (1, 1, null)
只有在表不包含任何行或指定默认值的情况下,才能给列添加NOT NULL约束
4.2.2. Unique 约束
Unique 约束要求在一个列或列集中的每个值是唯一的。 在一个表中, 不允许多个行在有唯一约束的列或列集上具有重复值。 唯一约束适合于不允许存在有重复值的列。唯一约束与主键约束不同,通常它只要求值唯一,但允许一些值为空。
例如,在供应商表SUPPLIERS中,存在唯一键COMPANYNAME,如下所示:
CREATE TABLE SUPPLIERS ( SUPPLIERID INT4 NOT NULL , COMPANYNAME VARCHAR(40 char), ADDRESS VARCHAR(60 char), CONSTRAINT PK_SUPPLIERS PRIMARY KEY (SUPPLIERID), CONSTRAINT UNIQUE_COMPANYNAME UNIQUE(COMPANYNAME) );
如以上示例所示,UNIQUE_COMPANYNAME约束可以确保每个公司名称都不同,如果插入两个值相同的公司名称,则会报错:
SELECT * FROM SUPPLIERS WHERE COMPANYNAME='Bejing Renda Jingcang'; SUPPLIERID | COMPANYNAME | ADDRESS ------------+-----------------------+--------- 10 | Bejing Renda Jingcang | Beijing (1 row) INSERT INTO PUBLIC.SUPPLIERS(SUPPLIERID, COMPANYNAME,ADDRESS) VALUES(11, 'Bejing Renda Jingcang','Beijing'); ERROR: duplicate key value violates unique constraint "UNIQUE_COMPANYNAME" Detail: Key (COMPANYNAME)=(Bejing Renda Jingcang) already exists.
除非也指定了 NOT NULL 约束,否则空值也始终满足唯一键约束。因此,典型的情况是在列上同时具有唯一键约束和非空约束。这种组合强制用户输入的是唯一值,并消除新行数据与现有行数据发生冲突的可能性。
4.2.3. Primary Key 约束
Primary Key 约束中的列或列集,其值能唯一的标识行。每个表只能有一个主键,确保不存在任何重复的非空行。 主键可以是自然键或代理键。自然键是由表中的现有属性组成的一个有特殊含义的标识符。例如,员工表中的身份证号码就可以作为一个自然键。代理键是一个系统生成的递增标识符,以确保值的唯一性。通常,由一个序列生成代理键。
Primary Key约束可以看成是 UNIQUE 和 NOT NULL 的组合,KingbaseES 数据库的主键约束规则如下:
任何两行在指定的列或列集上都不具有重复值。
主键列不允许空值。
例如,每个订单ID编号可以作为一个主键。每个订单只有一行记录。ORDERS表中ORDERID是主键,添加一行已经存在的订单ID号记录,或者添加一行没有订单ID号的记录都会报错,如下所示:
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4 NOT NULL, ORDERDATE DATE ); INSERT INTO ORDERS(ORDERID, CUSTOMERID, EMPLOYEEID) VALUES(10269, 'WHITC', 5); ERROR: duplicate key value violates unique constraint "PK_ORDERS" DETAIL: Key (ORDERID)=(10269) already exists. INSERT INTO ORDERS(ORDERID, CUSTOMERID, EMPLOYEEID) VALUES(null, 'WHITC', 5); ERROR: null value in column "ORDERID" violates not-null constraint DETAIL: Failing row contains (null, WHITC, 5).
数据库使用索引来强制主键约束。通常,在某列上创建的主键约束会隐含创建一个唯一索引和一个非空约束。 默认情况下,隐式创建的索引名称即是主键约束的名称,您也可以为索引指定一个用户定义的名称。您可以通过 CREATE TABLE 或 ALTER TABLE 语句中的USING TABLESPACE子句,来为索引指定存储选项。
4.2.4. Foreign Key 约束
只要两个表包含一个或多个公共列,则数据库可以通过一个外键约束来强制建立两个表之间的关系。外键约束要求定义约束的列中的每个值,必须与被引用表中的指定列中的值相匹配,这种约束规则维持了两个关联表之间的引用完整性。外键可以定义在多个列上。但是,组合外键必须引用具有相同数量和相同数据类型列的复合主键或复合唯一键。 外键的值要么匹配被引用主键或唯一键的值,要么为空。 如果一个组合外键的任何列为空,则该键的非空部分不需要匹配父项中的任何列。
例如,ORDERS表的FK_ORDERS_CUSTOMERS外键,引用CUSTOMERS表的CUSTOMERID列。外键约束就保证ORDERS表中CUSTOMERID列的每个值必须与CUSTOMERS表的CUSTOMERID列中的某个值相匹配。所以,在ORDERS表中就不会存在无效的CUSTOMERID值。
CREATE TABLE ORDERS ( ORDERID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4, ORDERDATE DATE ); CREATE TABLE CUSTOMERS ( CUSTOMERID VARCHAR(10) CONSTRAINT PK_CUSTOMERS PRIMARY KEY, COMPANYNAME VARCHAR(30) NOT NULL, CITY VARCHAR(20) ); ALTER TABLE ORDERS ADD CONSTRAINT FK_ORDERS_CUSTOMERS FOREIGN KEY(CUSTOMERID) REFERENCES CUSTOMERS(CUSTOMERID) ON DELETE CASCADE ON UPDATE CASCADE;
4.2.4.1. 自引用完整性约束
自引用完整性约束是一个引用同一个表中的父键为外键的约束,即引用列和被引用列都在同一张表中。例如,EMPLOYEES表可以添加一列部门经理编号MANAGERID,并引用表中的EMPLOYEEID,这样就可以保证不存在无效的MANAGERID值。
4.2.4.2. 空值和外键
KingbaseES数据库允许外键的值可以匹配被引用主键或唯一键值,或者为空。例如,用户可以向ORDERS表插入CUSTOMERID为空的数据行。 如果一个组合外键的任何列为空,则该键的非空部分不需要匹配父项中的任何列。例如,添加一个组合外键约束,ODERS表的SHIPVIA列和SHIPNAME列引用SHIPPERS表的SHIPPERID列和COMPANYNAME列,则允许 SHIPNAME列的值是null。
4.2.4.3. 父键的删除和更新
删除或修改更新会影响外键和父键之间的关系。在父键被修改时,参照完整性约束可以指定在子表中的相关行上,执行以下某种操作之一:
限制删除或更新操作(RESTRICT CASCADE OR RESTRICT CASCADE)
RESTRICT表示阻止删除或更新一个被引用的行,NO ACTION(DELETE NO ACTION OR UPDATE NO ACTION)表示约束被检查时如果有任何引用行存在,则会抛出一个错误,这是默认动作。这 两种选择的本质不同在于 NO ACTION 允许检查被推迟到事务的最后,而 RESTRICT 则不会。例如,ORDERS表的FK_ORDERS_CUSTOMERS外键,引用CUSTOMERS表的CUSTOMERID列,如果删除CUSTOMERS表被引用列所在的行会报错违反约束。
级联删除或更新(DELETE CASCADE OR UPDATE CASCADE)
级联删除或更新是当包含被引用键值的行被删除或更新时,子表中的所有的外键依赖值所在行也会被删除或更新。例如,删除CUSTOMERS表中的某行记录, 会导致订单表中所有属于这个客户的订单记录行也被删除。
对删除或者更新置空(DELETE SET NULL OR UPDATE SET NULL)
删除或者更新置空是当包含被引用键值的行被删除或者更新时,子表中的所有的外键依赖值被全部置空。 例如,删除CUSTOMERS表中的某行,会导致订单表中所有属于这个客户的订单记录行中的CUSTOMERID列被置为空。
4.2.4.4. 索引和外键
一个外键所引用的列必须是一个主键或者被唯一约束所限制。这意味着被引用列总是拥有一个索引(位于主键或唯一约束之下的索引),因此在其上进行的一个引用行是否匹配的检查将会很高效。由于从被引用表中 DELETE或者UPDATE一行时,一个被引用列将要求对引用表进行扫描以得到匹配旧值的行,在引用列上建立合适的索引也会提高检索速度。但由于这种做法并不是必须的,而且创建索引也有很多种选择,所以在KingbaseES数据库中外键约束的定义并不会自动在引用列上创建索引。
4.2.5. Check 约束
检查约束是最普通的约束类型,它允许指定一个特定列中的值必须要满足一个布尔表达式。检查约束在其约束表达式值为真或空值时被满足,如果 DML 语句导致约束条件的计算结果为假, 则 SQL 语句将被回滚。检查约束的主要好处是, 具备强制执行非常具体的完整性规则的能力。
例如,在ORDERS表中增加一个检查约束,运费FREIGHT列的值不能大于1000,如下所示:
ALTER TABLE ORDERS ADD CONSTRAINT CHECK_FREIGHT CHECK (FREIGHT < 1000);
现在尝试插入运费大于1000的值,报错如下所示:
INSERT INTO ORDERS(ORDERID, CUSTOMERID, EMPLOYEEID, FREIGHT) VALUES(999,2,2,1009); ERROR: new row for relation "ORDERS" violates check constraint "CHECK_FREIGHT" DETAIL: Failing row contains (999, 2, 2, 1009.00).
可以在列定义中包括多个检查性约束。例如,除了上面示例所示运费FREIGHT列值不能大于1000, 还可以再添加一个检查约束使其值必须大于5。 如果在某列上存在多个检查约束,则他们必须被合理设计,保证业务不会发生冲突。但数据库不会检验这些检查条件的顺序和是否相互排斥。
作为约束定义的一部分,您可以指定数据库应如何及何时强制执行该约束条件,从而确定约束状态。
4.3.1. 数据检查
KingbaseES数据库可以指定将某个约束应用于现有数据还是应用于新数据。如果约束是启用的,则当输入或更新数据时, 数据库会检查新的数据。不符合该约束的数据不能输入到数据库。例如,对ORDERS表的CUSTOMERID列启用 NOT NULL 约束,可以保证新插入每一个订单记录都有一个CUSTOMERID。如果约束是禁用的,则表可能存在违反约束的行。
您可以将约束设置为以下两种验证模式之一:
验证(VALIDATE)
现有数据必须符合约束。例如,ORDERS表的CUSTOMERID列启用 NOT NULL 约束,并将其设置为VALIDATE检查现有的每一行的CUSTOMERID是否都非空。
不验证(NOVALIDATE)。
当表的约束处于启用非校验状态时,表中已有的数据可以不符合约束,但后面新增的数据必须进行校验,这对于数据仓库来说特别有用。 因为基本上数据仓库都是使用增量更新,在停用约束后如果采用启用校验约束状态,则需要对大量数据进行校验,影响性能。 例如,可以在ORDERS表的ORDERDATE列上创建一个NOT NULL约束,并将该约束设置为NOVALIDATE。
数据检查的验证模式如下表所示:
新数据 | 已存在的数据 | 说明 |
---|---|---|
ENABLE | VALIDATE | 现有的和新数据都必须遵守约束。如果试图在一个已填充的表上应用一个新的约束,而现有的行违反该约束,则会导致一个错误。 |
ENABLE | NOVALIDATE | 数据库会检查该约束,但不需要对所有为行。真。因此,已存在的行可以违反该约束, 但新的或修改后的行必须遵守该约束。这种模式通常用于包含已验证完整性的现有数据的数据仓库。 |
DISABLE | VALIDATE | 数据库禁用该约束,并防止修改受约束的列。 |
DISABLE | NOVALIDATE | 不检查约束,也不需要为真。 |
4.3.2. 检查约束的有效性
约束可以处于不可延迟(默认值)或可延迟两种状态之一。该状态确定数据库何时检查约束的有效性
4.3.2.1. 不可延迟约束(NOT DEFERRABLE)
NOT DEFERRABLE是默认值,不可延迟的约束将在每一次命令后立刻被检查,如果违反了约束,则该语句被回滚。例如,ORDERS表的ORDERDATE列上创建一个不可延迟的NOT NULL 约束。如果用户试图插入一行没有订购日期的记录,则数据库会立即回滚该语句,因为违反了 NOT NULL 约束。
4.3.2.2. 可延迟约束(DEFERRABLE)
可延迟约束允许事务使用 SET CONSTRAINT 子句将约束检查推迟到事务结束时。如果你对数据库中做了可能违反约束的更改,则此设置让您有效地禁用该约束,直到完成所有更改。
您可以设置数据库检查可延迟约束时的默认时间。您可以指定下列属性之一:
INITIALLY IMMEDIATE
数据库在每个语句执行后,立即检查约束。如果违反了约束,则数据库回滚该语句。
INITIALLY DEFERRED
数据库在事务结束时检查约束,如果违反了约束,则数据库回滚该事务。
例如,在ORDERS表的ORDERDATE列上的创建可延迟的 PRIMARY KEY 约束,并被设置为 INITIALLY DEFERRED。主键值有重复时,当用户尝试提交时,数据库才检查约束并回滚所有的语句。
示例如下:
--创建表、可延迟约束 CREATE TABLE ORDERS ( ORDERID INT4 , CUSTOMERID VARCHAR(10) NOT NULL, EMPLOYEEID INT4, ORDERDATE DATE ); ALTER TABLE ORDERS ADD CONSTRAINT PK_ORDERS PRIMARY KEY(ORDERID) DEFERRABLE INITIALLY DEFERRED; --设置事务约束延迟 \set AUTOCOMMIT off START TRANSACTION; SET CONSTRAINTS ALL DEFERRED; --验证 INSERT INTO ORDERS(ORDERID,CUSTOMERID,ORDERDATE) VALUES(100,'KINGBASE','2000-01-01'); INSERT INTO ORDERS(ORDERID,CUSTOMERID,ORDERDATE) VALUES(100,'KINGBASE','2000-03-01'); --事务提交时才会检查约束,提示违反约束限制的错误 COMMIT; 错误:重复键违反唯一约束
4.3.3. 约束检查的示例
下面的例子可以帮助说明数据库何时执行检查约束。使用EMPLOYEES表分析,存在自引用外键约束,部门经理编号MANAGERID引用表中的员工编号EMPLOYEEID。
CREATE TABLE EMPLOYEES ( EMPLOYEEID INT4 CONSTRAINT PK_ORDERS PRIMARY KEY, MANAGERID INT4, TERRITORYID INT4 ); ALTER TABLE EMPLOYEES ADD CONSTRAINT FK_EMPLOYEES FOREIGN KEY(MANAGERID) REFERENCES EMPLOYEES(EMPLOYEEID) ON DELETE CASCADE ON UPDATE CASCADE; INSERT INTO EMPLOYEES VALUES(1,1,1); INSERT INTO EMPLOYEES VALUES(10,1,1);
例如,当父键值不存在时,往外键列中插入值。考虑EMPLOYEES表中插入第一行的情况。 由于当前不存在任何行,那么如果MANAGERID列中的值不能引用表EMPLOYEEID列中的任何现有值,输入新行可能存在的情况如下:
如果在MANAGERID列上没有定义NOT NULL约束,可以为第一行中的列输入一个空值。因为在外键中允许使用空值,此行可以被插入到表中。
如果员工是部门经理,插入的EMPLOYEEID列和MANAGERID列的值相同。这种情况是允许的,数据库是在完全运行该语句之后,执行其约束检查的。若要允许输入在父键和外键中具有相同值的行,数据库必须首先运行该语句,然后再确定表中是否有EMPLOYEEID列对应新行的MANAGERID列.
一个多行的 INSERT 语句,如一个具有嵌套 SELECT 语句的 INSERT 语句,可以插入相互引用的多个行。约束检查被推迟到整个语句执行完成。首先,所有行先被插入,然后对所有行进行违反约束检查。
在语句进行解析之前,其默认值作为INSERT语句的一部分被包括在内。因此,默认列值都要遵从所有完整性约束检查。 例如:同时更新外键和父键的值。假设需要对员工编号重新设置。对员工编号加100,您可以执行下面的 SQL 语句来更新数据:
UPDATE EMPLOYEES SET EMPLOYEEID= EMPLOYEEID +100,MANAGERID=MANAGERID+100;
虽然已定义了一个约束,以验证引用值匹配,但前面的语句仍然是合法的,因为数据库在该语句完成后检查约束。
本节中的示例只说明了在 INSERT 和 UPDATE 语句执行过程中的约束检查机制。实际上,数据库在所有类型的 DML 语句中都使用相同的机制。
每个KingbaseES数据库的只读引用表和视图的中心集合统称为数据字典。 动态性能视图是在数据库打开和使用时不断更新的特殊视图。
本章包括以下几节:
KingbaseES数据库的一个重要部分是它的数据字典,提供有关数据库管理元数据的表和视图。
数据字典包含如下信息:
在数据库中每个模式对象的定义,包括列的默认值和完整性约束信息
分配给模式对象的空间量及当前已使用量
KingbaseES数据库用户的名称、 授予用户的权限和角色、和与用户相关的审计信息
就像存储其他数据一样,数据库将数据字典数据存储在表中,用户可以使用 SQL 来查询其中的数据。数据字典包含基表和视图。
这些对象的定义如下:
基表
它们存储有关数据库的信息。只应该由数据库写入和读取这些表。用户很少直接访问基础表,因为他们已被规范化,且大多数数据存储为一种特定的格式。
视图
它们通过使用联接和 WHERE 子句来简化信息,将基础表的数据解码成有用的信息(如用户或表名等) 。这些视图包含数据字典中的所有对象的名称和描述。 一些视图可以被所有数据库用户访问, 而其他一些则仅供管理员访问。
5.1.1. 数据字典的内容
数据字典包含系统视图和一些兼容oracle的视图。一些视图可以被所有数据库用户访问, 而其他一些则仅供管理员访问。
KingbaseES数据库创建后,会生成几个默认的模式。information_schema信息模式和sys_catalog提供主要的系统视图,包含当前数据库里定义的用户,模式对象和存储结构信息,以及所有的内置数据类型、函数和操作符等。
KingbaseES数据库sys模式下还提供一些兼容oracle的视图,分为以下几类:
具有 'DBA_' 前缀的视图
具有 'DBA_' 前缀的视图显示在整个数据库中的所有相关信息。
具有 'ALL_' 前缀的视图
具有 'ALL_' 前缀的视图,是站在用户角度,从整体上看待数据库。
具有 'USER_' 前缀的视图
具有 'USER_' 前缀的视图指的是用户在数据库中的私有环境,包括用户所创建的模式对象的元数据,对该用户的授权等等
DUAL 表
DUAL 是数据字典中的一个很小的表, 数据库和用户编写的程序可以引用它, 以保证一个已知的结果。
5.1.2. 数据字典的存储
数据字典基表是在数据库初始化时创建的第一批对象。所有数据库的数据字典表和视图都存储在sys_global表空间中。当数据库打开时,sys_global表空间始终处于联机状态 , 数据字典总是可用的。
5.1.3. 数据字典的使用
在数据库运行期间,数据库读取数据字典,以确定存在模式对象,且用户对它们具有适当的访问权限。 KingbaseES 数据库也会不断地更新数据字典, 以反映对数据库结构、审计、授权、 和数据等所做的更改。数据字典由系统用户自行维护,其他用户不能修改系统表或系统视图。
5.1.3.1. 数据字典缓存
很多数据字典信息存在于数据字典缓存中,因为数据库需要这些信息,来不断验证用户的访问权限,并验证模式对象的状态。解析信息通常保存在缓存中。
5.1.3.2. 其他程序与数据字典
版本迭代时,新版本的KingbaseES数据库产品可能会引用现有的视图,并创建额外的数据字典表或视图。
在KingbaseES数据库的操作过程中,它会维护一组记录当前数据库活动的虚拟表。性能视图是动态的,因为他们在数据库处于打开状态和在使用过程中会不断更新。
系统和会话参数
内存使用和分配
CPU和IO优化点分析
文件状态
会话和事务进度
SQL运行
统计和度量
动态性能视图有以下主要用途:
数据库管理器使用这些视图,来获取有关数据库的信息
管理员可以使用这些视图,用于性能监控和调试。
5.2.1. 动态性能视图的内容
动态性能视图不能被数据库管理员更改或删除,用户只能访问动态性能视图。动态性能视图都存放在sys_catalog模式下,动态性能视图基于系统动态性能表创建。例如, sys_stat_activity视图包含数据库所有实时的会话信息。
5.2.2. 动态性能视图的存储
动态性能视图基于从数据库内存结构生成的虚拟表。这些视图不是存储在数据库中的常规表。由于数据是动态更新的,所以不能保证视图的读一致性。因为动态性能视图并不是真正的表,其数据取决于数据库和实例的状态。
5.3.1. 数据库对象的元数据
DBMS_METADATA包是Kingbase数据库中提供的从数据库字典中以DDL语句形式检索元数据的一系列函数。您还可以通过KingbaseES图形化数据库对象管理工具,直接选择查看每个对象的DDL语句。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。