探索 Oracle Database 23ai 中的 SQL 功能

发布于:2025-06-22 ⋅ 阅读:(15) ⋅ 点赞:(0)

介绍

在这个教程中,我们将学习 10 个您需要知道的功能及其与现有对应功能的比较。这些功能包括:

  • FROM 子句(可选)。
  • SQL 中的 BOOLEAN
  • 数据定义语言(DDL)中的 IF NOT EXISTS 子句。
  • 多值 INSERT
  • 新表 VALUE 构造函数。
  • GROUP BY 子句中的别名。
  • UPDATEMERGE 语句的 RETURNING 子句。
  • UPDATEDELETE 中的连接。
  • 注释,数据库对象的新元数据。
  • 使用 SQL 域的轻量级对象类型。

目标

  • 不使用 FROM 子句的 SQL:通过删除在选择表达式或内置函数时使用 FROM 子句的要求,使查询更简单。

  • 在 SQL 中实现原生 BOOLEAN 数据类型:在 SQL 表、查询和条件中利用原生 BOOLEAN 数据类型,以便更直观地处理真/假逻辑。

  • 在 DDL 语句中使用 IF NOT EXISTS:通过有条件地执行 CREATEDROP 语句,简化对象创建和删除逻辑,无需额外的 PL/SQL 检查。

  • 执行多值 INSERT 操作:通过在单个 INSERT 语句中插入多行,提高代码可读性并减少与数据库的往返次数。

  • 使用表值构造函数创建内联数据集:使用 VALUES 构造函数直接在 SQL 中创建临时行集,支持 MERGESELECT 或比较等操作。

  • GROUP BY 子句中引用列别名:通过允许在 GROUP BY 中使用 SELECT 别名而非重复表达式,增强查询的可读性。

  • UPDATEMERGE 中利用 RETURNING 子句:直接从 UPDATEMERGE 语句中检索受影响的数据,无需后续查询。

  • UPDATEDELETE 语句中执行连接:直接在 UPDATEDELETE 操作中使用 JOIN 逻辑,根据相关表的条件修改或删除记录。

  • 使用元数据注释数据库对象:使用 ANNOTATION 文档化数据库对象,以存储描述性元数据(例如所有者、用途),便于维护和内省。

  • 使用 SQL 域定义轻量级对象类型:创建可重用的域类型和约束,以在多个表中强制执行一致性和强类型。

前提条件

  • 基础 SQL 知识。

    • 了解 SQL 语法:SELECTINSERTUPDATEDELETEJOINGROUP BY 等。

    • 熟悉关系数据库概念和数据类型。

  • 使用 Oracle Database 23ai 及其早期版本的经验。

    • 了解 Oracle Database 19c、Oracle Database 21c 及更早版本中 DDL、数据操作语言(DML)和 PL/SQL 的工作方式。

    • 了解 Oracle 特定功能,如 DUALMERGERETURNING INTO 等。

  • 访问 Oracle Database 23ai 环境。

    • 访问 Oracle Database 23ai(本地设置、云实例或 Oracle Live SQL)。

    • 某些功能(如 SQL 域或 BOOLEAN)仅在 Oracle Database 23ai 中可用。

  • SQL*Plus、SQLcl 或 GUI 工具(如 SQL Developer 或 DataGrip)。能够在兼容的界面中运行和测试 SQL 语句。

  • PL/SQL 基础(用于高级功能)。用于 RETURNING INTO、过程块和处理动态 SQL。

  • 约束和数据完整性规则的知识。需要了解 SQL 域和表约束。

  • 熟悉 Oracle 数据字典视图。用于查询注释或元数据。例如,USER_TABLESUSER_ANNOTATIONS

  • Oracle Database 中的角色和权限。创建/修改表、域和注释的能力需要适当的用户权限。

  • 版本意识。确保您的工具和客户端支持 Oracle Database 23ai 功能(较旧的驱动程序或工具可能会失败)。

  • (可选)接触其他现代 SQL 方言(PostgreSQL、MySQL 等)。这将帮助您理解新功能(如 VALUESBOOLEANIF EXISTS)的跨兼容性。

功能 1:使用 FROM 子句

Oracle Database 23ai 引入的一个有趣功能是 SELECT 语句中 FROM 子句的可选性。在此版本之前,FROM 子句是必需的。

以下是 Oracle Database 23ai 中不使用 FROM 子句功能的一些潜在好处。

  • 选择当前日期以便于数据操作。

    SELECT CURRENT_DATE;
    

