目录
3.1. SQL处理 ¶
3.1.1. SQL语句的处理 ¶
3.1.2. 事务操作的分组 ¶
3.1.3. 事务的只读 ¶
3.1.4. 表的显式锁定 ¶
3.1.5. 事务控制表锁定 ¶
3.1.6. 事务的并发控制 ¶
3.1.7. DDL语句的阻塞 ¶
3.2. SQL数据类型 ¶
3.2.1. 数据类型的选择 ¶
3.2.2. 字符数据 ¶
3.2.3. 数值数据 ¶
3.2.4. 时间日期数据 ¶
3.2.5. 特殊数据 ¶
3.2.6. 伪列 ¶
3.3. 正则表达式的应用 ¶
3.3.1. 正则表达式概述 ¶
3.3.2. KingbaseES对正则表达式的支持 ¶
3.3.3. 正则表达式标准 ¶
3.3.4. 正则表达式的运算符 ¶
3.4. 索引的使用 ¶
3.4.1. 使用索引的基本准则 ¶
3.4.2. 管理索引 ¶
3.4.3. 函数索引的使用 ¶
3.5. 数据完整性的维护 ¶
3.5.1. 使用约束限制业务规则 ¶
3.5.2. 索引约束 ¶
3.5.3. 非空约束 ¶
3.5.4. 默认值 ¶
3.5.5. 主键约束 ¶
3.5.6. 唯一约束 ¶
3.5.7. 外键约束 ¶
3.5.8. check约束 ¶
3.5.9. 启用和禁用约束 ¶
3.5.10. 修改约束 ¶
3.5.11. 约束的重命名 ¶
3.5.12. 删除约束 ¶
3.5.13. 约束信息的数据字典 ¶
3.1.1.1. SQL语句的处理过程 ¶
应用程序通过接口向数据库发送SQL语句,执行的过程分为以下步骤:
1.打开或创建游标
程序接口调用游标来获取SQL语句。大多数应用程序隐式创建游标,预编译程序可以隐式或者显式创建游标。
2.分析语句
应用程序进程将SQL语句发送到KingbaseES数据库,数据库分析器对SQL语句进行分析,检查语义、语法等。如果解析过程中有错误,会返回报错信息给应用程序。
3.确定SQL语句的类型
4.定义变量
如果是查询语句,需定义变量,定义变量接收查询返回的结果。需要定义变量的数据类型和长度。
5.绑定参数
数据库需要SQL语句中变量的信息,通过绑定的参数获取。应用程序需要指定变量的位置即内存地址,每次数据库运行SQL时,都会通过其地址获取变量的值。数据库将会执行数据类型转换,并为每个值指定数据库类型和长度。
6.执行SQL语句
数据库运行查询语句,不会锁定数据行,会进行表数据检索,最后返回结果集。如果执行的是DML语句,数据库将产生行锁直到事务完成,来确保数据完整性。
7.关闭游标
事务完成后,数据库关闭游标。
3.1.1.2. SQL缓存区 ¶
KingbaseES数据库会检测到应用程序发送相似的SQL语句。解析过的SQL信息会将缓存到SQL缓存区内,以便后续再次利用。SQL缓存区机制减少了数据库服务器的内存使用量,提高了系统吞吐量。
3.1.2.1. 关于事务操作的分组 ¶
如何对事务中的操作进行分组是使用数据库编程接口的应用开发人员最关心的问题,在决定如何分组时,需要考虑以下方面:
定义事务在逻辑单元中完成工作,并且保证数据一致。
确保所有引用表的数据在事务开始和结束期间都处于一致状态。
确保每个事务只包括SQL语句或者PL/SQL程序,这些SQL语句或者PL/SQL程序对数据的修改一致。
3.1.2.2. 提高事务性能 ¶
在设计和编写应用程序时,可以考虑以下方式来增强性能:
如果不能使用单个SQL执行完一个事务,则可以使用PL/SQL。但尽可能的少用PL/SQL。
创建编写SQL语句的标准,以便可以合理利用共享SQL缓存区。
及时收集和更新表和索引对象的统计信息,优化器将更准确地为SQL语句生成最优计划。
通过在SQL表达式中使用用户编写的PL/SQL函数,提供用户程序的工作效率和查询效率。
要确保事务级的读取一致性,可以指定事务为只读状态。只读事务中的查询只能看到事务开始之前的提交的修改,保证查询结果在事务持续时间内是一致的。只读事务查询数据时,不需要产生表锁,其他事务可以查询和更新相同的数据。只读事务的语句如下:
SET TRANSACTION READ ONLY;
KingbaseES数据库有默认的锁机制,来确保数据的并发性、完整性和读取的一致性。但您可以显式地锁定表来实现这些机制。显式锁表通常用于以下场景:
应用程序中的事务需要对资源进行独占访问,以便该事务不需要等待其他的事务操作。
应用程序需要事务读取一致性
KingbaseES数据库可以通过以下语句来实现显式锁定表:
LOCK TABLE
SELECT ... FOR UPDATE
SET TRANSACTION READ ONLY OR ISOLATION LEVEL SERIALIZABLE
3.1.4.1. 显式表级锁 ¶
LOCK TABLE 获得一个表级锁,必要时会等待任何冲突锁被释放。如果指定了 NOWAIT,LOCK TABLE 不会等待以获得想要的锁。如果它不能立刻得到表锁,该命令会被中止并且发出一个错误。一旦获取到,该锁会被 在当前事务中一直持有(没有 UNLOCK TABLE 命令,锁总是在事务结束时被释放)。 当一个视图被锁定时,出现在该视图定义查询中的所有关系也将被使用同样的锁模式递归地锁住。
KingbaseES中支持以下模式锁定表:
ACCESS SHARE
ROW SHARE
ROW EXCLUSIVE
SHARE UPDATE EXCLUSIVE
SHARE
SHARE ROW EXCLUSIVE
EXCLUSIVE
ACCESS EXCLUSIVE
3.1.4.1.1. 行共享和行独占模式锁定 ¶
ROW SHARE 和 ROW EXCLUSIVE 表锁模式提供最高程序的并发。在以下情况下,可以使用这种模式的表锁:
事务在更新表之前,必须阻止另一个事务获取该表的独占锁或者行锁
事务必须防止表被更改或删除,然后事务才能修改表数据
3.1.4.1.2. 共享模式锁定 ¶
SHARE模式的表锁定是非常严格的数据锁。在以下情况下,可以使用这种模式的表锁:
事务只需要查询表,并且在事务持续时间内需要表数据一致
暂缓更新锁定表的其他事务,直到在该表上保留锁的事务都已提交或者回滚
其他事务可能会在同一张表上获取并发锁,这也为它们提供了事务级读取一致性的选项
3.1.4.1.3. 共享行独占模式锁定 ¶
SHARE ROW EXCLUSIVE 模式,在以下情况下,可以使用这种模式的表锁:
事务既需要指定表的事务级读取一致性,也需要更新锁定的表
不需要考虑其他事务是否获取显式行锁,但这可能会使锁定事务中的语句等待并产生死锁
只存在单个事务有此操作
3.1.4.1.4. 独占模式锁定 ¶
EXCLUSIVE 模式,在以下情况下,可以使用这种模式的表锁:
事务需要对锁定表进行即时更新。当事务持有独占表锁时,其他事务无法锁定表的数据行
事务可以确保表的事务级读取一致性,直到事务 被提交或者回滚
不用担心低级别的数据并发性,会使独占表锁的事务排队等待按顺序更新表
3.1.4.2. 显式行级锁 ¶
使用SELECT FOR UPDATE语句可以获取数据行锁,锁定数据行但不修改它。SELECT FOR UPDATE语句允许用户修改一个或者多个特定行字段,确保在交互式程序中只有一个用户能够更新数据行。 SELECT FOR UPDATE语句返回的结果集中每一行都被单独锁定,如果存在冲突,将会一直等待直到另一个事务释放冲突锁。
如果通过SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 语句来实现数据库控制表锁定,那么应用程序的编程逻辑会更简单,同时控制权也会更少。SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 语句可以保留ANSI可串行化,无需改变底层的锁协议。
默认情况下,数据库允许并发运行的事务对同一行数据进行增删改。当事务更改表时,在提交更新之前,这些更新对其他并发允许的事务是不可见的。如果一个事务尝试修改其他事务已锁定的行,那它需要等待其他事务提交或者回滚后才能修改成功。 此外,一些应用程序需要可序列化的事务运行机制。可序列化事务在序列化模式下并发允许。在序列化模式下,并发事务只能串行修改数据,否则会发生错误。
3.1.6.1. 事务并发和隔离级别 ¶
由于一个事务可能包含多个 SQL 语句,SQL 语句是顺序执行的多个事务在一个数据库上并发执行,可能有如下类型的问题:
事务并发 |
定义 |
---|---|
脏读(Dirty Read) |
事务T1更新了一个数据,并且在T1提交或者回滚前,另外一个事务T2读取了那个数据。 如果T1 这个时候回滚,那么T2就读取了一个未提交的虚假的值,即“脏”数据。 |
不可重复读(Fuzzy Read) |
事务T1读取了一个数据。这个时候另外一个事务T2修改或者删除了这个数据并且提交了。 如果T1尝试再读取一次这个数据,就会发现再次读到的数据与之前不一致或不存在。 |
幻象读(Phantom Read) |
事务T1读取了若干个满足某个查询条件的数据。这个时候事务T2创建了一个数据恰好也 满足 T1 的这个查询条件。如果这个时候 T1 再次根据这个查询条件进行读取, 它会发现会多出了部分数据。 |
很显然,高并发度带来了数据的不一致,这些情况一些是用户不愿意见到,一些则是无法接受的。用户需要在高并发带来的高性能和数据的一致性之间做取舍。SQL92 标准提出了事务隔离级别,来解决上面的问题。如下表所示:
隔离级别 |
读脏数据 |
不可重复读 |
读幻象数据 |
---|---|---|---|
读未提交(Read uncommitted) |
可能 |
可能 |
可能 |
读已提交(Read committed) |
不可能 |
可能 |
可能 |
可重复的读(Repeatable read) |
不可能 |
不可能 |
可能 |
可串行化(Serializable) |
不可能 |
不可能 |
不可能 |
四个隔离级别从上到下对事务执行的并发程度进行了不同程度的限制,更加严格的限制在带来更好的数据一致性的同时,也会损失更多并发带来的高性能。
KingbaseES向用户提供以下隔离级别:
READ COMMITTED
一个语句只能看到在它开始前提交的行。这是默认值。
SERIALIZABLE
当前事务的所有语句只能看到这个事务中执行的第一个查询或者数据修改语句之前提交的行。
3.1.6.2. 设置隔离级别 ¶
使用SET TRANSACTION 命令设置当前事务的隔离级别。SET SESSION CHARACTERISTICS 设置一个会话后续事务的默认事务特性。在个体事务中可以用 SET TRANSACTION 覆盖这些默认值。
3.1.6.3. 读取和可串行化隔离级别 ¶
KingbaseES向用户提供两种隔离级别:读已提交(Read committed)和可串行化(Serializable)。这两种隔离级别都提供高度的一致性和并发性,减少数据争用。在实际的应用程序中可以利用这些机制。
读已提交 (Read committed)
READ COMMITTED 是 KingbaseES 默认的事务隔离级别。在运行在该隔离级别的事务中,查询语句只能看到该查询开始执行之前提交的数据,而不会看到任何未提交的数据或查询执行期间并发的其它事务提交的数据,但是该查询可以看到本事务中查询之前执行的数据更新。 在 READ COMMITTED 隔离级别下,对于事务 T1 中的更新和删除语句,与查询语句相同,只能看到语句开始执行时提交的数据行,但是,这些数据行可能已经被同时并发的另一个事务 B 更新,在这种情况下,事务 T1 将等待事务 T2 回滚或提交。如果事务 T2 回滚,则事务 T1 在原来的数据上继续更新,如果事务 T2 提交了,将分两种情况,1):如果事务 T2 删除了该行,则事务 T1 忽略该行,2):如果事务 T2 对该行进行了更新,则事务 T1 判断该行的新值是否还满足条件,如果满足条件,则事务 T1 在新数据行上进行更新,如果不满足条件,则忽略该行。 在 READ COMMITTED 隔离级别下,同一个事务的不同查询看到的可能是不同的数据,因此会出现不可重复读的问题。这种隔离级别对于大多数的应用已经能够满足要求,但有些应用需要提供更加严格的数据一致性。 为了避免同样的查询得到不同的结果,在 READ COMMITTED 隔离级别下,应用程序在同一事务内应该尽量避免重复的查询。
可串行化 (Serializable)
SERIALIZABLE 提供了更加严格的事务隔离级别,在该事务隔离级别下,事务并发执行,其结果与某个串行执行顺序的结果完全相同。 在运行在 SERIALIZABLE 隔离级别的事务中,查询语句只能看到该事务开始执行之前已经提交的数据,而不会看到任何未提交的数据或事务执行期间并发的其它事务提交的数据,但是该查询可以看到本事务中查询之前执行的数据更新。 在 SERIALIZABLE 隔离级别下,对于事务 T1 中更新和删除语句,与查询语句相同,只能看到事务开始执行时提交的数据行,但是,这些数据行可能已经被同时并发的另一个事务 T2 更新(事务 T2 开始的时间在事务 T1 前),在这种情况下,事务 T1 将等待事务 T2 回滚或提交。如果事务 T2 回滚,则事务 T1 在原来的数据上继续更新,如果事务 T2 提交了,则事务 T1 就会出错回滚,因为一个串行事务不能修改在该事务开始后被其它事务修改的数据。 SERIALIZABLE 隔离级别可以确保每个事务看到一个完全一致的数据,但是当出现并发更新时,后更新的事务必须回滚,只能在之前的更新提交后再重新执行。 在 SERIALIZABLE 隔离级别下,应用程序如果碰到更新语句回滚,应该重执行更新语句所在的整个事务。
当有事务对表执行DML语句时,又有新的会话发出DDL语句,这个会话会一直等待。直到所有表的DML语句都已提交或者回滚后,DDL语句才能执行成功。
在会话等待期间,并发会话可能会发出新的DML语句,如果DDL语句是非阻塞的,那么新的DML语句会立即执行。如果DDL语句是阻塞的,那么新的DML语句会在DDL语句执行完成后再执行。存在DDL超时参数来控制会话阻塞是否超时,超时阻塞将会抛出错误。
对于分区表来说,DDL针对每个分区的阻塞情况都是不一样的,每个分区都是独立的。
数据库应用程序创建和使用数据库时,为每个对象选择正确的数据类型能够提高数据的完整性、降低存储开销和提高应用的性能。
3.2.1.1. 正确的数据类型提高数据完整性 ¶
正确的数据类型可以当作一种数据约束,来提高数据完整性。例如,日期类型字段只能存储日期数据,但如果使用字符类型,则可能存在非日期类型的数据。这样通过数据类型就能防止无效的数据写入,不需要再通过应用程序格外设计条件来规范数据格式。在kingbaseES数据库中,每种数据类型字段中只允许对应格式的数据写入。
3.2.1.2. 选择合适的数据类型降低存储开销 ¶
除了选择正确的数据类型,还需要正确地指定类型的长度或者精度,例如:
创建包含n个字符串的列时,需要指定字段类型为varchar(n)
创建整数列时,使用numeric(6,0),指定标度为0
数据类型的长度和精度会影响数据的存储。如果每个列都提供其数据类型的最大长度或精度,那么数据库为其分配固定的存储空间,会产生很多空闲的存储块,增加存储开销。并且对于查询来说,扫描的数据块的代价也会变大。对单个查询的效率可能影响不大,但如果是批量查询或者并发查询效率就会低很多。 因此,不要随意将字段类型的长度或者精度设置为最大值,如果应用程序后续需要增加列的长度,可以使用SQL语句再修改列定义。
3.2.1.3. 正确的数据库类型提高性能 ¶
选择正确的数据类型可以提高查询效率,不正确的数据类型可能会导致优化生成效率低的执行计划。比如,对日期数据比较范围时,在timestamp类型字段创建索引,会比varchar类型更容易生成最优执行计划。
KingbaseES支持的字符类型如下表所示:
数据类型 |
描述 |
---|---|
char/nchar |
固定长度的字符串 |
varchar/nvarchar |
可变长度的字符串 |
text |
无限变长,存储任何长度的字符串,存储最大值为1G。 |
clob |
字符大对象,存储最大值为1G。 |
在char和varchar之间进行选择时,需要考虑以下方面:
存储开销
char为固定长度字符类型,数据库将自动填充数据值,varchar不会自动被填充。因此,varchar类型比char类型更有效的利用空间,节省存储开销。
性能
由于填充差异,对包含varchar列的表进行全表扫描读取的数据块可能比包含char列的表进行全表扫描读取的数据块少。如果您的应用程序经常对包含字符类型的大表进行全表扫描,那您可以通过选择vachar数据类型来提高查询效率。
语义
需要比较语义中的ANSI兼容性时,请使用char数据类型。当字符串尾随空格会影响比较结果时,请使用varchar数据类型。
KingbaseES中存储数值数据的类型有numeric、integer、double precision等。
numeric存储0 以及绝对值为[1.0 x 10-130, 1.0 x 10126)的正、负定点数。integer存储有符号整数,取值范围 -2147483648~ +2147483647。double precision存储双精度浮点数(8 字节)。
KingbaseES支持使用date和timestamp类型来存储日期时间数据,表示世纪、年、月、日、小时、分钟、秒以及时区等格式数据。支持的类型见下表所示:
数据类型 |
描述 |
---|---|
date |
存储日期和时间值 |
timestamp |
存储日期和时间值 |
timestamp with time zone |
存储带时区的日期时间值 |
timestamp with local time zone |
存储本地时区的日期时间值 |
interval year to month |
存储两个timestamp值的间隔,描述若干年若干月间隔 |
interval day to second |
存储两个timestamp值的间隔,描述若干天若干秒间隔 |
3.2.4.1. 查询当前时间 ¶
查询当前日期时间最简单的方法,是查询SYSDATE。还可以通过to_char函数指定格式化模型。例如:
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss');
3.2.4.2. 插入和查询日期 ¶
插入或者查询日期数据时,可以使用to_char或者to_date格式模型函数来格式化日期数据。例如,下面的例子演示了如何格式化插入和查询日期数据
--建表 create table t_date (c1 date); --使用to_date插入日期数据 INSERT INTO t_date VALUES(TO_DATE('05 Dec 2000', 'DD Mon YYYY')); --不指定格式查询日期数据 select c1 from t_date; c1 | ----------------------| 2000-12-05 00:00:00.0 | --使用to_char指定格式查询日期数据 select to_char(c1,'yyyy-mon-dd') from t_date; to_char | ------------| 2000-dec-05 |
3.2.4.3. 插入和查询时间 ¶
插入或者查询时间数据时,可以使用to_char或者to_date格式模型函数来格式化时间数据。对于date列:
默认时间为00:00:00
如果只插入日期部分的数据,则时间部分的数据默认是00:00:00
必须插入日期数据
如果不插入日期部分的数据,数据库会报错无效数据。
例如,下面的例子演示了如何格式化插入和查询时间数据
--建表 create table t_time (c1 date); --使用to_date插入日期数据 INSERT INTO t_TIME VALUES(TO_DATE('05 Dec 2000 8:08:08 p.m', 'DD Mon YYYY HH:MI P.M')); --不指定格式查询日期数据 select c1 from t_time; c1 | ----------------------| 2000-12-05 08:08:00.0 | --使用to_char指定格式查询日期数据 select to_char(c1,'DD Mon YYYY HH:MI P.M') from t_time; to_char | ----------------------| 05 Dec 2000 08:08 P.M |
3.2.4.4. 日期时间数据的运算 ¶
日期时间值的运算结果有KingbaseES数据库SQL语言手册中的规则确定。KingbaseES提供很多日期时间函数,例如,函数DATEDIFF 计算两个 DATE 之间的天数。
3.2.4.5. 日期时间数据的转换函数 ¶
KingabaseES支持的时间转换函数如下表,函数的具体用法参见 KingbaseES SQL语言参考手册 。
函数 |
输入 |
输出 |
---|---|---|
to_char |
date、timestamp、timestamp with time zone、timestamp with local time zone、 interval year to month、interval day to second |
varchar |
to_date |
char、varchar |
date |
to_timestamp |
char、varchar |
timestamp |
3.2.4.6. 导出和导入日期时间数据 ¶
使用导出和导入备份功能时,无需担心timestamp with time zone等带时区日期时间数据值的偏移,因为数据库以标准化格式存储这些值。当导入导出date或者timestamp类型时,需要调整源端和目的端数据库之间的时区差异,因为数据库不存储它们的时区。
3.2.5.1. 空间数据 ¶
空间数据一般应用于支持位置的应用程序、地理信息系统和地理成像的应用程序,有关空间数据信息可参考 KingbaseGIS使用手册 。
3.2.5.2. 大对象类型 ¶
对象数据类型主要存储多媒体或者文件类型的数据,KingbaseES支持的大对象类型如下表所示:
数据类型 |
描述 |
---|---|
blob |
二进制大对象,以二进制格式存储数据。通常用于多媒体数据,例如图像、视频和音频。 |
clob |
字符串大对象,存储字符串数据,通常用于存储大型字符串或者文档 |
text |
字符串大对象,存储字符串数据 |
3.2.5.3. json数据 ¶
KingbaseES为存储JSON数据提供了json 和jsonb 两种类型。并且提供gin索引,可以对json数据进行操作。对于json数据的处理,可以使用json类型来约束,或者使用is_json函数来约束数据格式。更多json函数内容可参考 KingbaseES SQL语言参考手册 。
3.2.5.4. xml数据 ¶
KingbaseES提供xml数据类型来存储xml格式的文件数据。还可以使用XMLPARSE等相关的系统函数来处理数据,更多xml函数内容可参考 KingbaseES SQL语言参考手册 。
访问数据表行最快的一种方式是使用伪列。KingbaseES支持ROWNUM 伪列,ROWNUM返回一个数值指示该条记录在 KingbaseES 从表取得或连接产生时的顺序。 伪列的行为与普通的列十分相似,但伪列的值并非与表存储在一起。用户可以使用SELECT来选择伪列,但不能对伪列进行任何的修改。伪列与不带参数的函数十分相似,但没有参数的函数其函数返回结果对于结果 集中每条记录一般都是固定不变的,而伪列对于同一记录集在不同查询条件下所返回的结果可能是不相等的。
正则表达式是一个字符序列,它是定义一个串集合(一个正则集)的缩写。正则表达式使用操作符和字符指定搜索模式。 如果一个串是正则表达式描述的正则集中的一员时,我们就说这个串匹配该正则表达式。和 LIKE一样,模式字符准确地匹配串字符,除非 在正则表达式语言里有特殊字符—不过正则表达式用的特殊字符和 LIKE 用的不同。和 LIKE 模式不一样的是,正则表达式允许匹配串里的任何位置,除非该正则表达式显式地挂接在串的开头或者结尾。
KingbaseES支持数据库应用程序使用正则表达式来实现复杂的模式匹配。使用正则式进行模式匹配有以下优点:
通过在数据库建立集中模式匹配逻辑,可以避免应用程序对SQL结果集进行复杂的字符串处理
通过在服务器端使用正则表达式强制实施约束,可以避免在多个客户端上重复验证逻辑。
KingbaseES支持具有模式匹配条件的正则表达式和一些函数结合使用,模式匹配在给定的字符串中搜索指定的模式,支持的函数如下表所示:
函数 |
描述 |
---|---|
regexp_replace |
用目标字符串去替换模式匹配的源字符串,返回替换后的字符串 |
regexp_count |
返回返回目标字符串在源字符串中出现的次数 |
regexp_instr |
返回模式匹配字符串的起始位置,或者结束位置。 |
regexp_substr |
在字符串中搜索正则表达式字符串,返回搜索到的子字符串 |
regexp _matches |
返回模式匹配正则表达式得到的所有子串 |
函数的具体用法可参见 KingbaseES SQL语言参考手册 。
下表列举了一些模式匹配选项和例子:
模式匹配选项 |
描述 |
示例 |
---|---|---|
i |
大小写不敏感的匹配 |
函数返回的结果是{KingbaseES}: select regexp_match('KingbaseES','K.*es','i'); |
c |
大小写敏感的匹配 (默认) |
函数返回的结果是null: select regexp_match('KingbaseES','K.*es','c'); |
n |
新行敏感的匹配 |
函数返回的结果是null: select regexp_match('KingbaseES','K.*ES','n'); |
s |
非新行敏感的匹配(默认) |
函数返回的结果是{'Kingbase + ES'} : select regexp_match('KingbaseES','K.*ES','n'); |
x |
扩展语法(可忽略空格) |
函数返回的结果是KingbaseES: select regexp_match('KingbaseES','K.* ES','x'); |
KingbaseES 提供了三种独立的实现模式匹配的方法:SQL LIKE 操作符、更近一些的 SIMILAR TO 操作符(SQL:1999 里添加进来的)和 POSIX-风格的正则表达式。
正则表达式(RE)在POSIX 1003.2中定义,它有两种形式:扩展正则表达式ERE和基本正则表达式BRE。KingbaseES支持两种形式,并且还实现了一些POSIX标准中没有但是在类似Perl或者Tcl这样的语言中得到广泛应用的一些扩展。使用了那些 非 POSIX 扩展的 RE 叫高级 RE。
3.3.4.1. KingbaseES对POSIX运算符的支持 ¶
KingbaseES遵循POSIX标准中定义的运算符和匹配语义,支持的运算符如下表所示:
运算符 |
匹配 |
---|---|
|
一个由原子的 0 次或更多次匹配组成的序列 |
|
一个由原子的 1 次或更多次匹配组成的序列 |
|
一个由原子的 0 次或 1 次匹配组成的序列 |
|
一个由原子的 正好 m 次匹配组成的序列 |
|
一个由原子的 m 次或更多次匹配组成的序列 |
|
一个由原子的从 m 次到 n 次(包括)匹配组成的序 列; m 不能超过 n |
|
|
|
|
|
|
|
|
|
|
|
|
|
串开头的匹配 |
|
串末尾的匹配 |
|
在匹配 re 的子串开始的任何点的 positivelookahead 匹配(只对 ARE) |
|
在匹配 re 的子串开始的任何点的 negativelookahead 匹配(只对 ARE) |
|
只要有 一个点上有一个子串匹配 re 端, positivelookbehind 就在这个点上匹配(只对ARE) |
|
只要有 一个点上有一个子串匹配 re 端, positivelookbehind 就在这个点上匹配(只对ARE) |
3.3.4.2. KingbaseES-Perl对POSIX运算符的扩展 ¶
KingbaseES支持一些常用的perl正则表达式运算符,这些运算符不包含在POSIX标准中,但与其不冲突且可以结合使用。KingbaseES正则表达式中手perl影响的运算符如下表所示:
转义 |
描述 |
---|---|
|
匹配空格字符,等价于POSIX表达式``[[:space:]]`` |
|
匹配非空格字符,等价于POSIX表达式``[^[:space:]]`` |
|
匹配数字字符,等价于POSIX表达式``[[:digit:]]`` |
|
匹配非数字字符,等价于POSIX表达式``[^[:digit:]]`` |
|
匹配单词字符,等价于POSIX表达式``[[:alnum:]_]``(注意下划线是被包括的) |
|
匹配非单词字符,等价于POSIX表达式``[^[:alnum:]_]`` (注意下划线是被包括的) |
|
只在串开头匹配,与POSIX运算符 |
|
只在串的末尾匹配,与POSIX运算符 |
|
只在一个词的开头匹配 |
|
只在一个词的末尾匹配 |
|
只在一个词的开头或末尾匹配 |
|
只在一个词的不是开头或末尾的点上匹配 |
使用索引让SQL查询执行的更快,数据库索引提供了更快的表数据访问路径。使用索引优化查询效率,不需要修改SQL语句。
管理数据库索引,可以遵循以下准则:
插入表数据后再创建索引
为表和列创建正确的索引
创建顺序正确的索引列
限制每个表的索引数量
及时删除不在需要的索引
估算索引大小并设置存储参数
为索引单独指定表空间
考虑并行索引的创建
了解何时使用不可见或不可用的索引
考虑索引重建的成本
考虑删除或禁用索引的成本
管理索引的内容详情可参考 数据库管理员手册 ,主要包括以下几个方面:
创建索引
更改索引
监控索引空间的使用情况
删除索引
查询索引相关的数据字典
3.4.3.1. 函数索引的优缺点 ¶
基于函数的索引时将索引建立在某个函数或者表达式的基础上。 具有以下优点:
函数索引可以让数据库查询执行索引范围扫描,而不是全表扫描,提升查询效率。如果表达式能完全利用函数索引,则优化器可以更准确的估计表达式选择的行数。
函数索引会预先计算并存储表达式的值,查询可以从索引中获取表达式的值,不需要再计算。
可以基于表列创建函数索引,索引表达式可以是返回对象类型的方法调用。
函数索引让排序查询效率更高效
同时,函数索引也有以下的缺点:
优化器只能基于函数索引进行代价优化,而不能基于规则优化
SQL查询中使用or扩展时,不能利用函数索引
函数索引表达式如果是调用的函数,则不能约束返回类型。如果返回值有null,数据查询则执行全表扫描
函数索引表达式不能基于聚合函数
函数索引表达式的数据类型不支持大对象,或者长度未知的字符串
3.4.3.2. 函数索引的示例 ¶
本节介绍一些函数索引的例子,如下文所示。
3.4.3.2.1. 基于算术表达式的函数索引 ¶
你可以创建基于算术表达式的函数索引,查询子句使用其表达式时,数据库将进行索引范围扫描而不是全表扫描。示例如下:
--建表 create table t1 (c1 int ,c2 int,c3 numeric); insert into t1 values(1,2,3),(2,3,7),(2,78,200); --创建函数索引 create index idx_t1 on t1 (((c1+c2)*c3),c1); --查询使用索引范围扫描 select c1, (c1+c2)*c3 from t1 where (c1+c2)*c3<100;
3.4.3.2.2. 基于自定义函数的索引 ¶
你可以创建基于自定义函数的函数索引,但需要注意引用的函数必须是immutable类型。示例如下:
--建表 create table t_date (c1 date); insert into t_date values(sysdate); --创建函数 \set SQLTERM / create or replace function f_date (c1 in date) return date immutable as begin return (sysdate); end ; / --创建函数索引 create index inx_tdate on t_date(f_date(c1)); --查询使用函数索引 select * from t_date where f_date(c1)>'2000/09/09';
3.4.3.2.3. 基于大小写敏感的函数索引 ¶
你可以利用upper或者lower函数创建索引,允许表数据进行大小写不敏感的搜索扫描,加快查询速度。示例如下:
--建表 create table t2 (c1 varchar(100)); insert into t2 values('KingbaseES'),('KINGBASE'),('kes'); --创建函数索引 create index inx_t2 on t2(lower(c1)); --查询使用函数索引 select * from t2 where lower(c1)='kingbasees';
使用数据库约束来强制性限制业务规则或者数据规范,比起应用程序运行规则代码,执行效率更高并且检查速度更快,设计流程也会更简单。
但如果应用层可以在不查询表的情况下就能检查数据的有效性时,建议同时使用数据库约束和应用代码强制业务规则。这样可以减少无效数据插入数据库的负载。
创建唯一键或者主键约束后,KingbaseES数据库会自动创建索引。如果要使用包含外键约束的索引,需要手动创建。当已经存在约束索引时,再单独创建约束时数据库不会再自动为其创建索引。对于约束和索引的使用需要注意以下几点:
唯一约束和主键约束可以使用唯一索引
约束和索引中的列顺序不需要一致
如果已启用唯一或者主键约束,则不能删除对应的索引。需要先删除或者禁用该约束,再删除索引
数据库默认情况下,列可以包含null值。要确保列不包含null值,需要使用not null 约束。在以下情况下可以使用not null约束:
列必须为非空值
允许数据库对表进行索引扫描。在索引列中必须要有一个非空列,数据库查询时才能使用索引扫描
默认情况下,列值为null。向表插入数据时,如果未指定列值,则插入默认值。可以在建表时使用default指定列的默认值,也可以之后再修改表定义指定默认值。在下面这些情况下可以指定默认值:
列包含非空约束
列有一个最常见的值
非空列在未指定值时,有一个默认值
希望由数据库自动插入列值
primary key主键约束标识列值唯一且值非空。每张表只能有一个主键,但主键可以是组合列。但是通常情况下,单个列作为主键是最佳方案,例如可以选择序列作为主键列。单列作为主键有以下特点:
唯一的
非空
简短
因此,在程序设计中尽量减少使用复合主键,组合列的值很长且不能用序列自动生成
一般情况下,在不允许重复的非空列上使用unique唯一约束。例如,身份证号、员工编号等类型字段。
当两张表共享一列或者多列数据时,可以使用foreign key外键约束来强制执行完整性约束,确保共享列在两张表中始终包含相同值。将一个表指定为引用表,另一个表为被引用表。被引用表中的共享列需要包含主键约束或唯一约束,引用表中的共享列 引用外键约束。在外键列上定义额外的约束可能会影响引用关系。如果被引用键的主键约束或者唯一约束不存在或者被禁用,则无法启用外键。
3.5.7.1. 管理外键约束 ¶
3.5.7.1.1. 外键列的数据类型和名称 ¶
引用表和被引用表的共享列必须使用相同的数据类型,但列名可以不一样。
3.5.7.1.2. 创建外键的权限 ¶
要创建外键,需要对引用表和被引用表都有访问权限。用户对被引用表的引用列有对象权限,对于引用表需要有建表权限。
3.5.7.1.3. 外键的属性选择 ¶
KingbaseES数据库允许强制执行不同类型的外键约束。
默认设置下,数据库会阻止删除和更新被引用列操作。
设置外键为DELETE CASCADE 或 UPDATE CASCADE时,允许删除或者更新被引用键。这种情况下,引用键的数据行也会随之被删除或者更新。
设置外键为DELETE SET NULL 或 UPDATE SET NULL时,删除或者更新被引用键时,引用键的值会被置为空。
3.5.7.2. 外键与空值 ¶
外键允许键值为空。如果要对复合外键中的空值执行完整性约束,不允许空值存在,可以再定义一个check约束。
3.5.7.3. 引用表的关系 ¶
引用表和被引用表之间的关系可以通过被应用表外键列上的其他约束类型类确定。
外键没有约束
当没有在外键上定义其他约束时,引用表中的列值可以引用相同的引用列值。并且允许外键为空值。
外键和非空约束
当外键不允许空值时,引用表中的每一行值都必须显式引用被引用表的键值,外键不存在空值。
外键和唯一约束
在外键上创建唯一约束后,引用表只存在一行可以引用被引用表的键值,外键允许存在空值
外键和唯一非空约束
当外键上同时包含唯一约束和非空约束时,引用表只存在一行可以引用被引用表的键值,并且外键不允许为空值。
3.5.7.4. 多外键约束规则 ¶
KingbaseES允许一个列被多个外键约束引用,并且没有数量限制。
3.5.7.5. 延迟约束检查 ¶
如果需要将检查约束推迟到事务结束时,可以使用 SET CONSTRAINT 子句。设置延迟约束检查时,需要注意以下几点:
选择适当的数据,可以推迟唯一约束和外键约束的检查
外键定义为更新级联操作
确保约束是可延迟的,确保创建唯一约束和外键约束时设置的是DEFERRABLE
在事务开始前设置所有约束延迟
示例如下:
--创建表、可延迟约束 CREATE TABLE t1 (a int); ALTER TABLE t1 ADD CONSTRAINT pk_a PRIMARY KEY(a) DEFERRABLE INITIALLY DEFERRED; --设置事务约束延迟 \set AUTOCOMMIT off START TRANSACTION; SET CONSTRAINTS ALL DEFERRED; --验证 INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(1); --事务提交时才会检查约束,提示违反约束限制的错误 COMMIT; 错误:重复键违反唯一约束
3.5.7.6. 外键与索引 ¶
创建外键约束时,不会自动为其创建对应的索引。如果被引用列存在更新或者删除时,最好为外键单独创建索引,提升效率。
需要基于逻辑表达式强制完整性约束时,可以使用check约束。
3.5.8.1. check约束的限制 ¶
check约束要求表的每一行都满足表达式条件,否则回滚该数据修改语句。check约束条件有以下限制:
条件必须是一个布尔表达式,可以使用表达式对正在插入或者更新的行数据进行评估
条件不能包含子查询和序列
条件不能包含sysdate,user等不确定值的函数
条件不能包含伪列rownum
条件不能包含用户自定义的函数
3.5.8.2. check约束设计 ¶
使用检查约束是,仅当条件计算结果为false时才违反约束,true和null值都不违反检查条件
3.5.8.3. 多个check约束规则 ¶
单个列可以用多个检查约束,check约束的数量没有限制。但在设计时需要注意多个约束的顺序,以及避免互相检查产出的冲突。
3.5.8.4. check约束和非空约束 ¶
check约束可以实现not null约束的功能,可以使用not null约束或者check约束来执行非空约束。not null 约束设置会更简单一些。
复合键只能允许所有值为空或者非空时,必须使用check约束。例如,支持表t1的a列和b列必须同时为空或者同时非空:
create table t1 (a int,b int); alter table t1 add constraint t1_check check ((a is null and b is null) or (a is not null and b is not null));
启用和禁用约束都有对应的运行机制和过程:
启用约束
约束被启用后,将对关联列中的数据值强制执行约束规则。约束的定义存储在数据字典中
禁用约束
约束被禁用后,不会强制执行约束检查,但约束的定义仍然保存在数据字典中。
3.5.9.1. 禁用约束的场景 ¶
在正常情况下,约束一般保持被启用状态。但在某些特殊情况下,出于性能原因,可以暂时禁用约束。例如:
需要将大量数据插入到表中
对大表执行批量更新操作
导入导出表时
上述情况下,暂时禁用约束可以加快操作的执行速度。
3.5.9.2. 启用和禁用约束的执行 ¶
数据库创建约束时,默认约束是启用状态,可以通过ALTER语句来修改约束的启用状态。
例如,当数据库需要执行大批量更新操作时,可以暂时禁用约束:
ALTER TABLE table_name disable primary key; ALTER TABLE table_name MODIFY CONSTRAINT public_table_name_constraint_1 disable;
当批量操作完成后,再启用约束:
ALTER TABLE table_name enable primary key; ALTER TABLE table_name MODIFY CONSTRAINT public_table_name_constraint_1 enable;
3.5.9.3. 修复异常约束 ¶
在修改约束时,如果存在某一行数据违反约束规则,必须修改或者删除该行后,才能修改或者启用约束。
可以通过ALTER MODIFY CONSTRAINT语句来修改约束,例如约束的启用、验证以及延迟性:
--启用约束 ALTER TABLE table_name MODIFY CONSTRAINT public_table_name_constraint_1 enable; --设置约束为novalidate状态 ALTER TABLE table_name MODIFY CONSTRAINT public_table_name_constraint_1 enable novalidate; --延迟约束检查 ALTER TABLE table_name MODIFY CONSTRAINT public_table_name_constraint_1 initially deferred;
您可以修改约束的名字。和表对象一样,约束名必须是唯一的,不存在同名约束。创建约束时,如果未指定约束名,数据库会自动为约束命名。为了方便程序开发管理,建议最好是为约束统一命令,方便之后的约束管理。
使用alter语句来修改约束名,例如:
ALTER TABLE table_name rename CONSTRAINT public_table_name_constraint_1 to table_name_pk;
通过ALTER TABLE DROP 语句来删除约束。要删除约束和依赖于它的所有其他完整性约束,可以指定cascade语句。例如:
ALTER TABLE table_name drop CONSTRAINT table_name_pk;
要查找约束的相关信息,可以通过系统视图查看,如下表所示:
视图 |
描述 |
---|---|
sys_constraint |
包含数据库所有的约束信息 |
sys_constraint_status |
包含数据库所有的约束状态信息 |
all_cons_columns |
包含数据库所有的约束基本信息 |
all_constraints |
包含数据库所有的约束基本信息和状态信息 |
user_cons_columns |
包含用户所有的约束基本信息 |
user_constraints |
包含用户所有的约束基本信息和状态信息 |