本文介绍23ai的新特性,Data Use Case Domain,简称Use Case Domain或Domain,中文为用例域 或域。
分为两部分,第一部分来自Database Concepts,讲概念;第二部分来自Database Development Guide,讲语法。
Database Concepts - Data Use Case Domains
Data Use Case Domain (数据用例域)和 Schema Annotation 都属于 Application Data Usage 的范畴。
数据用例域是属于架构的高级字典对象,它封装了一组可选属性和约束。
数据库通常对应用程序实际使用数据的方式了解有限。数据库使用 VARCHAR2、NUMBER 或 DATE 等原始类型存储数据,而存储值所代表的知识(例如信用卡号或出生日期)仅存在于应用程序级元数据或上下文中。这增加了单个应用程序的复杂性,造成了工具和应用程序之间使用意识的隔阂,并引入了不同语义的可能性。
过去为应对这种分歧,人们尝试向数据库添加更多特定用途的内置数据类型,并为用户定义类型提供支持,以实现可扩展性。更复杂的数据库类型系统很少得到采用,因为它们增加了与应用程序语言类型的不匹配,创建了不可移植的应用程序代码,并且由于跨不同类型的操作受到限制而增加了开发复杂性。
可以使用 SQL 标准定义的 Oracle Database 扩展域概念来避免表示数据使用方面的此类限制。使用这种方法,可以使用原始数据类型(例如 NUMBER)以及数据使用域(例如"温度"或"信用评分")来声明列。这种用例域可以选择性地与不同的使用属性(例如检查约束、显示属性、排序规则等)相关联 。最重要的是,应用程序可以使用集中的域信息来标准化操作,而无需应用程序级元数据;例如,屏蔽信用卡号、格式化电话号码和货币值、将百分比值显示为饼图等。
用例域不会修改底层数据类型,因此也可以将其添加到现有数据中,而不会破坏应用程序或产生可移植性问题。可以为表中的一个或多个列声明用例域,也可以使用它来表示可变的使用场景,其中数据的实际使用取决于其他列中的数据。用例域可以被视为轻量级类型修饰符,用于集中记录应用程序的预期数据使用情况。此外,用例域还可用于共享注释。
用例域的特征:
- 约束 - 域可用于在多个表和列之间共享约束。这些约束可能因可延迟约束而被禁用。域中不允许使用主键和外键约束。
- 数据类型 - 允许使用所有内置 Oracle 数据类型,但 long、long raw 和对象类型除外。
- 默认表达式 - 允许使用默认表达式,它们与列默认值相同。
- 不区分大小写和重音的搜索 - 域允许不区分大小写的搜索,可用于 WHERE 和 ORDER BY 子句。
- 显示表达式 - 显示表达式允许自定义显示规则。例如,以本地格式显示货币。
- 排序表达式 - 订单表达式允许复杂的排序。例如,按规范化的标准商业货币(如美元金额)排序。
用例域类型:
- 单列 - 适用于单个数据列。
- 多列 - 适用于多个列,例如货币。
- 灵活域 - 基于其他域,并且必须与子域兼容数据类型。例如,不同国家的地址格式。
- 枚举 - 由一组名称组成,并且可选地包含与名称相对应的值。
严格与非严格:
- 非严格 - 非严格域必须与给定的数据类型匹配,但不一定与数字数据的长度或小数位数匹配。
- 严格 - 要求数据类型、长度和小数位数完全匹配。例如,货币代码必须正好是两个或三个字符。
您可以使用 Oracle SQL 创建枚举域。与常规域不同,枚举域具有默认 CHECK 约束以及显示表达式。枚举域中的名称可以在标量 SQL 表达式中允许使用文字的任何地方使用,并且域本身可以在 SELECT 语句的 FROM 子句中使用,就像它是一个表一样。枚举域是一种特殊类型的使用域,可以像任何单列域一样使用。
枚举域由一组名称和(可选)与名称相对应的值组成。名称必须是有效的 SQL 标识符,并且每个指定的值都必须是文字,或者在多列枚举的情况下,是文字列表。虽然值可以是数据用例域支持的任何数据类型,但它们都必须是相同的数据类型。
例如,之前被授予 CREATE DOMAIN 权限的用户 SCOTT 为职位创建了一个单列枚举域。
sql
CREATE DOMAIN Job_Title AS
ENUM (
Clerk = 'CLERK',
Salesman = SalesPerson = 'SALESMAN',
Manager = 'MANAGER',
Analyst = 'ANALYST',
President = 'PRESIDENT'
);
现在,可以在创建 EMP 表时使用域 Job_Title 作为数据类型。例如:
sql
CREATE TABLE emp
( empno NUMBER(4) CONSTRAINT emp_pk PRIMARY KEY,
ename VARCHAR2(10),
job Job_Title,
mgr NUMBER(4) CONSTRAINT emp_mgr_fk REFERENCES emp(empno),
hiredate DATE,
sal NUMBER(7,2)
);
插入新行数据时,可以使用枚举域来指定 JOB 列的值。以下三个 INSERT 语句是等效的。
sql
INSERT INTO emp VALUES (7698, 'PATEL', Job_Title.Salesman, 7698, TO_DATE('11-MAR-2024', 'DD-MON-YYYY'), 3500);
INSERT INTO emp VALUES (7698, 'PATEL', Job_Title.SalesPerson, 7698, TO_DATE('11-MAR-2024', 'DD-MON-YYYY'), 3500);
INSERT INTO emp VALUES (7698, 'PATEL', 'SALESMAN', 7698, TO_DATE('11-MAR-2024', 'DD-MON-YYYY'), 3500);
出错的示例,因为指定的值不在枚举中:
sql
SQL> INSERT INTO emp VALUES (7699, 'PATEL', 'xxx', 7698, TO_DATE('11-MAR-2024', 'DD-MON-YYYY'), 3500);
INSERT INTO emp VALUES (7699, 'PATEL', 'xxx', 7698, TO_DATE('11-MAR-2024', 'DD-MON-YYYY'), 3500)
*
ERROR at line 1:
ORA-11534: check constraint (SSB.SYS_C0010171) involving column JOB due to domain constraint SSB.SYS_DOMAIN_C0045 of
domain SSB.JOB_TITLE violated
Help: https://docs.oracle.com/error-help/db/ora-11534/
Database Development Guide - Data Use Case Domains
本节介绍如何在应用程序中使用数据用例域(以下简称"用例域")。
用例域概述
用例域是轻量级使用说明符,具有可选的数据库端强制执行功能,应用程序可以使用它来集中记录预期的数据使用情况。作为高级字典对象,用例域包括与表列相关的内置使用属性,例如默认值、检查约束、排序规则、显示和排序格式以及标注。使用集中式 域信息,应用程序可以标准化操作,而无需依赖应用程序级元数据。例如,您可以使用用例域来屏蔽信用卡号或格式化电话号码和货币值。
作为数据库对象,用例域属于架构并提供通用列定义,而无需修改基础列数据类型。用例域将表列的一些常见特征封装到可重用的对象中,该对象可以在其他表列上重用,而无需重复这些特征。
例如,一个架构可能有许多表,其中的列包含电子邮件地址,例如账单电子邮件、发票电子邮件和客户联系电子邮件。电子邮件地址具有特殊格式,因为它们需要"@"符号。您可以使用检查约束(例如 regexp_like (email_dom, '^(\S+)@(\S+).(\S+)$'))为电子邮件地址定义用例域,并将该域与其他电子邮件列关联,以使您的应用程序显示关联列的电子邮件地址,并在域名前加上标准"@"符号。同样,您可能希望标准化汽车牌照的显示格式,这可能需要连字符将数字与其他信息分开。使用用例域,即使许可证列具有 varchar2(6) 数据类型,您也可以将许可证信息显示为"ABC-123"。
使用用例域的好处包括:提高数据质量 ,因为它使您能够跨应用程序一致地处理值;减少编码 ,因为您可以在应用程序中重用列的属性和约束;以及操作的一致性和清晰度,因为应用程序可以查询这些用例域以了解它们所操作的数据。
用例域类型以及何时使用
有三种不同类型的域:单列、多列和灵活域。
单列用例域
单列用例域是为一个列创建的,当您希望使列使用定义在整个应用程序中保持一致时,可以使用该域。例如,您可以为电子邮件地址、邮政编码或车辆编号创建单列域。
除非以多列或灵活为前缀,否则"用例域"或"域"在本文档中是指单列用例域。
多列用例域
多列用例域为单个域下的多个列创建列使用定义。当您的逻辑实体跨越两个或更多个表列时,多列用例域是理想的选择。例如,您可以使用多列用例域来表示地址。
灵活用例域
灵活用例域是根据判别列的值从一组单列或多列用例域中动态选择的域。灵活域根据由一个或多个判别列组成的映射表达式将特定域(也称为组成域)分配给一组值列。
除了这些用例域类型之外,您还可以直接在表列上使用内置用例域。一些示例是 email_d、ssn_d 和 credit_card_number_d。
用例域所需的权限
要使用用例域,您需要以下权限:
注:数据库管理员 (DBA) 角色包括以下所有权限。
DDL 权限 | 操作 |
---|---|
CREATE DOMAIN | 您可以在自己的架构中创建域。RESOURCE 和 DB_DEVELOPER_ROLE 角色包括创建域权限。 |
CREATE ANY DOMAIN | 您可以在任何架构中创建域。 |
ALTER ANY DOMAIN | 您可以在任何架构中更改域。 |
DROP ANY DOMAIN | 您可以在任何架构中删除域。 |
EXECUTE ANY DOMAIN | 您可以在任何架构中引用或使用域。 |
要明确授予任何架构中域上的用户执行权限,请使用以下代码:
sql
GRANT EXECUTE ON <schemaName.domainName> TO <user>;
使用单列用例域
本节介绍如何创建、关联、修改、取消关联和删除单列用例域(以下简称"用例域"或"域")。
创建用例域
您可以定义一个用例域,封装一组可选属性和约束来表示常用值,例如电子邮件地址和信用卡号。
以下是创建用例域的一些示例,其中域基于表列的属性,例如默认值、约束、注释或显示和顺序表达式。
示例 10-1 创建小时工资用例域
人力资源应用程序 (HRA) 为不同的公司创建许多表。大多数公司都有一个存储小时工资的列。HRA 可以为小时工资列创建用例域,如下所示:
sql
CREATE DOMAIN HourlyWages AS NUMBER
DEFAULT ON NULL 15
CONSTRAINT MinimalWage CHECK (HourlyWages > = 7 AND HourlyWages <=1000) ENABLE
DISPLAY TO_CHAR(HourlyWages, '$999.99') ORDER ( -1*HourlyWages )
ANNOTATIONS (properties '{"Purpose": "Wages", "Applicability": "USA", "Industry": {"Sales", "Manufacturing"} }');
示例 10-2 为代理键创建用例域
如果您想在数据库应用程序中注释代理键列并维护此类列的标准元数据,请创建类似于以下内容的域:
sql
CREATE DOMAIN surrogate_id AS INTEGER
STRICT
NOT NULL
ANNOTATIONS ( primary_key, mandatory, operations '["insert", "delete"]' );
示例 10-3 创建出生日期用例域
以下是一个用例域,它确保所有包含出生日期的列都是"仅含日期"并以年为单位显示年龄。
sql
CREATE DOMAIN birth_date AS DATE
CONSTRAINT birth_date_only_c check ( birth_date = trunc ( birth_date ) )
DISPLAY FLOOR ( months_between ( sysdate, birth_date ) / 12 ) || ' years'
ANNOTATIONS ( sensitive 'PII Data', operations '["insert", "update"]' );
示例 10-4 为默认日期和时间值创建用例域
您可以为日期和时间值创建用例域,以确保插入内容采用标准格式。
sql
CREATE DOMAIN insert_timestamp AS
TIMESTAMP WITH LOCAL TIME ZONE
DEFAULT ON NULL systimestamp;
示例 10-5 创建正身高用例域
以下用例域确保人的身高为正,并且身高按降序排列。
sql
CREATE DOMAIN height AS NUMBER
CONSTRAINT positive_height_c CHECK ( value > 0 )
ORDER value * -1
ANNOTATIONS ( operations '["insert", "update"]' );
示例 10-6 创建正权重的用例域
以下用例域确保人们具有正权重。
sql
CREATE DOMAIN weight AS NUMBER
CONSTRAINT positive_weight_c CHECK ( value > 0 )
ANNOTATIONS ( operations '["insert", "update"]' );
示例 10-7 定义具有多个检查约束的域
您可以为电子邮件地址定义一个用例域,类似于以下示例中的域,它也具有多个检查约束。
sql
CREATE SEQUENCE IF NOT EXISTS email_seq;
CREATE DOMAIN email AS VARCHAR2(100)
DEFAULT ON NULL email_seq.NEXTVAL || '@domain.com'
CONSTRAINT email_c CHECK (REGEXP_LIKE (email, '^(\S+)\@(\S+)\.(\S+)$'))
CONSTRAINT email_max_len_c CHECK (LENGTH(email) <=100) DEFERRABLE INITIALLY DEFERRED
DISPLAY '---' || SUBSTR(email, INSTR(email, '@') + 1);
满足两个约束的任何 VARCHAR2(L [BYTE|CHAR]) 数据类型的列都可以与域关联。INITIALLY DEFERRED 子句将约束 email_max_len_c 值的验证延迟到提交时间。
示例 10-8 JSON 模式验证
域还可用于可重复使用的 JSON 模式验证,如下例所示。
sql
CREATE DOMAIN department_json_doc AS JSON
CONSTRAINT CHECK (
department_json_doc IS JSON VALIDATE USING '{
"type": "object",
"properties": {
"departmentName": { "type": "string" },
"employees": { "type": "array" }
},
"required" : [ "departmentName", "employees" ],
"additionalProperties": false
}' );
在创建表时将用例域与列关联
定义用例域后,您可以在创建表时将域与新表列关联,也可以将其与现有表中的现有列关联。将用例域与列关联会明确将域的可选属性和约束应用于该列。
您可以使用 CREATE TABLE DDL 将用例域与新创建的列关联。以下示例显示了如何将用例域与新表中的列关联。这些示例使用前面示例中创建的用例域。
示例 10-9 在创建表时关联 HourlyWages 用例域
使用 HourlyWages 域,HRA 可以创建工资列具有相同域特征的多个表。
sql
CREATE TABLE employee (
name VARCHAR2(100),
id NUMBER,
wage NUMBER DOMAIN HourlyWages);
CREATE TABLE wage (
name VARCHAR2(100),
id NUMBER,
wage NUMBER DOMAIN HourlyWages,
gross_pay NUMBER,
deductions NUMBER,
net_pay NUMBER);
示例 10-10 在创建表时关联 surrogate_id 用例域
将严格用例域(如 surrogate_id)与列关联时,请确保关联列具有与域相同的数据类型。严格域还要求列的长度、小数位数和精度与域匹配。
以下代码失败,出错为 ORA-11517: the column data type does not match the domain column,因为您尝试将 NUMBER 数据类型列与 INTEGER/NUMBER(*,0) 数据类型域链接。
为了确保关联有效,可以使用 NUMBER(*,0) 列数据类型与 surrogate_id 用例域关联 (INTEGER == NUMBER(*,0))。
sql
CREATE TABLE orders (
order_id NUMBER(*,0) DOMAIN surrogate_id,
customer_id NUMBER,
order_datetime TIMESTAMP WITH LOCAL TIME ZONE
DEFAULT SYSTIMESTAMP);
示例 10-11 在创建表时关联 surrogate_id、birth_date、height 和 weight 用例域
DOMAIN 关键字是可选的。您可以在以下示例中看到,birth_date 域与不带 DOMAIN 关键字的 date_of_birth 列相关联。该示例还表明,您可以为列定义比域中更精确的数据类型(精度和小数位数)。height_in_cm 和 weight_in_kg 列的关联域数据类型为 NUMBER,而列数据类型具有精度和小数位数值,例如 NUMBER(4,1)。
sql
CREATE TABLE people (
person_id DOMAIN surrogate_id
GENERATED BY DEFAULT AS IDENTITY
PRIMARY KEY,
full_name VARCHAR2(256),
date_of_birth birth_date,
height_in_cm NUMBER(4, 1) DOMAIN height,
weight_in_kg NUMBER(6, 3) DOMAIN weight);
示例 10-12 在创建表时关联department_json_doc用例域
sql
CREATE TABLE departments (
department_id INTEGER PRIMARY KEY,
department_doc JSON DOMAIN department_json_doc);
指南
- 将域与列关联时,除了列的数据类型外,您还可以指定域名,在这种情况下,将使用列的数据类型,前提是域数据类型与列的数据类型兼容。
- 将域与列关联时,您可以指定域名而不是列的数据类型(最常用的情形),在这种情况下,将使用域数据类型作为列,其中 DOMAIN 关键字是可选的。
- 如果将域定义为 STRICT,则域的数据类型、小数位数和精度必须与列的数据类型、小数位数和精度匹配。
- 如果域未定义为 STRICT,则可以将任意长度的域与任意长度的列关联。例如,您可以将 VARCHAR2(10) 域与任何 VARCHAR2 列关联。
在与用例域相关的列上使用 DML
以下是一些可以在新创建的表列上使用的 DML 语句示例,这些语句具有关联的用例域。
示例 10-13 在人员表上使用 DML 语句
以下 INSERT 命令将数据插入人员表列,同时验证关联域中指定的检查约束是否未被违反。
sql
INSERT INTO people
VALUES ( 1, 'Sally Squirell', date'1981-01-01', 180.1, 61 );
INSERT INTO people
VALUES ( 2, 'Brian Badger', date'2016-12-31', 120.4, 27.181 );
以下 INSERT 命令失败,因为高度被指定为负数,因此违反了相关的检查约束。
sql
INSERT INTO people
VALUES ( 3, 'Fergal Fox', date'2023-04-12', -99, 1 );
输出为:
sql
Error report -
ORA-11534: check constraint (SSB.SYS_C0010332) involving column HEIGHT_IN_CM due to domain constraint SSB.POSITIVE_HEIGHT_C of domain SSB.HEIGHT violated
您可以使用关联域来显示按身高降序排列的数据,还可以查看相应的年龄和体重。
sql
SELECT full_name, DOMAIN_DISPLAY ( date_of_birth ) age,
height_in_cm, weight_in_kg
FROM people
ORDER BY DOMAIN_ORDER ( height_in_cm );
FULL_NAME AGE HEIGHT_IN_CM WEIGHT_IN_KG
-------------------- ---------- ------------ ------------
Sally Squirell 42 years 180.1 61
Brian Badger 6 years 120.4 27.181
您可以描述people表来查看数据类型定义和引用的域信息。
sql
DESC people;
Name Null? Type
---------------------- -------- ----------------------------------------
PERSON_ID NOT NULL NUMBER(38) HR.SURROGATE_ID
FULL_NAME VARCHAR2(256)
DATE_OF_BIRTH DATE HR.BIRTH_DATE
HEIGHT_IN_CM NUMBER(4,1) HR.HEIGHT
WEIGHT_IN_KG NUMBER(6,3) HR.WEIGHT
以下 SELECT 命令使您能够查看从关联域继承的列注释。
sql
SELECT column_name, annotation_name, annotation_value
FROM user_annotations_usage
WHERE object_name = 'PEOPLE';
COLUMN_NAME ANNOTATION_NAME ANNOTATION_VALUE
-------------------- -------------------- --------------------
PERSON_ID PRIMARY_KEY <null>
PERSON_ID MANDATORY <null>
PERSON_ID OPERATIONS ["insert", "delete"]
DATE_OF_BIRTH OPERATIONS ["insert", "update"]
DATE_OF_BIRTH SENSITIVE PII Data
HEIGHT_IN_CM OPERATIONS ["insert", "update"]
WEIGHT_IN_KG OPERATIONS ["insert", "update"]
示例 10-14 在带有 JSON 数据的部门表上使用 DML 语句
部门表上的以下 INSERT 命令成功,因为它包含 Department_json_doc 域中的所有 JSON 属性。
sql
INSERT INTO departments
VALUES ( 1, '{
"departmentName" : "Accounting",
"employees" : [
{"empName":"William"},
{"empName":"Shelley"}
]
}');
以下 INSERT 命令失败,因为缺少员工属性。
sql
INSERT INTO departments
VALUES ( 2, '{
"departmentName" : "Finance"
}');
Error report -
SQL Error: ORA-40875: JSON schema validation error
JZN-00501: JSON schema validation failed
将用例域与现有或新列关联
您可以使用带有 MODIFY 或 ADD 子句的 ALTER TABLE DDL 将用例域与现有或新添加的列关联。
示例 10-15 将用例域与新创建的列关联
对于新创建的客户表:
sql
CREATE TABLE customers (
cust_id NUMBER,
cust_email VARCHAR2(100));
您可以使用 ADD 添加新列:cust_new_email 并将其与电子邮件域关联。
sql
ALTER TABLE customers
ADD (cust_new_email VARCHAR2(100) DOMAIN email);
示例 10-16 将电子邮件用例域与现有列关联
您可以使用 MODIFY 修改现有列:cust_email 并将其与电子邮件域关联。
sql
ALTER TABLE customers
MODIFY (cust_email VARCHAR2(100) DOMAIN email);
您还可以使用 ALTER TABLE ... MODIFY 语句将用例域添加到列。在下面的示例中,订单表列(即 order_datetime)和 insert_timestamp 域具有不同的默认值。insert_timestamp 域具有 DEFAULT with ON NULL 子句,而 order_datetime 列中缺少该子句。因此,当您尝试将域与列关联时,您会收到错误 ORA-11501:列默认值与列的域默认值不匹配。
sql
ALTER TABLE orders
MODIFY order_datetime DOMAIN insert_timestamp;
Error report -
ORA-11501: The column default does not match the domain default of the column.
为了克服默认不匹配的问题,请指定与域默认值匹配的列默认子句。
sql
ALTER TABLE orders
MODIFY order_datetime DOMAIN insert_timestamp
DEFAULT ON NULL systimestamp;
示例 10-17 查询以查看关联域
sql
SELECT constraint_name, search_condition_vc, domain_constraint_name
FROM user_constraints
JOIN user_cons_columns
USING ( constraint_name, table_name )
WHERE table_name = 'PEOPLE'
AND constraint_type = 'C'
AND column_name = 'WEIGHT_IN_KG';
输出为:
sql
CONSTRAINT_NAME SEARCH_CONDITION_VC DOMAIN_CONSTRAINT_NAME
-------------------- ------------------------------ --------------------------
SYS_C008493 "WEIGHT_IN_KG">0 POSITIVE_WEIGHT_C
指南
- 如果仅为新添加的列指定域,则 DOMAIN 关键字对于 ALTER TABLE ... ADD 语句是可选的。
- DOMAIN 关键字对于 ALTER TABLE ... MODIFY 语句是必需的。
- 列数据类型应与域数据类型兼容。
- 如果域具有默认表达式或排序规则,则它应与关联列的默认表达式和排序规则匹配。
- 如果关联列已具有与其关联的域,则返回错误。
更改用例域
您可以更改用例域的显示表达式、顺序表达式和注释。支持以下 ALTER DOMAIN DDL 语句。
示例 10-18 从域中删除顺序表达式
高度用例域定义将顺序表达式作为 VALUE,因此要删除顺序表达式,您必须删除顺序表达式。
sql
ALTER DOMAIN height DROP ORDER;
示例 10-19 向用例域添加显示表达式
要向高度用例域添加显示表达式,请使用以下 ALTER 命令。
sql
ALTER DOMAIN height
ADD DISPLAY round ( value ) || ' cm';
示例 10-20 更改birth_date域的显示表达式
要将birth_date域的显示表达式更改为年份和月份,请使用以下ALTER命令。
sql
ALTER DOMAIN birth_date
MODIFY DISPLAY
FLOOR ( months_between ( sysdate, birth_date ) / 12 ) || ' years ' ||
MOD ( FLOOR ( months_between ( sysdate, birth_date ) ), 12 ) || ' months';
示例 10-21 查询人员表中更改的出生日期、身高和体重域
sql
COLUMN age FORMAT A20
SELECT full_name, DOMAIN_DISPLAY ( date_of_birth ) age,
DOMAIN_DISPLAY ( height_in_cm ) height_in_cm, weight_in_kg
FROM people
ORDER BY DOMAIN_ORDER ( height_in_cm );
FULL_NAME AGE HEIGHT_IN_CM WEIGHT_IN_KG
-------------------- -------------------- ------------- ------------
Sally Squirell 42 years 5 months 180 cm 61
Brian Badger 6 years 5 months 120 cm 27.181
示例 10-22 更改用例域上的注释
以下代码为高度用例域添加标注。
sql
ALTER DOMAIN height
ANNOTATIONS (
operations '["insert", "update", "sort"]',
sensitive 'Private data');
示例 10-23 查询注释更改的字典视图
sql
COLUMN annotation_value FORMAT A40
SELECT column_name, annotation_name, annotation_value
FROM user_annotations_usage
WHERE object_name = 'PEOPLE';
COLUMN_NAME ANNOTATION_NAME ANNOTATION_VALUE
-------------------- -------------------- ----------------------------------------
PERSON_ID PRIMARY_KEY <null>
PERSON_ID MANDATORY <null>
PERSON_ID OPERATIONS ["insert", "delete"]
DATE_OF_BIRTH SENSITIVE PII Data
DATE_OF_BIRTH OPERATIONS ["insert", "update"]
HEIGHT_IN_CM OPERATIONS ["insert", "update"]
HEIGHT_IN_CM OPERATIONS ["insert", "update", "sort"]
HEIGHT_IN_CM SENSITIVE Private data
WEIGHT_IN_KG OPERATIONS ["insert", "update"]
指南
- 仅当域不是灵活域的组成部分时,您才可以更改域的显示表达式。
- 仅当域不是灵活域的组成部分时,您才可以更改域的顺序表达式。
- 您只能更改域级注释。
取消用例域与列的关联
您可以使用带有 DROP 子句的 ALTER TABLE DDL 来解除用例域与列的关联。
示例 10-24 解除用例域与列的关联
要从 customers 表的 cust_email 列中删除关联的域:
sql
ALTER TABLE customers
MODIFY ( cust_email ) DROP DOMAIN;
删除域但保留域的约束:
sql
ALTER TABLE customers
MODIFY ( cust_email ) DROP DOMAIN PRESERVE CONSTRAINTS;
示例 10-25 解除用例域与列的关联
以下代码从人员表中的 height_in_cm 列中删除高度域的域关联,同时保留约束。
sql
ALTER TABLE people
MODIFY ( height_in_cm ) DROP DOMAIN PRESERVE CONSTRAINTS;
示例 10-26 查询具有分离用例域的列
对 user_constraints 字典表的 SELECT 查询揭示了更改。
sql
SELECT constraint_name, search_condition_vc, domain_constraint_name
FROM user_constraints
JOIN user_cons_columns
USING ( constraint_name, table_name )
WHERE table_name = 'PEOPLE'
AND constraint_type = 'C';
CONSTRAINT_NAME SEARCH_CONDITION_VC DOMAIN_CONSTRAINT_NAME
-------------------- ---------------------------------------- ------------------------------
SYS_C009491 "DATE_OF_BIRTH"=TRUNC("DATE_OF_BIRTH") BIRTH_DATE_ONLY_C
SYS_C009494 "WEIGHT_IN_KG">0 POSITIVE_WEIGHT_C
SYS_C009489 "PERSON_ID" IS NOT NULL <null>
SYS_C009490 "HEIGHT_IN_CM">0 <null>
示例 10-27 重新添加已删除的身高域
以下代码将已删除的身高域重新添加到 people 表中的 height_in_cm 列。
sql
ALTER TABLE people
MODIFY ( height_in_cm ) ADD DOMAIN height;
示例 10-28 查询重新添加的域
使用重新添加的高度域创建了重复的 height_in_cm > 0 约束。
sql
SELECT constraint_name, search_condition_vc, domain_constraint_name
FROM user_constraints
JOIN user_cons_columns
USING ( constraint_name, table_name )
WHERE table_name = 'PEOPLE'
AND constraint_type = 'C'
ORDER BY search_condition_vc;
CONSTRAINT_NAME SEARCH_CONDITION_VC DOMAIN_CONSTRAINT_NAME
-------------------- ---------------------------------------- ------------------------------
SYS_C009491 "DATE_OF_BIRTH"=TRUNC("DATE_OF_BIRTH") BIRTH_DATE_ONLY_C
SYS_C009495 "HEIGHT_IN_CM">0 POSITIVE_HEIGHT_C
SYS_C009490 "HEIGHT_IN_CM">0 <null>
SYS_C009489 "PERSON_ID" IS NOT NULL <null>
SYS_C009494 "WEIGHT_IN_KG">0 POSITIVE_WEIGHT_C
指南
解除列的域关联时,默认情况下会保留以下内容:
- 域的排序规则。
- 添加到列的非域约束。
不保留域的默认值。仅当将默认值明确应用于列时才会保留。
删除用例域
您可以删除用例域。有关删除域的更多信息,请参阅 Oracle 数据库 SQL 语言参考。
示例 10-29 删除用例域
以下客户表的一列与电子邮件域相关联:
sql
CREATE TABLE customers (
cust_id NUMBER,
cust_email VARCHAR2(100) DOMAIN email);
以下 DROP 命令返回错误,因为客户表具有与电子邮件域关联的 cust_email 列。
sql
DROP DOMAIN email;
以下 DROP 命令成功:
sql
DROP DOMAIN email FORCE;
cust_email 列与电子邮件域解除关联,所有提及电子邮件域的语句均无效。
示例 10-30 删除域但保留约束
以下 DROP 命令成功,但保留默认和约束表达式。
sql
DROP DOMAIN email FORCE PRESERVE;
cust_email 列保留默认的 ON NULL email_seq.NEXTVAL || '@domain.com',并将约束中的域名替换为列名后保留约束:CONSTRAINT email_c CHECK(REGEXP_LIKE (cust_email, '^(\S+)@(\S+).(\S+)$'))。
指南
- 要删除在灵活域中引用的域,请使用带有 FORCE 选项的 DROP DOMAIN。
- 如果域未与任何表列关联,并且域不是灵活域的组成部分,则删除该域。如果用例域正在使用中,则 DROP 语句将失败。
- 如果域与任何表列关联,则必须使用 FORCE 选项删除该域。使用 FORCE 选项还可以:
- 如果仅设置了域默认值,则删除默认表达式。
- 如果同时设置了域和列默认值,则保留列默认表达式。
- 从所有关联列中删除域注释。
- 保留任何域关联列的排序规则。
- 使游标缓存中的所有 SQL 相关语句无效。
- 如果使用了 FORCE PRESERVE,则保留任何域关联列上的约束。
关于删除域和回收站
删除的表将放置在回收站中。要删除与回收站中的表关联的域,必须在 DROP 命令中使用 FORCE 选项。
可以使用 FLASHBACK TABLE 命令将回收站中的已删除表恢复到删除前的位置。如果在删除关联域后将表恢复到删除前的位置(使用 FLASHBACK TABLE TO BEFORE DROP),则该表具有与删除前相同的默认值、排序规则、可空性和约束,只是这些属性都不会被标记为从域继承。
示例 10-31 删除与已删除表关联的用例域
以下 DROP 命令尝试删除与 people 表的 weight_in_kg 列关联的 weight 域。
sql
DROP DOMAIN weight;
该命令返回以下错误输出:
sql
ORA-11502: The domain WEIGHT to be dropped has dependent objects.
如果删除人员表,然后删除体重域,它会返回错误,因为该表仍在回收站中。
sql
DROP TABLE people;
DROP DOMAIN weight;
该命令返回以下错误输出:
sql
ORA-11502: The domain WEIGHT to be dropped has dependent objects.
从回收站中永久删除人员表,然后在权重域上运行 DROP 命令,删除权重域。
sql
PURGE TABLE people;
DROP DOMAIN weight;
指南
删除与已删除表(回收站中的表)关联的域时,请注意以下几点:
- 当表位于回收站中时,不允许对该表执行 ALTER 命令。
- 如果与域关联的表位于回收站中,则无法删除关联的域,并且 DROP DOMAIN 命令会失败。
- 使用 DROP DOMAIN FORCE 和 DROP DOMAIN FORCE PRESERVE 命令时,回收站中的表将与域解除关联。即使您仅指定 FORCE,数据库也会对回收站中的表使用 FORCE PRESERVE 语义。
- 如果要删除与回收站中的表关联的域,可以使用 PURGE TABLE 命令从回收站中删除表,然后运行 DROP DOMAIN 命令删除该域。
使用多列用例域
本节介绍如何创建、关联、更改、取消关联和删除多列用例域。
创建多列用例域
您可以使用多列用例域对跨表列的逻辑实体(例如地址)进行分组。
示例 10-32 为地址创建多列用例域
您可以创建一个名为"US_city"的多列用例域,其中包含三列用于地址条目,如下所示:
sql
CREATE DOMAIN US_city AS (
name AS VARCHAR2(30) ANNOTATIONS (Address),
state AS VARCHAR2(2) ANNOTATIONS (Address),
zip AS NUMBER ANNOTATIONS (Address)
)
CONSTRAINT City_CK CHECK(state in ('CA','AZ','TX') and zip < 100000)
DISPLAY name||', '|| state ||', '||TO_CHAR(zip)
ORDER state||', '||TO_CHAR(zip)||', '||name
ANNOTATIONS (Title 'Domain Annotation');
示例 10-33 为货币创建多列用例域
以下代码创建一个名为"货币"的多列用例域,该域将货币值显示为给定货币的金额和货币代码。显示内容按值排序(从低到高),然后按货币代码排序。
sql
CREATE DOMAIN US_city AS (
name AS VARCHAR2(30) ANNOTATIONS (Address),
state AS VARCHAR2(2) ANNOTATIONS (Address),
zip AS NUMBER ANNOTATIONS (Address)
)
CONSTRAINT City_CK CHECK(state in ('CA','AZ','TX') and zip < 100000)
DISPLAY name||', '|| state ||', '||TO_CHAR(zip)
ORDER state||', '||TO_CHAR(zip)||', '||name
ANNOTATIONS (Title 'Domain Annotation');
示例 10-33 为货币创建多列用例域
以下代码创建一个名为"货币"的多列用例域,该域将货币值显示为给定货币的金额和货币代码。显示内容按值排序(从低到高),然后按货币代码排序。
sql
CREATE DOMAIN currency AS (
amount AS NUMBER,
iso_currency_code AS CHAR(3 CHAR)
)
DISPLAY iso_currency_code || TO_CHAR ( amount, '999,999,990.00' )
ORDER TO_CHAR ( amount, '999,999,990.00' ) || iso_currency_code;
指南
- 多列用例域中各个列的数据类型可以与单列用例域相同。
- 对于多列用例域,列不得在不同域之间重叠。例如,在表 T(TC1, TC2, TC3, TC4) 上,域 D1(C1, C2) 和 D2(C1, C2) 不能关联为 D1(TC1, TC2) 和 D2(TC2, TC3)。
- 同一表中的多个有序列子集可以与同一域关联。例如,域 D1 可以关联为 D1(TC1, TC2) 和 D1(TC3, TC4)。
- 与最多只能有一个 LONG 列的表不同,域可以有多个 LONG 数据类型的列。此类域对于使用 DOMAIN_CHECK 运算符评估涉及多个 LONG 列的检查条件非常有用。
在创建表时关联多列用例域
您可以使用 CREATE TABLE DDL 语句将多列用例域与新创建的列关联。
示例 10-34 将 US_city 域与多个列关联
您可以创建一个客户表并将 US_city 域与该表的三列关联。
sql
CREATE TABLE customer(
cust_id NUMBER,
cust_name VARCHAR2(30),
city_name VARCHAR2(30),
state VARCHAR2(2),
zip NUMBER,
DOMAIN US_city(city_name, state, zip));
以下示例返回错误,因为 CITY 和 STATE 列在域之间重叠。
sql
CREATE TABLE customer(
cust_id NUMBER,
cust_name VARCHAR2(30),
city_name VARCHAR2(30),
state VARCHAR2(2),
zip NUMBER,
DOMAIN US_city(city_name, state, zip),
DOMAIN US_city(cust_name, state, zip));
Error report -
ORA-11512: The column already has an associated domain.
以下示例也会返回错误,因为 CITY_NAME 列重复了。
sql
CREATE TABLE customer(
cust_id NUMBER,
cust_name VARCHAR2(30),
city_name VARCHAR2(30),
state VARCHAR2(2),
zip NUMBER,
DOMAIN US_city(city_name, city_name, zip));
Error report -
ORA-11512: The column already has an associated domain.
以下示例也会返回错误,因为关联的列数不对。
sql
CREATE TABLE customer(
cust_id NUMBER,
cust_name VARCHAR2(30),
city_name VARCHAR2(30),
state VARCHAR2(2),
zip NUMBER,
DOMAIN US_city(city_name),
DOMAIN US_city(state, zip));
Error report -
ORA-11515: incorrect number of columns in domain association list
示例 10-35 将货币域与多个列关联
您可以创建一个 order_items 表,其中 total_paid 和 currency_code 列与货币域关联。
sql
CREATE TABLE order_items (
order_id INTEGER, product_id INTEGER,
total_paid NUMBER(10, 2), currency_code char (3 CHAR ),
DOMAIN currency ( total_paid, currency_code ));
指南
- 作为实际参数传递给域的列名必须是唯一的。
- 域列可以与具有不同名称的表列相关联。
- DOMAIN 关键字是必需的。
在与多列域关联的列上使用 DML
以下是一些可以在新创建的表列上使用的 DML 语句示例,这些语句具有关联的多列用例域。
示例 10-36 在关联列上使用 DML 命令
插入值并查询表会根据货币域显示和订单表达式显示结果。
sql
INSERT INTO order_items
VALUES (1, 1, 9.99, 'USD'),
(2, 2, 8.99, 'GBP'),
(3, 3, 8.99, 'EUR'),
(4, 4, 1399, 'JPY'),
(5, 5, 825, 'INR');
SELECT order_id, product_id,
DOMAIN_DISPLAY ( total_paid, currency_code ) amount_paid
FROM order_items
ORDER BY DOMAIN_ORDER ( total_paid, currency_code );
ORDER_ID PRODUCT_ID AMOUNT_PAID
---------- ---------- ------------------
3 3 EUR 8.99
2 2 GBP 8.99
1 1 USD 9.99
5 5 INR 825.00
4 4 JPY 1,399.00
将多列用例域与现有列关联
您可以使用带有 MODIFY 或 ADD 子句的 ALTER TABLE DDL 语句将多列用例域与现有列或现有表中新添加的列关联。
示例 10-37 将多列用例域与现有列关联
以下示例将 US_city 域应用于客户表的三列。
sql
ALTER TABLE customer
MODIFY (city_name, state, zip) ADD DOMAIN US_city;
注意:DOMAIN 关键字对于 ALTER TABLE...MODIFY 语句是必需的。
更改多列用例域
您可以像更改单列用例域一样更改多列用例域。在多列用例域中,您可以更改 DISPLAY 和 ORDER 属性。对于多列域,目前不支持在列级别更改注释,但您可以更改对象级别注释。
示例 10-38 更改多列用例域的显示和顺序表达式
以下 ALTER 语句更改货币域的显示表达式。当前显示表达式显示货币代码,然后显示货币值。更改后的显示表达式显示货币值,然后显示货币代码。
sql
ALTER DOMAIN currency
MODIFY DISPLAY TO_CHAR ( amount, '999,990.00' ) || '-' || iso_currency_code;
以下 ALTER 语句更改货币域的顺序表达式。当前顺序表达式先按货币值排序,然后按货币代码排序。更改后的顺序表达式先按货币代码排序,然后按货币值排序。
sql
ALTER DOMAIN currency
MODIFY ORDER iso_currency_code || TO_CHAR ( amount, '999,990.00' );
示例 10-39 查询与更改的多列域关联的表
sql
SELECT order_id, product_id,
DOMAIN_DISPLAY ( total_paid, currency_code ) amount_paid
FROM order_items
ORDER BY DOMAIN_ORDER ( total_paid, currency_code );
ORDER_ID PRODUCT_ID AMOUNT_PAID
----------- ------------- ---------------
3 3 8.99-EUR
2 2 8.99-GBP
5 5 825.00-INR
4 4 1,399.00-JPY
1 1 9.99-USD
将多列用例域与列解除关联
您可以使用带有 DROP 子句的 ALTER TABLE DDL 语句来解除多列用例域与列的关联。
示例 10-40 解除多列用例域关联的示例
以下 ALTER TABLE 命令从 customer 表的 city_name、state 和 zip 列中删除 US_City 域。
sql
ALTER TABLE customer
MODIFY(city_name, state, zip) DROP DOMAIN;
如果具有列 (c1, c2, c3) 的表 T 与域 D 关联,并且另一组列 (c4, c5, c6) 也与域 D 关联,则可以删除所有列的域:
sql
ALTER TABLE T
MODIFY (c1, c2, c6, c5, c4, c3) DROP DOMAIN;
您不能仅删除与多列域关联的列的子集。例如,对于表 T,仅删除 c1 和 c2 列将返回错误:
sql
ALTER TABLE T
MODIFY (c1, c2) DROP DOMAIN;
示例 10-41 解除多列用例域关联的更多示例
以下代码从 order_items 表的 total_paid 和 currency_code 列中删除货币域。
sql
ALTER TABLE order_items
MODIFY ( total_paid, currency_code ) DROP DOMAIN;
指南
- 同一个表中可以有多个有序的列子集与同一个域相关联。删除多列域语法必须指定要分离的关联列的列表。
- 无法指定域名。
- 您不能为 ALTER TABLE ...MODIFY 和 ALTER TABLE ...DROP DOMAIN 指定其他选项。
删除多列用例域
要删除多列用例域,请使用与单列用例域相同的语法。
指南
- 要删除灵活域中引用的域,请使用带有 FORCE 选项的 DROP DOMAIN。
使用灵活用例域
本节介绍如何创建、关联、取消关联和删除灵活用例域。
注意:您无法更改灵活用例域,但作为替代方案,您可以取消灵活域与表的关联,删除域,重新创建域,然后将其与表重新关联。
创建灵活用例域
您可以创建一个灵活的用例域,该域引用其他非灵活域(单列和多列用例域),并允许您根据数据的上下文将其中一个域应用于表列。例如,您可以创建多列域来验证每个国家/地区的地址格式。表列只能属于一个域。因此,为了使数据库能够为每行使用与每个国家/地区相对应的地址域,请在国家/地区域上创建一个灵活域。使用国家/地区作为判别列将列与灵活域关联。然后,每行都可以应用相应国家/地区域的地址规则。
示例 10-42 为温度读数创建灵活的用例域
以下代码从三个域(即摄氏、华氏和开尔文)创建一个名为温度的灵活用例域。为每个温标创建的域都有适当的绝对零度检查和显示表达式。灵活域中没有 ELSE 子句,因此您可以插入其他温度单位的值,并且此类温度单位的温度值不受约束。
sql
CREATE DOMAIN celcius AS NUMBER
CONSTRAINT abs_zero_c_c CHECK ( celcius >= -273.15 )
DISPLAY celcius || ' °C';
CREATE DOMAIN fahrenheit AS NUMBER
CONSTRAINT abs_zero_f_c CHECK ( fahrenheit >= -459.67 )
DISPLAY fahrenheit || ' °F';
CREATE DOMAIN kelvin AS NUMBER
CONSTRAINT abs_zero_k_c CHECK ( kelvin >= 0 )
DISPLAY kelvin || ' K';
以下代码创建了一个灵活的域,该域根据温度单位选择使用哪个域。
sql
CREATE FLEXIBLE DOMAIN temperature (
temp
) CHOOSE DOMAIN USING ( units char(1) )
FROM (
CASE units
WHEN 'C' THEN celcius ( temp )
WHEN 'F' THEN fahrenheit ( temp )
WHEN 'K' THEN kelvin ( temp )
END);
示例 10-43 为地址创建灵活的用例域
以下代码创建多列用例域来表示美国和英国的地址,以及其他国家/地区的默认地址域。
sql
/* US addresses */
CREATE DOMAIN us_address AS (
line_1 AS VARCHAR2(255 CHAR) NOT NULL,
town AS VARCHAR2(255 CHAR) NOT NULL,
state AS VARCHAR2(255 CHAR) NOT NULL,
zipcode AS VARCHAR2(10 CHAR) NOT NULL
) CONSTRAINT us_address_c check (
REGEXP_LIKE ( zipcode, '^[0-9]{5}(-[0-9]{4}){0,1}$' ));
/* British addresses */
CREATE DOMAIN gb_address AS (
street AS VARCHAR2(255 CHAR) NOT NULL,
locality AS VARCHAR2(255 CHAR),
town AS VARCHAR2(255 CHAR) NOT NULL,
postcode AS VARCHAR2(10 CHAR) NOT NULL
) CONSTRAINT gb_postcode_c check (
REGEXP_LIKE (
postcode, '^[A-Z]{1,2}[0-9][A-Z]{0,1} [0-9][A-Z]{2}$' ));
/* Default address */
/* 原文中代码执行不成功,改成以下就成功了 */
CREATE DOMAIN global_address AS (
line_1 AS VARCHAR2(255 CHAR) NOT NULL,
line_2 AS VARCHAR2(255 CHAR),
line_3 AS VARCHAR2(255 CHAR) NOT NULL,
line_4 AS VARCHAR2(255),
postcode AS VARCHAR2(10 CHAR) NOT NULL);
以下代码创建了一个灵活的域,该域根据国家代码选择使用哪个多列地址域。
sql
CREATE FLEXIBLE DOMAIN address (
line_1, line_2, line_3, line_4,
postal_code
)
CHOOSE DOMAIN USING ( country_code VARCHAR2(2 char) )
FROM (
CASE country_code
WHEN 'GB' THEN gb_address ( line_1, line_2, line_3, postal_code )
WHEN 'US' THEN us_address ( line_1, line_2, line_3, postal_code )
ELSE global_address ( line_1, line_2, line_3, line_4, postal_code )
END);
注意:要创建灵活域,您必须对每个组成域拥有 EXECUTE 权限。
在创建表时关联灵活用例域
您可以使用 CREATE TABLE DDL 将灵活用例域与新表新创建的一组列关联起来。要将灵活域添加到一组列中,请指定要与域关联的列列表(按域列顺序),然后指定要用作判别式的列列表(按灵活域中的判别式列顺序)。
示例 10-44 将温度灵活域与新表列关联起来
使用温度灵活域创建 sensor_readings 表,同时使用 USING 关键字指定判别式列。
sql
CREATE TABLE sensor_readings (
sensor_id integer, reading_timestamp TIMESTAMP,
temperature_reading NUMBER,
temperature_units CHAR(1 CHAR),
DOMAIN temperature ( temperature_reading )
USING ( temperature_units ));
示例 10-45 将地址灵活域与新表列关联
以下代码创建一个名为地址的新表并将其列与地址灵活域关联。
sql
CREATE TABLE addresses (
line_1 VARCHAR2(255) NOT NULL,
line_2 VARCHAR2(255),
line_3 VARCHAR2(255),
line_4 VARCHAR2(255),
country_code VARCHAR2(2 CHAR) NOT NULL,
postal_code VARCHAR2(10 CHAR),
DOMAIN address (
line_1, line_2, line_3, line_4, postal_code)
USING ( country_code ));
注意:关联灵活域时,DOMAIN 和 USING 关键字是必需的。
在与灵活域关联的列上使用 DML
以下是对具有关联灵活用例域的新创建的表列执行 DML 语句的一些示例。
示例 10-46 对与温度灵活域关联的列使用 DML 命令
sql
INSERT INTO sensor_readings
VALUES ( 1, timestamp'2023-06-08 12:00:00', 21.1, 'C' ),
( 1, timestamp'2023-06-08 12:05:00', 21.2, 'C' ),
( 1, timestamp'2023-06-08 12:10:00', 20.9, 'C' ),
( 2, timestamp'2023-06-08 12:00:00', 68.5, 'F' ),
( 2, timestamp'2023-06-08 12:05:00', 68.1, 'F' ),
( 2, timestamp'2023-06-08 12:10:00', 68.9, 'F' ),
( 3, timestamp'2023-06-08 12:00:00', 290.23, 'K' ),
( 3, timestamp'2023-06-08 12:05:00', 289.96, 'K' ),
( 3, timestamp'2023-06-08 12:10:00', 289.65, 'K' ),
( 4, timestamp'2023-06-08 12:00:00', 528.15, 'R' ),
( 4, timestamp'2023-06-08 12:05:00', 528.42, 'R' ),
( 4, timestamp'2023-06-08 12:10:00', 527.99, 'R' );
SELECT sensor_id, reading_timestamp,
DOMAIN_DISPLAY ( temperature_reading, temperature_units ) temp
FROM sensor_readings;
SENSOR_ID READING_TIMESTAMP TEMP
---------- ------------------------------ -------------------------------------------
1 08-JUN-2023 12.00.00.000000000 21.1 °C
1 08-JUN-2023 12.05.00.000000000 21.2 °C
1 08-JUN-2023 12.10.00.000000000 20.9 °C
2 08-JUN-2023 12.00.00.000000000 68.5 °F
2 08-JUN-2023 12.05.00.000000000 68.1 °F
2 08-JUN-2023 12.10.00.000000000 68.9 °F
3 08-JUN-2023 12.00.00.000000000 290.23 K
3 08-JUN-2023 12.05.00.000000000 289.96 K
3 08-JUN-2023 12.10.00.000000000 289.65 K
4 08-JUN-2023 12.00.00.000000000 <null>
4 08-JUN-2023 12.05.00.000000000 <null>
4 08-JUN-2023 12.10.00.000000000 <null>
示例 10-47 越界约束错误
以下值是其各自温度标度的越界约束错误。
sql
INSERT INTO sensor_readings
VALUES ( 1, timestamp'2023-06-08 12:15:00', -400, 'C' );
INSERT INTO sensor_readings
VALUES ( 2, timestamp'2023-06-08 12:15:00', -999, 'F' );
INSERT INTO sensor_readings
VALUES ( 3, timestamp'2023-06-08 12:15:00', -1, 'K' );
输出为:
sql
Error starting at line : 2 in command -
INSERT INTO sensor_readings
VALUES ( 1, timestamp'2023-06-08 12:15:00', -400, 'C' )
Error report -
ORA-11534: check constraint (SSB.SYS_C0010374) involving columns TEMPERATURE_READING, TEMPERATURE_UNITS due to domain constraint SSB.SYS_DOMAIN_C0099 of domain SSB.TEMPERATURE violated
Error starting at line : 5 in command -
INSERT INTO sensor_readings
VALUES ( 2, timestamp'2023-06-08 12:15:00', -999, 'F' )
Error report -
ORA-11534: check constraint (SSB.SYS_C0010375) involving columns TEMPERATURE_READING, TEMPERATURE_UNITS due to domain constraint SSB.SYS_DOMAIN_C0098 of domain SSB.TEMPERATURE violated
Error starting at line : 8 in command -
INSERT INTO sensor_readings
VALUES ( 3, timestamp'2023-06-08 12:15:00', -1, 'K' )
Error report -
ORA-11534: check constraint (SSB.SYS_C0010376) involving columns TEMPERATURE_READING, TEMPERATURE_UNITS due to domain constraint SSB.SYS_DOMAIN_C0097 of domain SSB.TEMPERATURE violated
示例 10-48 在与地址灵活域关联的列上使用 DML
sql
-- Great Britian
INSERT INTO addresses ( line_1, line_3, country_code, postal_code )
VALUES ( '10 Big street', 'London', 'GB', 'N1 2LA' );
-- United States
INSERT INTO addresses ( line_1, line_2, line_3, country_code, postal_code )
VALUES ( '10 another road', 'Las Vegas', 'NV', 'US', '87654-3210' );
-- Tuvalu 这条记录插入失败
INSERT INTO addresses ( line_1, country_code )
VALUES ( '10 Main street', 'TV' );
SELECT * FROM addresses;
LINE_1 LINE_2 LINE_3 LINE_4 COUNTRY_CODE POSTAL_CODE
------------------ ------------ --------- --------- --------------- -----------
10 Big street <null> London <null> GB N1 2LA
10 another road Las Vegas NV <null> US 87654-3210
10 Main street <null> <null> <null> TV <null>
以下 INSERT 命令返回错误,因为它尝试插入带有美国邮政编码的英国地址。
sql
INSERT INTO addresses ( line_1, line_3, country_code, postal_code )
VALUES ( '10 Big street', 'London', 'GB', '12345-6789' );
ORA-11534: check constraint (schema.SYS_C0010286) due to domain constraint schema.SYS_DOMAIN_C00639 of domain schema.ADDRESS violated
以下 INSERT 命令返回错误,因为它尝试插入没有州值的美国地址。
sql
INSERT INTO addresses ( line_1, line_2, country_code, postal_code )
VALUES ( '10 another road', 'Las Vegas', 'US', '87654-3210' );
ORA-11534: check constraint (schema.SYS_C0010289) due to domain constraint schema.SYS_DOMAIN_C00636 of domain schema.ADDRESS violated
将灵活域与现有列关联
您可以使用带有 MODIFY 或 ADD 子句的 ALTER TABLE DDL 将灵活用例域与现有列或现有表中新添加的列关联。
示例 10-49
以下代码创建一个名为 temp_sensor_readings 的新表。
sql
CREATE TABLE temp_sensor_readings (
sensor_id integer, reading_timestamp TIMESTAMP,
temperature_reading NUMBER,
temperature_units CHAR(1 CHAR));
以下代码将温度灵活域与名为temperature_reading的现有列关联。
sql
ALTER TABLE temp_sensor_readings
MODIFY (temperature_reading, temperature_units)
ADD DOMAIN temperature;
指南
- 关联灵活域时,DOMAIN 关键字是必需的。
- USING 关键字对于 ALTER TABLE ... ADD 语句是必需的。
- 您不能将同一列与多个灵活域关联,无论是作为域列还是作为判别列。
- 您不能将列与同一域关联,但列定位不同。
将灵活用例域与列解除关联
您可以使用带有 DROP 子句的 ALTER TABLE DDL 来解除灵活用例域与列的关联。
示例 10-50
以下代码从 temp_sensor_readings 表中删除温度域。
sql
ALTER TABLE temp_sensor_readings
MODIFY (temperature_reading, temperature_units) DROP DOMAIN;
指南
- 域名不是必需的,因为数据库知道哪些列与哪些域相关联,并且一个列只能与一个域相关联。
- 您不能为 ALTER TABLE ...MODIFY 和 ALTER TABLE ...DROP DOMAIN 指定其他选项。
删除灵活用例域
要删除灵活用例域,请使用与单列用例域相同的语法。
指南
- 要删除灵活域中引用的域,请使用带有 FORCE 选项的 DROP DOMAIN。执行此操作也会删除灵活域。
- 要在 FORCE 模式下删除灵活域,您必须具有删除组成灵活域的权限。
使用枚举用例域
本节介绍如何创建和使用枚举用例域(枚举域)。
关于枚举类型
枚举类型(也称为 ENUM 或枚举)是一种由一组有序值组成的数据类型(key是字符型,value可以是数值型或字符型)。作为语言构造,ENUM 可用于定义数据输入字段的一组固定的允许文字(命名值)。根据您的应用程序要求,您可以在创建表时明确定义列规范中的有效值。枚举的一些很好的例子是日期和月份,或北、南、东和西等方向。
枚举域概述
从版本 23ai(版本 23.4)开始,Oracle 数据库支持将枚举类型用于域。
作为枚举类型创建的域称为枚举域。因此,枚举域定义了可以存储在列中的预定义值列表,例如客户订单的可能状态列表:打开、待处理、已发货和已交付。创建枚举域后,您可以将域用作表列的数据类型。枚举域简化了在 Oracle SQL 中向表添加枚举类型列的过程。
以下是创建和使用枚举域的一般规则和指南。
- 枚举域包含一组名称,以及与名称对应的值(可选)。
- 枚举中的名称必须是有效的 SQL 标识符。
- 名称是普通的 Oracle SQL 标识符,因此必须遵守适用于 Oracle SQL 中有效标识符的所有限制。
- 每个指定的值都必须是文字。
- 该值可以是用例域支持的任何数据类型,但域中使用的所有值都必须是相同的数据类型。
- 如果未指定值,则第一个名称的值为 1。每个后续名称的值都比前一个名称的值大一。
- 您可以将多个名称与每个值关联。
- 可以将名称用双引号引起来以绕过关键字限制、使其区分大小写或两者兼而有之。
- 枚举域中的名称可用于标量 SQL 表达式中允许使用文字的任何地方。
- 枚举域可以像任何其他单列域一样使用。但是,与常规域不同,枚举域具有默认检查约束和显示表达式。
- 枚举域可以在 SELECT 语句的 FROM 子句中使用,就像它是一个表一样。
创建枚举域
要创建枚举域,请使用 CREATE DOMAIN AS ENUM 命令。
以下示例说明了如何为各种用例创建枚举域。
为订单状态创建枚举域
以下代码创建一个仅包含一组名称的 order_status 域。域中每个名称的值如下:New = 1、Open = 2、Shipped = 3、Closed = 4 和 Canceled = 5。
sql
CREATE DOMAIN order_status AS
ENUM (
New,
Open,
Shipped,
Closed,
Canceled
);
创建带有双引号名称的枚举域
与普通标识符类似,枚举名称可以用双引号括起来以绕过关键字限制、使其区分大小写或两者兼而有之。以下示例创建了一个名为 CRUD 的枚举域,其中 CRUD 函数作为名称,但使用双引号可使名称绕过关键字限制。
sql
CREATE DOMAIN CRUD AS
ENUM ( "Create",
"Read",
"Update",
"Delete"
);
为状态代码创建枚举域
您还可以创建一个枚举域,并为名称分配值。以下名为 Status_Code 的域为每个状态分配了一个状态代码值。
sql
CREATE DOMAIN Status_Code AS
ENUM (
OK = 200,
Not_Found = 404,
Internal_Error = 500
);
创建一周中各天的枚举域
以下代码为一周中的所有天创建一个名为 Days_Of_Week 的枚举域。域中每个名称的值如下:0 表示星期日和星期日,1 表示星期一和星期一,2 表示星期二和星期二,3 表示星期三和星期三,4 表示星期四和星期四,5 表示星期五和星期五,6 表示星期六和星期六。因此,每个值都与两个名称相关联。
sql
CREATE DOMAIN Days_Of_Week AS
ENUM (
Sunday = Su = 0,
Monday = Mo,
Tuesday = Tu,
Wednesday = We,
Thursday = Th,
Friday = Fr,
Saturday = Sa
);
为职位创建枚举域
以下代码为组织中可能的职位创建枚举域。
sql
CREATE DOMAIN Job_Title AS
ENUM (
Clerk = 'CLERK',
Salesman = Salesperson = 'SALESMAN',
Manager = 'MANAGER',
Analyst = 'ANALYST',
President = 'PRESIDENT'
);
为美国节假日创建枚举域
值也可以是文字或常量表达式(因此可以将其作为 CREATE DOMAIN DDL 的一部分进行评估)。以下示例使用评估表达式为美国节假日创建枚举域。
sql
CREATE DOMAIN US_Holidays_2023 AS
ENUM (
"New Years Day" = to_date('Jan 2, 2023', 'Mon DD, YYYY'),
"MLK Jr. Day" = to_date('Jan 16, 2023', 'Mon DD, YYYY'),
"Presidents Day" = to_date('Feb 20, 2023', 'Mon DD, YYYY'),
"Memorial Day" = to_date('May 29, 2023', 'Mon DD, YYYY'),
"Juneteenth" = to_date('Jun 19, 2023', 'Mon DD, YYYY'),
"Independence Day" = to_date('Jul 4, 2023', 'Mon DD, YYYY'),
"Labor Day" = to_date('Sep 4, 2023', 'Mon DD, YYYY'),
"Columbus Day" = to_date('Sep 4, 2023', 'Mon DD, YYYY'),
"Veterans Day" = to_date('Nov 11, 2023', 'Mon DD, YYYY'),
"Thanksgiving Day" = to_date('Nov 23, 2023', 'Mon DD, YYYY'),
"Christmas Day" = to_date('Dec 25, 2023', 'Mon DD, YYYY')
);
为医疗保健应用程序创建枚举域
以下是医疗保健应用程序可能使用的枚举域的示例:
sql
CREATE DOMAIN Blood_Group AS
ENUM (
A_Positive = A_Pos = 'A +',
A_Negative = A_Neg = 'A -',
B_Positive = B_Pos = 'B +',
B_Negative = B_Neg = 'B -',
AB_Positive = AB_Pos = 'AB +',
AB_Negative = AB_Neg = 'AB -',
O_Positive = O_Pos = 'O +',
O_Negative = O_Neg = 'O -'
);
以下是医疗保健应用中枚举域的更多用例。
- 成像方式:X 射线、MRI、CT、超声波。
- 处方频率:每日一次、每日两次、每日三次、按需。
- 患者状况:稳定、危急、恢复中、晚期。
- 过敏类型:食物、药物、环境、昆虫、动物、其他。
- BMI 类别:体重过轻、正常、超重、肥胖。
在创建表时关联枚举域
与单列用例域类似,您可以在创建表时使用枚举域作为表列的数据类型。以下示例创建了一个订单表,并将 order_status 枚举域(先前创建)与 status 列关联。
sql
CREATE TABLE orders(
id NUMBER,
cust VARCHAR2(100),
status order_status
);
描述订单表表明,状态列实际上是具有单列域的数字列。实际值以表示订单状态的数字形式存储在状态列中。
sql
-- SCOTT为schema的名字
DESCRIBE orders;
Name Null? Type
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ID NUMBER
CUST VARCHAR2(100)
STATUS NUMBER SCOTT.ORDER_STATUS
在与枚举域关联的列上使用 DML
以下是对与枚举域关联的表列发出的一些 DML 语句示例。
对订单表使用 INSERT 命令
要将数据插入订单表,请使用适当的订单状态构造每一行。
sql
INSERT INTO orders VALUES
(1, 'Costco', order_status.open),
(2, 'BMW', order_status.closed),
(3, 'Nestle', order_status.open);
您可以列出这些行,但要使用相应的枚举名称查看枚举值,您可以使用域提供的标准机制,即 DOMAIN_DISPLAY() 函数:
sql
SELECT id, DOMAIN_DISPLAY(status) status FROM orders;
ID STATUS
- - - - - - - - - - - - - - - - - -
1 OPEN
2 CLOSED
3 OPEN
SELECT id, status FROM orders;
ID STATUS
- - - - - - - - - - - - -
1 2
2 4
3 2
在订单表上使用 UPDATE 命令
与插入一样,枚举名称也可以在其他 DML 语句中使用,例如 UPDATE 语句。
以下示例更新订单表中的行,将状态从"已关闭"更改为"已取消",将"打开"更改为"已关闭"。
sql
-- 原代码有误,calcelled 改为 calceled
UPDATE orders
SET status = CASE status
WHEN order_status.closed
THEN order_status.canceled
WHEN order_status.open
THEN order_status.closed
END
WHERE status IN(order_status.closed, order_status.open);
要验证更新是否按预期进行,请再次使用带有 DOMAIN_DISPLAY 函数的 SELECT 命令。
sql
SELECT id, DOMAIN_DISPLAY(status) status FROM orders;
ID STATUS
- - - - - - - - - - - - - - - - - -
1 CLOSED
2 CANCELLED
3 CLOSED
由于状态列的底层数据类型只是一个数字,因此您也可以直接使用任何数值来更新状态。
sql
UPDATE orders SET status = 2 WHERE status = 5;
更新订单设置状态=2其中状态=5;
sql
UPDATE orders SET status = -7;
ERROR at line 1:
ORA-11534: check constraint (SSB.SYS_C0010424) involving column STATUS due to
domain constraint SSB.SYS_DOMAIN_C00100 of domain SSB.ORDER_STATUS violated
Help: https://docs.oracle.com/error-help/db/ora-11534/
因为枚举名称只是文字值的占位符,所以它们可以在 SQL 允许文字的任何地方使用:
sql
SELECT 2*order_status.canceled;
2*ORDER_STATUS.CANCELLED
- - - - - - - - - - - -
10
在枚举域上使用 SELECT 命令
与常规域不同,枚举域可以视为表并使用 SELECT 语句进行查询。
sql
SELECT * FROM order_status;
ENUM_NAM ENUM_VALUE
-------- ----------
NEW 1
OPEN 2
SHIPPED 3
CLOSED 4
CANCELED 5
但是,与常规域一样,枚举域不能成为 DML 语句的目标。
sql
UPDATE order_status SET value = 4;
UPDATE order_status SET value = 4
*
ERROR at line 1:
ORA-11563: Domain is not allowed here
Help: https://docs.oracle.com/error-help/db/ora-11563/
将枚举域与现有列关联
与常规域类似,可以使用 ALTER-TABLE-MODIFY 命令将枚举域添加到现有表的列中。以下代码假设 emp 是一个包含 job、deptno 和 comm 列的现有表。
sql
ALTER TABLE emp
MODIFY(job)
ADD DOMAIN Job_Title;
您可以继续使用标准 SQL 语句,如下所示:
sql
UPDATE emp
SET job = 'MANAGER'
WHERE deptno = 20;
此外,您还可以在 SQL 语句中明确使用枚举,如下所示:
sql
UPDATE emp
SET job = Job_Title.Salesperson
WHERE comm IS NOT NULL;
为域指定数据类型
域数据类型可以是 Oracle 数据类型之一。但是,限定域名不得与限定的用户定义数据类型或 Oracle 内置类型冲突。
如果列与域相关联,并且未指定列数据类型,则域数据类型将用作关联列的默认数据类型。如果关联列已有数据类型,则使用该列的数据类型。
如果域数据类型定义为非严格域,则关联列的数据类型只需与域数据类型兼容,这意味着它们的数据类型必须相同,但长度、精度和小数位数可以不同。例如,对于非严格域,您可以将 VARCHAR2(10) 域与任何 VARCHAR2 列关联。
如果域数据类型定义为严格域,则关联列的数据类型必须与域数据类型兼容,并且还必须与域数据类型的长度、精度和小数位数匹配。
示例 10-51 将列与域数据类型关联
以下示例创建 year_of_birth 域和 email_dom 域,并将域与列关联以显示域和列数据类型的兼容性。
sql
DROP DOMAIN IF EXISTS year_of_birth;
CREATE DOMAIN year_of_birth AS NUMBER(4)
CONSTRAINT CHECK ( (TRUNC(year_of_birth) = year_of_birth) AND (year_of_birth >= 1900) );
DROP DOMAIN IF EXISTS email_dom;
CREATE DOMAIN email_dom AS VARCHAR2(100)
CONSTRAINT email_chk check (REGEXP_LIKE (email_dom, '^(\S+)\@(\S+)\.(\S+)$'));
DROP TABLE IF EXISTS newcustomers PURGE;
CREATE TABLE newcustomers (
cust_Id NUMBER,
cust_year_of_birth NUMBER(6) DOMAIN year_of_birth,
cust_hq_email VARCHAR2(50),
cust_office_email VARCHAR2(100),
cust_rep_email VARCHAR2(100));
DESC newcustomers;
输出为:
sql
Name Null? Type
----------------------------- -------- ----------------------------
CUST_ID NUMBER
CUST_YEAR_OF_BIRTH NUMBER(6) SH.YEAR_OF_BIRTH
CUST_HQ_EMAIL VARCHAR2(50)
CUST_OFFICE_EMAIL VARCHAR2(100)
CUST_REP_EMAIL VARCHAR2(200)
cust_year_of_birth 列定义为 Oracle 数据类型:Number,并且还与 year_of_birth 域相关联,因此该列的数据类型被分配给该列。cust_year_of_birth 列继承了 year_of_birth 域中定义的所有属性,例如约束、显示和排序属性。
以下示例创建一个 ukcustomers 表,其中包含与 year_of_birth 域相关联的列,但没有该列的数据类型:
sql
CREATE TABLE ukcustomers (
cust_Id NUMBER,
cust_year_of_birth DOMAIN year_of_birth);
DESC ukcustomers;
输出为:
sql
Name Null? Type
------------------------------ -------- ----------------------------
CUST_ID NUMBER
CUST_YEAR_OF_BIRTH NUMBER(4) SH.YEAR_OF_BIRTH
这里,cust_year_of_birth 列被分配了域的数据类型,即 NUMBER(4)。
在以下示例中,省略了 DOMAIN 关键字。
sql
CREATE TABLE incustomers (
cust_id NUMBER,
cust_year_of_birth year_of_birth);
DESC incustomers;
输出为:
sql
Name Null? Type
----------------------------- -------- ----------------------------
CUST_ID NUMBER
CUST_YEAR_OF_BIRTH NUMBER(4) SH.YEAR_OF_BIRTH
在列定义子句中,域子句必须替换数据类型子句,或紧跟其后。
如果域列数据类型未定义为 STRICT,则可以将域与具有相同数据类型的任何列关联,而不管列长度如何。
以下 ALTER 命令成功,因为域和列数据类型具有相同的数据类型,并且不会检查非 STRICT 域的列长度。
sql
ALTER TABLE newcustomers
MODIFY cust_hq_email DOMAIN email_dom;
ALTER TABLE newcustomers
MODIFY cust_office_email DOMAIN email_dom;
ALTER TABLE newcustomers
MODIFY cust_rep_email DOMAIN email_dom;
如果域列数据类型定义为 STRICT,则仅当列和域具有相同的数据类型且长度也匹配时,域关联才有效。
sql
DROP DOMAIN IF EXISTS email_dom;
CREATE DOMAIN email_dom AS VARCHAR2(100) STRICT
CONSTRAINT email_chk check (REGEXP_LIKE (email_dom, '^(\S+)\@(\S+)\.(\S+)$'));
以下 ALTER 命令成功。
sql
ALTER TABLE newcustomers
MODIFY cust_office_email DOMAIN email_dom;
以下 ALTER 命令失败,因为列长度和域长度不匹配。
sql
ALTER TABLE newcustomers
MODIFY cust_hq_email DOMAIN email_dom;
ALTER TABLE newcustomers
MODIFY cust_rep_email DOMAIN email_dom;
表列数据类型与非 STRICT 域列数据类型兼容性
以下各点列出了兼容类型。您可以将任何表列与具有兼容类型的域列关联。对于非 STRICT 域,表列和域列可以具有不同的长度、精度和比例。
- NUMBER, NUMER§, NUMBER(p, s), NUMERIC, NUMERIC§, NUMERIC(p, s), DECIMAL, DECIMAL§, DEC, DEC§, INTEGER, INT, SMALLINT, FLOAT, FLOAT§, REAL, DOUBLE_PRECISION
- CHAR(L), CHAR(L CHAR), CHAR(L BYTE), CHARACTER(L CHAR), CHARACTER(L BYTE), CHARACTER(L)
- NCHAR(L), NATIONAL CHARACTER(L), NATIONAL CHAR (L)
- VARCHAR2(L), VARCHAR2(L CHAR), VARCHAR2(L BYTE), CHAR VARYING(L CHAR), CHAR VARYING(L BYTE), CHAR VARYING(L), CHARACTER VARYING(L CHAR), CHARACTER VARYING(L BYTE), CHARACTER VARYING(L)
- NVARCHAR2(L), NATIONAL CHAR VARYING (L), NATIONAL CHARACTER VARYING (L)
- TIMESTAMP, TIMESTAMP§
- TIMESTAMP WITH TIME ZONE, TIMESTAMP§ WITH TIME ZONE
- TIMESTAMP WITH LOCAL TIME ZONE, TIMESTAMP§ WITH LOCAL TIME ZONE
- INTERVAL YEAR TO MONTH, INTERVAL YEAR§ TO MONTH
- INTERVAL DAY TO SECOND, INTERVAL DAY§ TO SECOND, INTERVAL DAY TO SECOND(s), INTERVAL DAY§ TO SECOND(s)
- ROWID, UROWID, UROWID§
- RAW§, RAW§
表列数据类型与 STRICT 域列数据类型兼容性
以下各点列出了兼容类型。您可以将任何表列与具有兼容类型的域列关联。对于 STRICT 域,表列和域列的长度、精度和小数位数必须完全匹配。
NUMBER(*), NUMBER
NUMBER(p, 0), NUMERIC§, NUMERIC(p, 0), DECIMAL§, DEC§, provided the table column data type and the domain column data type have the same precision.
- NUMBER(p, s), NUMERIC(p, s), DECIMAL(p, s), DEC(p, s),前提是表列数据类型和域列数据类型具有相同的精度和小数位数。
- NUMBER(,0), NUMERIC( ), NUMERIC(,0), DEC( ), DEC(,0), DECIMAL(), DECIMAL(*,0), INTEGER, INT, SMALLINT
- FLOAT(63), REAL
- FLOAT, FLOAT(126), DOUBLE PRECISION
- CHAR(L CHAR), CHAR(L BYTE), CHARACTER (L),前提是列数据类型和域列数据类型的大小(以字节为单位)相同。例如,如果 1 个字符占用 4 个字节,则 CHAR (4 BYTE) 可以与 CHAR(1 CHAR) 的 STRICT 域列相关联。
- NCHAR(L), NATIONAL CHARACTER(L), NATIONAL CHAR (L),前提是列数据类型和域列数据类型的大小(以字节为单位)相同。
- VARCHAR2(L CHAR), VARCHAR2(L BYTE), CHARACTER VARYING (L), CHAR VARYING(L),前提是列数据类型和域列数据类型的大小(以字节为单位)相同。
- NVARCHAR2(L), NATIONAL CHAR VARYING (L), NATIONAL CHARACTER VARYING (L),前提是列数据类型和域列数据类型的大小(以字节为单位)相同。
- TIMESTAMP, TIMESTAMP(6)
- TIMESTAMP WITH TIME ZONE, TIMESTAMP(6) WITH TIME ZONE
- TIMESTAMP WITH LOCAL TIME ZONE, TIMESTAMP(6) WITH LOCAL TIME ZONE
- INTERVAL YEAR TO MONTH, INTERVAL YEAR(2) TO MONTH
- INTERVAL DAY TO SECOND, INTERVAL DAY(2) TO SECOND, INTERVAL DAY TO SECOND(6), INTERVAL DAY(2) TO SECOND(6)
- ROWID, UROWID(10)
- UROWID, UROWID(4000), ROWID(4000)
表列数据类型与域列数据类型兼容性
以下列数据类型仅与等效域列数据类型兼容。例如,DATE 列类型只能与 DATE 域列类型关联。
- BINARY_FLOAT
- BINARY_DOUBLE
- DATE
- BLOB
- CLOB
- NCLOB
- BFILE
- LONG
- LONG RAW
- JSON
根据数据类型关联表列和用例域的规则
对于域的数据类型 VARCHAR2(L [CHAR|BYTE]),假设国家语言支持 (NLS) 设置的 NLS_LENGTH_SEMANTICS 中的会话级长度语义值为 BYTE,则让 L 字节为与 L 对应的最大字节长度。
将列与域关联时适用以下规则:
- 如果域定义为非 STRICT,则域可以与任何 x 字节的数据类型 VARCHAR2(x) 列关联。对于非 STRICT 域,L 和 x 可以不同。
- 如果域定义为 STRICT,则域可以与任何 x 字节 = L 字节的数据类型 VARCHAR2(x) 列关联。对于 STRICT 域,L 和 x 必须是相同的字节数,即使在将 L|x CHAR 转换为 BYTES 之后也是如此(如果需要)。
例如,如果域数据类型规范为 VARCHAR2 STRICT,并且 MAX_STRING_SIZE 为 STANDARD,则该域可以与 VARCHAR2(L BYTE) 数据类型的列关联,其中 L = 4000。如果会话中的当前 NLS 设置是最多需要 2 个字节来表示一个字符,则可以将 VARCHAR2(2000 CHAR) 数据类型的列与该域关联。如果 MAX_STRING_SEMANTICS 更改为 EXTENDED,则可以将数据类型为 VARCHAR2(L BYTE)(L = 32767)或 VARCHAR2(16383 CHAR) 的列与该域关联。
类似规则适用于 NVARCHAR2(L)、CHAR(L [CHAR|BYTE]) 和 NCHAR(L)。
更改用例域属性
随着应用程序的发展,列定义可能需要更改,并且相关用例域的属性也必须更改。**在更改域时,您只能更改域的注释、显示表达式和顺序表达式。**以下建议了更改其他域属性的方法。
您可以使用这些示例作为入门方法,也可以基于这些示例进行构建并适应您的特定数据和业务需求。第一个示例是手动方法,下一个示例是使用 dbms_redefinition 包的在线方法。
示例 10-52 手动更改域属性
假设您要将电子邮件域字段的字符串长度从 100 更改为 200。要进行该更改,您必须完成以下步骤:
- 将关联列的字符串长度更改为 200。
- 删除与该列关联的当前域。
- 创建一个电子邮件字符串长度为 200 的新域。
- 将与当前域关联的列与新域重新关联。
如果当前域定义和列关联如下:
sql
CREATE DOMAIN email_dom AS VARCHAR2(100)
CONSTRAINT email_chk CHECK (regexp_like (email_dom, '^(\S+)\@(\S+)\.(\S+)$'));
CREATE TABLE t1 (
id NUMBER,
email DOMAIN email_dom
);
要查看与域相关的列的列表:
sql
SELECT table_name, column_name
FROM user_tab_columns
WHERE domain_name = 'EMAIL_DOM';
TABLE_NAME COLUMN_NAME
---------------------- -------------------
T1 EMAIL
修改 t1 表以将电子邮件列的字符串大小更改为 200:
sql
ALTER TABLE t1 modify email varchar2(200);
使用 FORCE PRESERVE 选项删除域:
sql
DROP DOMAIN email_dom FORCE PRESERVE;
然后,创建一个电子邮件字符串大小为 200 的新域:
sql
CREATE DOMAIN email_dom AS VARCHAR2(200)
CONSTRAINT email_chk CHECK (regexp_like (email_dom, '^(\S+)\@(\S+)\.(\S+)$'));
并将新域名与电子邮件列重新关联:
sql
ALTER TABLE t1
MODIFY email DOMAIN email_dom;
示例 10-53 使用在线 dbms_redefinition 包更改域属性
您可以使用 Oracle 在线表重组包 dbms_redefinition 来更改允许的值,例如更新支持的货币。例如,要更改货币域中支持的货币,所需的步骤如下:
- 创建一个新域。
- 迁移与当前域关联的列以使用新域。
以下示例仅具有基本的错误处理。在迁移多个表后,可能会出现错误,例如当某些数据违反新约束时。完整的解决方案需要考虑这些情况。
以下代码创建一个具有约束的域,该约束允许以下货币:USD、GBP 和 EUR。
sql
CREATE DOMAIN currency AS (
amount AS NUMBER,
iso_currency_code AS CHAR ( 3 CHAR )
) CONSTRAINT supported_currencies_c
CHECK ( iso_currency_code in ( 'USD', 'GBP', 'EUR' ) );
假设下表包含与货币域相关的列。
sql
CREATE TABLE order_items (
order_id INTEGER, product_id INTEGER,
total_paid NUMBER(10, 2), currency_code CHAR ( 3 CHAR ),
DOMAIN currency ( total_paid, currency_code ),
PRIMARY KEY ( order_id, product_id )
);
CREATE TABLE product_prices (
product_id INTEGER,
unit_price NUMBER(10, 2),
currency_code CHAR( 3 char ),
DOMAIN currency ( unit_price, currency_code ),
PRIMARY KEY ( product_id, currency_code )
);
使用 INSERT 命令将一些值存储到 product_prices 和 order_items 表中。
sql
INSERT INTO product_prices
VALUES (1, 9.99, 'USD'),
(1, 8.99, 'GBP'),
(1, 8.99, 'EUR');
INSERT INTO order_items
VALUES (1, 1, 9.99, 'USD'),
(2, 1, 8.99, 'GBP'),
(3, 1, 8.99, 'EUR');
COMMIT;
假设您的业务正在扩展,并且您希望支持更多货币。您无法直接修改约束,因此您需要一种替代方法,包括:
- 创建新域。
- 修改 product_prices 和 order_items 表以从关联列中删除现有域,同时保留约束。
- 修改表以添加新域。
- 从表中删除保留的约束。
但是,在线执行时,这些是阻塞 DDL 语句。相反,您可以使用 dbms_redefinition 包在线修改约束。
您必须创建一个包含新支持的货币约束的新域,并将其与临时表关联。新域将替换原始域。
sql
CREATE DOMAIN currency_d as (
amount AS NUMBER,
iso_currency_code AS CHAR ( 3 CHAR )
) CONSTRAINT supported_currencies_d_c
CHECK ( iso_currency_code in ( 'USD', 'GBP', 'EUR', 'CAD', 'MXP', 'INR', 'JPY' ) );
CREATE TABLE order_items_tmp (
order_id INTEGER, product_id INTEGER,
total_paid NUMBER(10, 2), currency_code CHAR ( 3 CHAR ),
DOMAIN currency_d ( total_paid, currency_code ) ,
PRIMARY KEY ( order_id, product_id )
);
CREATE TABLE product_prices_tmp (
product_id INTEGER,
unit_price NUMBER(10, 2),
currency_code CHAR (3 CHAR),
DOMAIN currency_d ( unit_price, currency_code ),
PRIMARY KEY ( product_id, currency_code )
);
为了使代码更具可重用性,可以创建一个 redefine_table 过程,该过程调用 dbms_redefinition 过程将临时表列的属性复制到当前表列,然后将当前表列交换到新域。
sql
DECLARE
PROCEDURE redefine_table ( current_table VARCHAR2, staging_table VARCHAR2 ) AS
num_errors pls_integer;
BEGIN
DBMS_REDEFINITION.CAN_REDEF_TABLE(user, current_table);
DBMS_REDEFINITION.START_REDEF_TABLE(user, current_table, staging_table);
DBMS_REDEFINITION.copy_table_dependents(
uname => user,
orig_table => current_table,
int_table => staging_table,
copy_constraints => false,
num_errors => num_errors);
IF num_errors > 0 THEN
dbms_redefinition.abort_redef_table(user, current_table, staging_table);
raise_application_error ( -20001, num_errors || ' errors copying dependents from ' || current_table || ' to ' || staging_table );
ELSE
dbms_redefinition.finish_redef_table(user, current_table, staging_table);
END IF;
END redefine_table;
BEGIN
FOR tabs IN (
SELECT distinct table_name from user_tab_cols
WHERE domain_name = 'CURRENCY'
) LOOP
redefine_table(tabs.table_name, tabs.table_name || '_TMP');
END LOOP;
END;
/
在 product_prices 表上使用 DML 查看现在是否支持新货币。
sql
-- New currencies now supported
INSERT INTO product_prices
VALUES (1, 9.99, 'CAD');
-- Invalid currencies raise exception
INSERT INTO product_prices
VALUES (1, 9.99, 'N/A');
SELECT * FROM product_prices;
输出为:
sql
PRODUCT_ID UNIT_PRICE CUR
---------- ---------- ---
1 9.99 USD
1 8.99 GBP
1 8.99 EUR
1 9.99 CAD
清理临时对象和旧域:货币。
sql
DROP TABLE order_items_tmp PURGE;
DROP TABLE product_prices_tmp PURGE;
DROP DOMAIN currency;
用例域的 SQL 函数
域函数使您能够更高效地使用用例域。
您可以将以下 SQL 函数与用例域一起使用:
- DOMAIN_DISPLAY 返回与参数关联的域的域显示表达式。
- DOMAIN_NAME 返回与参数关联的域的限定域名。请注意,如果域有公共同义词,则返回该同义词;否则,将以 domain_owner.domain_name 格式返回域名。
- DOMAIN_ORDER 返回与参数关联的域的域顺序表达式。
- DOMAIN_CHECK(domain_name, value1, value2, ...) 将 domain_name 的约束条件(非空或检查约束)应用于值表达式。它还会检查值的类型兼容性。可以有多个值;值表达式的数量必须与域中的列数匹配,否则语句会引发错误。
- DOMAIN_CHECK_TYPE(domain_name, value1, value2, ...) 验证输入表达式是否与域的数据类型兼容。可以有多个值;值表达式的数量必须与域中的列数匹配,否则语句会引发错误。
另请参阅:《Oracle 数据库 SQL 语言参考》中的"域函数"了解有关使用 SQL 函数处理用例域的更多信息
查看域信息
您可以使用字典视图获取有关域的信息。字典视图还可以帮助识别具有不同属性的列,例如与相关域相比具有不同属性的约束和默认表达式。
用例域的字典视图
字典视图:[USER|DBA|ALL]_DOMAINS 和 [USER|DBA|ALL]_DOMAIN_COLS 表示域并提供有关域列的以下信息。对于灵活域,视图还包括域选择器表达式。
- 域名
- 域所有者
- 显示和排序表达式
- 默认值
- 域的数据类型
- 排序规则
以下字典视图可用于用例域:
- ALL_DOMAINS 描述当前用户可访问的域。
- DBA_DOMAINS 描述数据库中的所有域。
- USER_DOMAINS 描述当前用户拥有的域。
- ALL_DOMAIN_COLS 描述当前用户可访问的域的列。
- DBA_DOMAIN_COLS 描述数据库中所有域的列。
- USER_DOMAIN_COLS 描述当前用户拥有的域的列。
- ALL_DOMAIN_CONSTRAINTS 描述当前用户可访问的域中的约束定义。
- DBA_DOMAIN_CONSTRAINTS 描述数据库中所有域中的约束定义。
- USER_DOMAIN_CONSTRAINTS 描述当前用户拥有的域中的约束定义。
内置用例域
Oracle 数据库提供了一些内置用例域,您可以直接在表列上使用它们,例如 email_d、ssn_d 和 credit_card_number_d。所有 PDB 中都存在内置用例域。将新的 PDB 添加到 CDB 中时,内置用例域会自动创建并添加到 PDB 中。
Oracle 数据库支持以下内置域。这些域分类如下:
- 标识符内置域
- 技术内置域
- 数字内置域
- 其他内置域
表 10-1 标识符内置域
# | 域名 | 说明 | 示例 | 数据类型 | 标注 | 约束 |
---|---|---|---|---|---|---|
1 | city_d | 市区或直辖市 | "San Francisco", "Mumbai", "Tokyo" | VARCHAR2(100) | 地址 | |
2 | country_code_d | 分配给每个国家或地区的标准化两字母或三字母代码,通常由 ISO 3166 等国际标准定义。 | "USA", "IND", "AUS" | VARCHAR2(3) | 地址 | |
3 | country_d | 一个被承认为独立民族国家的独特的领土和主权实体,具有自己的政府、法律和国际承认。 | "USA", "India", "Mexico" | VARCHAR2(100) | 地址 | |
4 | credit_card_number_d | 为了识别和交易目的而分配给信用卡的一系列唯一数字。 | "12345678901234" | VARCHAR2(20) | REGEX - ^[0-9]{12,19}$ |
|
5 | date_of_birth_d | 个人出生的具体日期、月份和年份,代表其出生日期。这是用于身份识别、年龄验证和记录保存的基本个人信息。显示格式取决于会话使用的日期格式。 | 01-JAN-91, 02/28/2023, 2023/54 | DATE | 个人信息 | |
6 | district_d | 城市或国家内划定的区域或地区,通常具有独特的行政、社会或地理特征。 | "Manhattan", "Brooklyn" | VARCHAR2(100) | 地址 | |
7 | floor_d | 建筑物内特定单元或住宅所在的楼层或层数,通常在其地址中注明。 | "1", "1A", "Ground", "First" | VARCHAR2(20) | 地址 | |
8 | house_number_d | 分配给街道或地区内的建筑物或住宅的数字标识符,通常用于邮政寻址和导航目的。 | "1-A", "10", "C1-H123" | VARCHAR2(50) | 地址 | |
9 | license_plate_d | 车辆牌照上显示的字母、数字或符号的唯一组合,作为该车辆的唯一标识符。 | "ABC123", "1234 XYZ" | VARCHAR2(100) | 个人信息 | |
10 | marital_status_d | 一种人口统计属性,表示个人与婚姻相关的法律关系状态。常见类别包括"单身"、"已婚"、"离婚"、"丧偶"或"分居"等。 | "single", "married", "widowed" | VARCHAR2(50) | 个人信息 | |
11 | national_id_d | 政府为识别和管理目的向其公民或居民颁发的唯一标识符。国民身份证用于访问政府服务、证明身份以及促进投票、旅行和金融活动等交易。 | "123-45-6789", "AB123456C" | VARCHAR2(50) | 个人信息 | |
12 | passport_d | 政府签发的旅行证件,证明持有人的身份和国籍,允许其进行国际旅行。 | "AB123456","123456789" | VARCHAR2(100) | 个人信息 | |
13 | payment_card_id_d | 与支付卡(如信用卡、借记卡或预付卡)关联的唯一标识符。此标识符用于在金融机构系统中区分一张卡与另一张卡,并用于授权、处理和跟踪交易。 | "4012 8888 8888 1881" | VARCHAR2(200) | 个人信息、付款信息 | |
14 | phone_d | 用于电信的数字序列,允许个人通过语音通话或短信进行连接。 | "1-555-555-5555" | VARCHAR2(20) | 个人信息 | |
15 | phone_number_d | 用于电信的数字序列,允许个人通过语音通话或短信进行连接。 | "+1234567890123456" | VARCHAR2(17) | REGEX - ^[+]{0,1}[0-9]{1,16}$ |
|
16 | postal_code_d | 邮政服务使用的数字或字母数字代码,用于识别邮件投递和分类的特定地理区域。 | "SW1A 1AA" | VARCHAR2(20) | 地址 | |
17 | province_d | 一个国家内的领土划分,通常拥有自己的政府和行政权力,特别是在联邦或分散体制下。 | "Alberta", "Ontario" | VARCHAR2(100) | 地址 | |
18 | social_media_id_d | 与特定社交媒体平台上的个人或实体帐户关联的唯一标识符。它充当平台系统内的独特标签或参考点,允许识别特定用户或个人资料并与之互动。此域通常与代表社交媒体平台的 social_media_type_d 域结合使用。 | "user123" | VARCHAR2(100) | 个人信息 | |
19 | social_media_type_d | 指社交媒体平台的分类或类别,例如 Facebook、Twitter、Instagram 或 LinkedIn。此域通常与 social_media_id_d 域结合使用,后者表示社交媒体平台的用户标识符。 | "facebook", "x", "instagram" | VARCHAR2(100) | ||
20 | ssn_d | 社会保障局为美国个人分配的唯一九位数字标识符。 | "123-45-6789" | VARCHAR2(11) | REGEX - ^[0-9]{3}[-][0-9]{2}[-][0-9]{4}$ |
|
21 | state_d | 一个国家内的领土和行政区划,通常拥有自己的政府和法律体系。 | "California", "Texas" | VARCHAR2(100) | 地址 | |
22 | street_d | 建筑物或住宅所在的道路或大道的名称,构成其地址的一部分。 | "4th Main Road", "Purple Street 104" | VARCHAR2(200) | 地址 | |
23 | us_license_plate_d | 美国车辆牌照上显示的唯一字母数字组合,作为该车辆的唯一标识符。 | "ABC-XY-1234" | VARCHAR2(20) | 个人信息 | REGEX - ^[A-Za-z]{1,3}-[A-Za-z]{1,2}-[0-9]{1,4}$ |
24 | us_passport_d | 美国政府向其公民颁发的用于国际旅行的旅行证件。 | "ABC123456" | VARCHAR2(100) | 个人信息 | |
25 | us_phone_d | 用于美国电信的一系列数字。 | "123-456-7890" | VARCHAR2(20) | 个人信息 | REGEX - ^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$ |
26 | us_postal_code_d | 美国邮政编码,通常指邮政编码。 | "10001-1234" | VARCHAR2(20) | 地址 | REGEX - ^\d{5}([ \-]\d{4})?$ |
27 | us_ssn_d | 社会保障局为美国个人分配的唯一九位数字标识符。 | "123-45-6789" | VARCHAR2(15) | 个人信息 | |
28 | us_state_d | 美国境内的行政区域。 | "CA", "NY", "TX", "FL" | VARCHAR2(2) | 地址 | `REGEX-^(AE |
29 | us_vehicle_id_d | 在美国分配给车辆的唯一标识符,通常称为车辆识别号 (VIN)。 | "1G1JC1441Y7167030" | VARCHAR2(20) | 个人信息 | REGEX - ^[A-HJ-NPR-Z0-9]{17}$ |
30 | us_zip_d | 美国邮政服务 (USPS) 使用的字母数字代码,用于方便邮件投递和地址分类。 | "10001-1234" | VARCHAR2(15) | 地址 | REGEX - ^\d{5}(-\d{4})?$ |
31 | vehicle_id_d | 分配给车辆的唯一标识符,通常用于政府当局和运输机构的登记、跟踪和识别目的。 | "UK11AP999" | VARCHAR2(100) | 个人信息 | |
32 | zip_d | 邮政服务使用的邮政编码,以便有效地将邮件路由到特定的地理区域。 | "SW1A 1AA" | VARCHAR2(20) | 地址 |
表 10-2 技术内置域
# | 域名 | 说明 | 示例 | 数据类型 | 标注 | 约束 |
---|---|---|---|---|---|---|
1 | cidr_d | CIDR:无类域间路由的缩写,用于指定 IP 网络的大小。 | "192.168.1.0/24", "10.0.0.0/8" | VARCHAR2(18) | 技术信息 | 略 |
2 | created_on_d | 创建文件、记录或实体的日期和时间。 | '1960-01-01 23:03:20' | TIMESTAMP | 技术信息,时间戳 | |
... |
表 10-3 数字内置域
# | 域名 | 说明 | 示例 | 数据类型 | 标注 | 约束 |
---|---|---|---|---|---|---|
1 | count_d | 指一组或一组内项目、事件或实体的数值或总量。它表示存在的元素数量,用于量化和跟踪某事物的丰度、频率或范围。 | 5, 12, 100, 1000 | NUMBER | ||
2 | counter_d | 一种指标类型,表示随时间推移而增加的累积值。计数器通常用于测量事件或发生次数的总数,例如收到的 HTTP 请求数或传输的总字节数。 | 1000, 50000, 8, 128, 56.4, | NUMBER | prometheus | |
... |
表 10-4 其他内置域
# | 域名 | 说明 | 示例 | 数据类型 | 标注 | 约束 |
---|---|---|---|---|---|---|
1 | binary_d | 以二进制为基数的数字系统仅使用两个符号(通常是 0 和 1)来表示数字。每个数字代表 2 的幂。例如,二进制数 1010 表示十进制数 10。 | "01010001111", "0", "1", "00001111" | VARCHAR2(4000) | REGEX - ^[01]*$ |
|
2 | currency_amount_d | 指在金融交易或货币兑换中分配给特定货币单位的数值。它表示交易的货币数量或金额。 此域通常与代表交易货币的 currency_code_d 域结合使用。 | 10, 1000, 5000.67, 100000 | NUMBER | 货币 | |
... |