在这里插入图片描述

  • 无需涉及表数据的数学运算或计算。

    SELECT 25.50*25.25;
    25.50*25.25
    -----------
    643.875
    耗时: 00:00:00.002
    1 行已选中。
    
  • 不使用 FROM 子句的 PL/SQL 块。

    CREATE SEQUENCE empno_seq INCREMENT BY 1 START WITH 1 MINVALUE 1 MAXVALUE 1000;
    序列 EMPNO_SEQ 已创建。
    耗时: 00:00:00.005
    declare v1 number;
    begin
    select empno_seq.nextval into v1;
    dbms_output.put_line ('v1= '||v1);
    end;
    /
    v1= 1
    PL/SQL 过程已成功完成。
    耗时: 00:00:00.009
    
  • 使用内置或用户定义的函数执行操作或检索值。

    SELECT DBMS_RANDOM.VALUE() as random_number;
    
  • 无需依赖表数据的字符串操作或转换。

    SELECT UPPER('oracle') AS uppercase_text;
    

在这里插入图片描述

  • 不使用表的条件或逻辑表达式。

    SELECT CASE WHEN 10 > 5 THEN 'True' ELSE 'False' END AS result;
    

功能 2:使用 BOOLEAN 数据类型

Oracle Database 23ai 引入了新的 BOOLEAN 数据类型。这使得可以使用真正的布尔列/变量,而不是用数值或 Varchar 模拟它们。能够编写布尔谓词简化了 SQL 语句的语法。

  1. 创建一个名为 TEST_BOOLEAN 的表。

    CREATE TABLE IF NOT EXISTS TEST_BOOLEAN (name VARCHAR2(100), IS_SLEEPING BOOLEAN);
    表 TEST_BOOLEAN 已创建。
    耗时: 00:00:00.004
    
  2. 向新表中输入数据。IS_SLEEPING 的值将是 NOT NULL 并默认为 FALSE

    ALTER TABLE TEST_BOOLEAN modify (IS_SLEEPING boolean NOT NULL);
    

    在这里插入图片描述

    ALTER TABLE TEST_BOOLEAN modify (IS_SLEEPING default FALSE);
    表 TEST_BOOLEAN 已修改。
    耗时: 00:00:00.014
    

    在这里,您可以看到 Mick、Keith 和 Ron 的不同布尔输入。所有输入都是有效的。对于 Mick,使用默认的 FALSE 值 - Mick 没有睡觉。

    INSERT INTO TEST_BOOLEAN (name, is_sleeping) values ('Mick', default);
    1 行已插入。
    耗时: 00:00:00.006
    

    对于 Keith,我们使用 NO 值 - Keith 没有睡觉。

    INSERT INTO TEST_BOOLEAN (name, is_sleeping) values ('Keith','NO');
    1 行已插入。
    耗时: 00:00:00.002
    

    对于 Ron,我们使用 1 值 - Ron 正在睡觉。

    INSERT INTO TEST_BOOLEAN (name, is_sleeping) values ('Ron',1);
    1 行已插入。
    耗时: 00:00:00.002
    
  3. 查看基于我们布尔值的结果。

    SELECT * FROM test_boolean;
    

    您不再需要记住您设置的布尔系统类型。如我们所示,使用 0/1、True/False、Yes/No 或任何其他常见输入将返回准确的表值。

功能 3:使用 IF NOT EXISTS DDL 子句

从 Oracle Database 23ai 开始,新的 IF NOT EXISTS DDL 子句允许决定如何处理 DDL 错误。这简化了 DDL 脚本编写,因为由于对象存在或不存在导致的潜在错误可以被脚本隐藏。

  1. 首先,测试不使用此新功能。运行以下语句。

    DROP TABLE DEPT;
    

    由于没有现有的 DEPT 表可删除,我们将看到错误:ORA-00942: 表或视图不存在

  2. 然而,在 Oracle Database 23ai 中,我们可以使用 DROP IF EXISTS 而不出现错误。这让我们在避免错误的同时心安理得。现在,运行相同的语句,但包含此新的 IF EXISTS 功能。

    DROP TABLE IF EXISTS DEPT;
    
  3. 类似地,我们可以使用此功能在表不存在时创建表。创建 DEPT 表。

    CREATE TABLE IF NOT EXISTS DEPT
    (DEPTNO NUMBER(2) CONSTRAINT PK_DEPT PRIMARY KEY,
    DNAME VARCHAR2(14) ,
    LOC VARCHAR2(13) ) ;
    

    在这里插入图片描述

  4. 使用此功能在本教程中创建更多示例表。在这里,我们将创建一个名为 EMP 的员工表。

    CREATE TABLE IF NOT EXISTS EMP (EMPNO NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY, ENAME VARCHAR2(10), JOB VARCHAR2(9), MGR NUMBER(4), HIREDATE DATE, SAL NUMBER(7,2), COMM NUMBER(7,2), DEPTNO NUMBER(2) CONSTRAINT FK_DEPTNO REFERENCES DEPT);
    表 EMP 已创建。
    耗时: 00:00:00.006
    

