数据库管理 2023-04-13
- 第六十六期 SQL Domain
- 1 基本介绍
- 2 Domain的表达式和条件
- 3 语法
- 4 语义
- 5 示例
- 总结
第六十六期 SQL Domain
上一期一笔带过了部分Oracle 23c的新特性,这一期重点讲一下SQL Domain新特性。
【https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/create-domain.html】
1 基本介绍
SQL Domain是一个属于SCHEMA的高级字典对象,包含一组可选的属性和约束。可以将表中的列与Domain进行关联,从而显示的将Domain包含的可选属性和约束应用到这些列中。一个Domain至少必须指定一个Oracle内建的数据类型。Domain的数据类型必须是单个的Oracle数据库类型。对于字符数据类型,必须指定一个最大长度,即是VARCHAR2(L [CHAR|BYTE]), NVARCHAR2(L), CHAR(L [CHAR|BYTE]), or NCHAR(L)中的一个。
2 Domain的表达式和条件
Domain的表达式可以简单的、日期时间、时间间隔,CASE、复合或者domain列表表达式之一:
- 简单的domain表达式可以是字符串、数字、 sequence.CURRVAL、 sequence.NEXTVAL、NULL或者SCHEMA Domain。它类似于简单表达式,只不过使用domain名称而不是列名。它将域名引用为限定名称、Oracle内置的Domain名或者使用一个Domain的公有同义词。
- 日期时间Domain表达式仅引用域表达式的日期时间表达式。
- 时间间隔表达式仅仅被定义为正则区间表达式,只是它引用了domain表达式。例如, (SYSTIMESTAMP - DayOfWeek) DAY(9) TO SECOND 是一个时间间隔表达式。
- 符合表达式是(expr), expr op expr with op +, -, *, /, ||, or expr排序collation_name,其中expr是一个domain表达式。
'email: ' || EmailAddress
DayOfWeek + INTERVAL '1' DAY
TO_CHAR(LastFour(SSN))
- CASE domain表达式与正则case表达式类似,只是它仅引用domain表达式。
CASE WHEN TO_UPPER(DOMAIN_DISPLAY(DayOfWeek)) != 'SAT'
AND TO_UPER(DOMAIN_DISPLAY(DayOfWeek)) != 'SUN'
THEN 'week day' ELSE 'weekend' END
- 与SQL Domain表达式的定义类似,Domain条件就像正则SQL条件一样,只是它只引用Domain表达式。您可以在Domain表达式中使用关键字值,而不是使用Domain名称。
CREATE DOMAIN DayOfWeek AS CHAR(3 CHAR)
CONSTRAINT
CHECK DayOfWeek_C (UPPER(Substr(VALUE, 1, 3)) IN ('MON', 'WED', 'FRI', 'SAT', 'SUN') or
UPPER(Substr(VALUE, 1, 2)) IN ('TU', 'TH'))
DEFERRRABLE INITIALLY DEFERRED
COLLATE BINARY_CI
DISPLAY SUBSTR(VALUE, 1, 3);
3 语法
create_domain::=
create_single_column_domain::=
column_properties_clause::=
create_multi_column_domain::=
create_flexible_domain::=
result_expr::=
default_clause::=
constraint_clause::=
annotations_clause::=
4 语义
-
IF NOT EXISTS
- 如果domain不存在则创建新的domain
- 如果domain存在则不会创建新的domain
如果使用IF EXISTS则会报错:Incorrect IF NOT EXISTS clause for CREATE statement。
-
domain_name
domain_name遵循与任何类型名称相同的限制,并且不能与domain schema中的任何对象的名称、任何Oracle提供的数据类型以及任何Oracle提供的domain名称发生冲突。
这些限制适用于CDB环境中的PDB级别。
请注意,域是schema的catalog对象,因此受schema级别对象的限制。 -
datatype
datatype必须是Oracle内建的数据类型:- CHAR(L [CHAR|BYTE]), NCHAR(L), VARCHAR(L [CHAR|BYTE]), VARCHAR2(L [CHAR|BYTE]), NVARCHAR2(L), LONG
- NUMBER[p, [s]], FLOAT, BINARY_FLOAT, BINARY_DOUBLE
- RAW, LONG RAW (extended included)
- DATE, TIMESTAMP (WITH (LOCAL) TIME ZONE), INTERVAL
- BFILE, BLOB, CLOB, NCLOB
- JSON native datatype
- BOOLEAN
-
default_expression
default_expression必须是一个域表达式,并且必须符合给定数据类型的默认列表达式的所有限制:- default_expression不能包含返回域引用或嵌套函数调用的SQL函数,它不能是子查询表达式。
- default_expression的数据类型必须于domain指定的数据类型匹配。
- 作为domain的表达式,default_expression不能引用任何表或列以及任何其他domain名。
- default_expression可以指一个序列的NEXTVAL和CURRVAL。不能引用PL/SQL函数。
-
constraint_clause
请注意,domain约束可以有可选的名称。它们NOT NULL、NULL或CHECK约束。可以在列级别和domain级别上同时指定多个这样的约束子句。
CHECK条件以及ALTER DOMAIN中的检查条件和表达式只能引用domain的列。如果domain有单个列,则列名是domain名或关键字VALUE,但相同的表达式不能同时包含domain名和VALUE作为列名。
constraint_name是可选。当指定时,它不能与schema中的任何其他约束的名称发生冲突(如果在CDB环境中,则在给定的PDB中)。当未指定时,将使用系统生成的名称。domain约束遵循与表级约束和列级约束相同的规则:一个已命名的表或列级约束不能与同一schema中的任何其他约束的名称相一致。即使是在相同的schema中,domain约束可以与表名相同。它们可以与列名相同,并且约束可以与它在上面定义的表或列使用相同的名称。
CHECK条件必须是一个domain的逻辑条件,同时必须符合对转换为domain表达式的检查约束的所有限制:- 它只能引用domain名,就像列上的检查约束只能引用列一样。它不能引用任何表或视图中的任何列,甚至在domain schema中也不能。
- 不能使用子查询或标量查询表达式。
- 条件不能引用非确定性函数(如CURRENT_DATE),或用户定义的PL/SQL函数。
- 允许CHECK IS JSON(STRICT)约束。
- CHECK约束条件一次应用于一个值,如果值替换为domain_name的CHECK条件计算结果为TRUE或UNKNOWN,则满足该条件。
Domain约束可以按任意顺序强制执行。
NULL约束意味着允许domain的值为NULL,并且是默认值。
如果未指定constraint_state,则约束为NOT DEFERABLE INITIALLY IMMEDIATE。 -
COLLATE
当指定排序规则时,它符合列级别或SCHEMA级别排序规则的所有限制。如果指定了排序规则,数据类型必须是字符数据类型。
当创建一个表,其中的列标记为一个排序规则不同于该列的排序规则的domain时,将引发错误。
将列更改为具有与列domain的排序规则不同的排序规则时,将引发错误。
这应该确保具有指定排序规则的域的所有列具有与其域相同的排序规则的不变性。如果未指定排序规则且数据类型是可排序的,则将使用列的排序规则(如果已指定),否则将使用域模式中的基础默认数据类型排序规则。
如果未指定排序规则且数据类型是可排序的,则将使用列的排序规则(如果已指定)。 -
display_expression
使用 display_expression 根据domain规范格式化数据。它可以是任何允许作为domain数据类型的数据类型。 display_expression 必须是不包含表或视图列、子查询、非确定性函数或 PL/SQL 函数的域表达式。它可以引用 domain_name。如果您没有为表达式指定排序规则,则 display_expression 将使用domain的排序规则(如果已指定)。 -
order_expression
使用 order_expression 对domain规范的值进行排序和比较。
order_expression 必须符合与 display_expressions 相同的限制,并且还必须是字节或字符可比较数据类型。如果为表达式的domain指定了order_expression,则返回的有domain_nameorder_expression而不是表达式,否则返回表达式。 -
annotations_clause
annotation_name是一个最多可以包含4000个字符的标识符。如果注释名称是保留字,则必须用双引号提供。当使用双引号标识符时,该标识符还可以包含空白字符。但是,不接受仅包含空白字符的标识符。
有关annotations_clause的示例,请参见末尾的示例。
有关annotations子句的完整语义,请参阅CREATE TABLE的annotations_clause。 -
FROM Clause of Create Flexible Domain
expr和comparison_expr引用了domain_disperiment_column列表中的domain判别列。
灵活东面的FROM子句是DECODE或CASE表达式,它只引用搜索表达式中的判别式列名(在CHOOSE DOMIAN USING后面的列表中),并且在结果表达式中只有domain名后面跟着列列表。结果表达式中的列必须仅为东面列列表中的列(在CREATE FLEXILE DOMAIN之后)。
5 示例
从CREATE TABLE章节中找到了建表是SQL DOMAIN的使用方法。
CREATE TABLE [owner.]name
( colname [DOMAIN][domain_owner.]domain_name [<column_def_clause>]
[, colname [DOMAIN] [domain_owner.]domain_name [<column_def_clause>]])
CREATE TABLE [owner.]name (column_list_def_clause
[, DOMAIN [domain_owner.]domain_name (column_name_list)])
首先在尝试创建domain时,报了个错:
ORA-43929: Collation cannot be specified if parameter MAX_STRING_SIZE=STANDARD is set
需要MAX_STRING_SIZE=EXTENDED,详情建四十七期。
- 示例1
这里domain dn1是数字类型;domain dn2两个值均为数字类型,第一个不能为空,第二个默认为1;domain dm1包含四个值:两个值均为数字类型,第一个不能为空,第二个不能为空切必须大于0,第三个为默认值为abc的长度为10的varhar2类型,第四个值也为数字仅为空值时插入默认0,同时要求前两个值相加小于等于100,同时第三个值长度大于第二个数值。
对应的tm1表,c1-c4满足domain dm1,c5-c6满足domain dn2,c7满足domain dn1。
下面我们来尝试插入数据:
这里c4因为插入为NULL所以值为10,c6获取默认值1。
这里插入的c2为20,而c3默认值abc长度为3,不满足大于c2的条件,因此插入失败。
将上一条语句c3改为2即可成功插入。
这里又因为c1+c2>100,不满足domain dm1。
这里c1插入的不是数字类型(domain dm1)。
这里c5不能为空(domain tm1)。 - 示例2
创建表tm2,遵循domain email限制,插入空值时拼接序列t_seq.NEXTVAL和字符串‘gmail’,插入内容必须包含@和. 且@不在首位。
插入空值时,自动填充。
正确格式的邮箱地址能正确插入。
错误格式的邮箱地址就无法插入。
使用domain_display还可以通过domain email对数据显示进行脱敏。
总结
SQL Domain还有很多功能,继续探索中。
老规矩,知道写了些啥。