功能 4:使用 INSERT 插入多行

另一个确保与其他常用数据库管理系统更好共存和兼容性的有趣功能是多值 INSERT 语句。

  1. 在 Oracle 数据库的早期版本中,例如,插入多行需要为每一行单独插入语句。

    INSERT INTO DEPT VALUES (10,'ACCOUNTING','NEW YORK');
    INSERT INTO DEPT VALUES (20,'RESEARCH','DALLAS');
    INSERT INTO DEPT VALUES (30,'SALES','CHICAGO');
    INSERT INTO DEPT VALUES (40,'OPERATIONS','BOSTON');
    INSERT INTO DEPT VALUES (50,'HR','LOS ANGELES');
    INSERT INTO DEPT VALUES (60,'IT','SAN FRANCISCO');
    INSERT INTO DEPT VALUES (70,'MANUFACTURING','DETROIT');
    

    Oracle Database 23ai 引入了新的语法,允许在单个 INSERT 语句中插入所有这些行,因此您可以在一个 DML 中插入几个元组。运行以下语句。

    INSERT INTO DEPT VALUES (10,'ACCOUNTING','NEW YORK'), (20,'RESEARCH','DALLAS'), (30,'SALES','CHICAGO'), (40,'OPERATIONS','BOSTON'), (50,'HR','LOS ANGELES'), (60,'IT','SAN FRANCISCO'), (70,'MANUFACTURING','DETROIT');
    

    在这里插入图片描述

    除了与其他数据库更好的兼容性外,此语句还可以用于确保在自动提交模式下的一些插入操作的一致性。这对于使用此模式处理数据的 Oracle APEX 应用程序可能很重要。

  2. 运行以下语句以使用此功能为 EMP 表填充值。

    INSERT INTO EMP VALUES (7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20), (7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30), (7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30), (7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,NULL,20), (7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30), (7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,NULL,30), (7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,NULL,10), (7788,'SCOTT','ANALYST',7566,to_date('13-JUL-87')-85,3000,NULL,20), (7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),5000,NULL,10), (7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30), (7876,'ADAMS','CLERK',7788,to_date('13-JUL-87')-51,1100,NULL,20), (7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,NULL,30), (7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,NULL,20), (7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,NULL,10);
    

功能 5:使用新的 VALUE 构造函数

从 Oracle 数据库 23ai 开始,表值构造函数已扩展。现在可以在 INSERT 语句中使用,以便在单个命令中创建多行。它还可以在 SELECT 语句和视图因式分解语法中使用。在这种情况下,它简化了语句的语法,并避免使用 DUAL 表。

以下语句看起来像是一种即时的表函数。

SELECT * FROM (VALUES (50,'HR'), (60,'DEV'), (70,'AI')) virt_dept (deptno, dname);

在这里插入图片描述

功能 6:在 GROUP BY 子句中使用别名

Oracle Database 23ai 引入了在 SELECT 语句的 GROUP BY 子句中使用别名的能力。此功能简化了编写具有复杂表达式的查询,并确保与某些其他关系数据库(如 Teradata、MySQL 和 PostgreSQL)的更好兼容性。

例如:

SELECT to_char(hiredate,'YYYY') "Year", count(*) FROM emp GROUP BY to_char(hiredate,'YYYY');

在 Oracle Database 23ai 中,这可以以更简单的方式编写,如下图所示:

在这里插入图片描述

功能 7:使用 UPDATEMERGE 语句的 RETURNING 子句

此子句曾作为 EXECUTE IMMEDIATE 语句的一部分实现。然而,在 Oracle Database 23ai 中,我们可以在传统的、静态的 DML 语句中找到它。

  1. 在这种情况下,它允许从处理的行获取列的旧值和新值。首先,让我们看看 King 的当前薪水。

    SELECT ename, sal FROM emp WHERE ename = 'KING';
    
  2. 为了在 LiveSQL 中使用变量,我们将将我们的语句包装在 PL/SQL 中。运行此脚本。它首先为旧薪水和新薪水创建变量,然后使用 RETURNING 子句更新 King 的薪水以设置我们的变量。然后我们将查看结果。

    BEGIN
    DECLARE
        old_salary NUMBER;
        new_salary NUMBER;
    BEGIN
        UPDATE emp
        SET sal = sal + 1000
        WHERE ename = 'KING'
        RETURNING OLD sal, NEW sal INTO old_salary, new_salary;
        DBMS_OUTPUT.PUT_LINE('Old Salary: ' || old_salary);
        DBMS_OUTPUT.PUT_LINE('New Salary: ' || new_salary);
    END;
    END;
    

    在这里插入图片描述

    Old Salary: 6000
    New Salary: 7000
    

    此示例使用了 UPDATE 语句,但 RETURNING 子句可以类似地用于 MERGE 语句。

功能 8:在 UPDATEDELETE 中使用连接

您可以使用基于外部表条件的连接更新表数据。无需子查询或 IN 子句。

  1. 运行以下语句以查看研究部门的员工薪资信息。

    select e.sal, e.empno from emp e, dept d where e.deptno=d.deptno and d.dname='RESEARCH';
    
  2. 在 Oracle Database 23ai 之前,我们需要使用嵌套语句来更新薪资信息。

    UPDATE emp e set e.sal=e.sal*2 WHERE e.deptno in (SELECT d.deptno FROM dept d WHERE e.deptno=d.deptno and d.dname='RESEARCH');
    

    在 Oracle Database 23ai 中,您可以这样使用它:

    UPDATE emp e set e.sal=e.sal*2
    FROM dept d
    WHERE e.deptno=d.deptno
    and d.dname='RESEARCH';
    

    在这里插入图片描述

  3. 您可以看到薪资已成功更新。

    select e.sal, e.empno from emp e, dept d where e.deptno=d.deptno and d.dname='RESEARCH';
    

    在这里插入图片描述

功能 9:使用注释

注释是数据库对象的可选元数据。注释是名称-值对或仅名称。名称和可选值是自由格式的文本字段。注释表示为数据库对象的从属元素,该注释已添加。支持的模式对象包括表、视图、物化视图和索引。使用注释,您可以存储和检索有关数据库对象的元数据。您可以使用它来自定义业务逻辑、用户界面或将元数据提供给元数据存储库。它可以在表或列级别使用 CREATEALTER 语句添加。

使用注释,您可以存储和检索有关数据库对象的元数据。您可以使用它来自定义业务逻辑、用户界面或将元数据提供给元数据存储库。

  1. 创建带有列和表注释的注释表 EMP_ANNOTATED_NEW

    CREATE TABLE emp_annotated_new
    (empno number annotations(identity, display 'person_identity', details 'person_info'),
    ename varchar2(50),
    salary number annotations (display 'person_salary', col_hidden))
    annotations (display 'employee_table');
    

    在这里插入图片描述

  2. 数据字典视图,如 USER_ANNOTATIONSUSER_ANNOTATIONS_USAGE,可以帮助监控使用情况。

    SELECT object_name, object_type, column_name, annotation_name, annotation_value FROM user_annotations_usage;
    

    在这里插入图片描述

功能 10:使用 SQL 域

SQL 域是属于模式的字典对象,它封装了一组可选的属性和约束,并使用 CREATE DOMAIN 语句创建。域提供约束、显示、排序和注释属性。定义 SQL 域后,您可以定义表列与该域关联,从而将域的可选属性和约束显式应用于这些列。

SQL 域允许用户声明列的预期用途。它们是字典对象,因此可以轻松重用抽象的域特定知识。

  1. 创建名为 yearbirth 的域和名为 person 的表。

    CREATE DOMAIN yearbirth as number(4) constraint check ((trunc(yearbirth) = yearbirth) and (yearbirth >= 1900)) display (case when yearbirth < 2000 then '19-' ELSE '20-' end)||mod(yearbirth, 100) order (yearbirth -1900) annotations (title 'yearformat');
    

    在这里插入图片描述

    CREATE TABLE person (id number(5), name varchar2(50), salary number, person_birth number(4) DOMAIN yearbirth ) annotations (display 'person_table');
    

    在这里插入图片描述

    desc person;
    
    Name Null? Type
    
    * * *
    
    ID NUMBER(5)
    NAME VARCHAR2(50)
    SALARY NUMBER
    PERSON_BIRTH NUMBER(4) DOMAIN YEARBIRTH
    
    INSERT INTO person values (1,’MARTIN’,3000, 1988);
    
  2. 使用新函数 DOMAIN_DISPLAY 可以显示属性。

    SELECT DOMAIN_DISPLAY(person_birth) FROM person;
    
  3. 域的使用情况和注释可以通过数据字典视图监控。让我们查看 user_annotations_usage

    SELECT * FROM user_annotations_usage;
    
    定义可重用的域类型(轻量级对象)。
    CREATE DOMAIN EmailAddress AS VARCHAR2(100) CHECK (REGEXP_LIKE(VALUE,^\[^@\]+@\[^@\]+.\[^@\]+$’));
    CREATE TABLE users ( user_id NUMBER, email EmailAddress );
    

    在这里插入图片描述

Oracle Database 23ai 功能的限制和限制

  • FROM 子句

    • 仅适用于简单表达式,如函数、字面量或变量。

    • 不能用于涉及表、连接或子查询的查询。

    • 不支持在 PL/SQL 上下文中使用游标循环,这些循环期望 FROM 子句。

  • 原生 BOOLEAN 数据类型

    • 可以用于表列和表达式。

    • 不可索引,BOOLEAN 数据类型的列不能被索引。

    • 并非所有客户端工具或报表工具都直接支持(可能需要将其转换为 0/1 或 Y/N)。

    • 某些较旧的 API 或驱动程序不支持(JDBC/ODBC 客户端可能需要更新)。

  • DDL 中的 IF NOT EXISTS

    • 简化了幂等 DDL 脚本。

    • 仅适用于特定对象:TABLEINDEXSEQUENCEVIEW 等。

    • 并非所有对象类型都支持此功能(例如,TRIGGERSYNONYM 可能仍需要手动检查)。

    • 不支持较旧的 Oracle 版本。

  • 多值 INSERT

    • 批量插入的清晰语法。

    • 仅限于显式值集,不能在相同的 VALUES 子句中使用 SELECT 或子查询插入。

    • 不能与 RETURNING 子句结合使用,以便在一步中为所有插入的行返回值。

  • 表值构造函数(VALUES 子句)

    • 适用于小型、临时的行集。

    • 有行限制(通常为 999 行或更少,具体取决于上下文)。

    • 不适用于大规模加载,更好地使用临时表或暂存区域处理大型数据集。

  • GROUP BY 子句中的别名

    • 使查询更容易阅读和编写。

    • 不支持所有分析函数或涉及子查询列的复杂查询。

    • 可能在查询中引起混淆,其中别名被重复使用(例如,在内部查询中使用相同名称)。

  • UPDATE/MERGE 中的 RETURNING 子句

    • 减少了 DML 后的 SELECT 需要。

    • 仅能返回实际修改的行的值。

    • 不能用于批量更新,除非使用 FORALL,必须使用 PL/SQL 并显式 RETURNING BULK COLLECT

  • UPDATEDELETE 中的连接

    • 使多表逻辑更简单。

    • 仅支持某些上下文中的 INNER JOINLEFT JOIN 类型。

    • 如果使用较旧的工具或需要与较早的 Oracle 版本兼容,可能需要重写 MERGE 逻辑。

  • 注释

    • 非常适合文档化。

    • 注释仅是元数据,无法在运行时强制执行。

    • 需要使用 Oracle 数据字典视图(*_ANNOTATIONS)来检索。

    • 尚未集成到 Oracle Data Pump 导出/导入或复制工具中。

  • 使用 SQL 域的轻量级对象类型

    • 可重用,一致的类型强制。

    • 仍然不如完整的对象类型强大(没有方法、属性)。

    • 没有继承或组合,仅用于原始约束强制。

    • 无法在域本身中定义默认值(默认值仍在列级别指定)。

一般限制

  • 工具兼容性:许多 GUI 工具和较旧的 Oracle 客户端库可能尚未完全支持 Oracle Database 23ai 语法(特别是 VALUESBOOLEANDOMAIN)。

  • 导出/导入:某些功能(如注释或域)可能不会在较旧的 expdp/impdp 工作流中保留。

  • 实验性行为:由于这些功能是新的,某些功能可能会在次要版本中发展——请频繁查看补丁说明和文档。


网站公告

今日签到

点亮在社区的每一天
去签到