数据库-Oracle:常用语法 / Oracle 核心知识技能梳理

Oracle 数据库是业界领先的关系型数据库管理系统,其 SQL 语法在遵循 ANSI/ISO 标准的基础上,扩展了大量特有功能。以下从基础到高级,系统性地梳理 Oracle 的核心语法。

说明 :本文聚焦于 Oracle 特有的 SQL 语法和功能。通用的 SQL 语法(如 SELECT、INSERT、UPDATE、DELETE 等)与其他数据库类似,不再赘述。

一、数据定义语言(DDL)

DDL 用于定义或更改数据库结构。

1.1 创建表(CREATE TABLE)

sql 复制代码
-- 标准建表语法
CREATE TABLE employees (
    id           NUMBER(6) PRIMARY KEY,
    name         VARCHAR2(50) NOT NULL,
    salary       NUMBER(10,2),
    hire_date    DATE DEFAULT SYSDATE,
    status       VARCHAR2(20) CHECK (status IN ('active', 'inactive')),
    dept_id      NUMBER(4),
    CONSTRAINT emp_dept_fk FOREIGN KEY (dept_id) REFERENCES departments(id)
);

Oracle 特有数据类型

数据类型 说明
VARCHAR2(n) 变长字符串(推荐使用,最多4000字符)
NUMBER(p,s) 数值类型,p=总位数,s=小数位数
DATE 日期时间(精确到秒)
TIMESTAMP 高精度日期时间
CLOB 大文本对象(最多4GB)
BLOB 二进制大对象
ROWID 伪列,表示行物理地址

1.2 修改表结构(ALTER TABLE)

sql 复制代码
-- 添加列
ALTER TABLE employees ADD (email VARCHAR2(100));

-- 修改列属性
ALTER TABLE employees MODIFY (email VARCHAR2(200));

-- 重命名列
ALTER TABLE employees RENAME COLUMN email TO emp_email;

-- 删除列
ALTER TABLE employees DROP COLUMN emp_email;

-- 添加约束
ALTER TABLE employees ADD CONSTRAINT emp_email_uk UNIQUE (emp_email);

1.3 删除表(DROP TABLE)

sql 复制代码
-- 删除表(不可恢复,不经过回收站)
DROP TABLE employees PURGE;

-- 删除表但保留到回收站(可闪回恢复)
DROP TABLE employees;

-- 查看回收站
SHOW RECYCLEBIN;

-- 从回收站恢复表
FLASHBACK TABLE employees TO BEFORE DROP;

-- 清空表(重置高水位线,不可回滚)
TRUNCATE TABLE employees;

1.4 创建索引(CREATE INDEX)

sql 复制代码
-- B树索引(默认)
CREATE INDEX idx_emp_name ON employees(name);

-- 唯一索引
CREATE UNIQUE INDEX idx_emp_email ON employees(email);

-- 位图索引(适用于低基数列)
CREATE BITMAP INDEX idx_emp_status ON employees(status);

-- 函数索引
CREATE INDEX idx_emp_upper_name ON employees(UPPER(name));

-- 组合索引
CREATE INDEX idx_emp_dept_salary ON employees(dept_id, salary);

1.5 创建视图(CREATE VIEW)

sql 复制代码
-- 普通视图
CREATE VIEW emp_view AS
SELECT id, name, salary FROM employees WHERE salary > 5000;

-- 带检查选项的视图(禁止修改视图范围外的数据)
CREATE VIEW emp_active_view AS
SELECT * FROM employees WHERE status = 'active'
WITH CHECK OPTION;

-- 只读视图
CREATE VIEW emp_readonly_view AS
SELECT id, name FROM employees
WITH READ ONLY;

-- 物化视图(预计算结果集,提升查询性能)
CREATE MATERIALIZED VIEW mv_dept_salary
REFRESH COMPLETE ON DEMAND
AS
SELECT dept_id, AVG(salary) avg_sal FROM employees GROUP BY dept_id;

二、Oracle 特有的数据操纵语言(DML)

2.1 MERGE 语句(合并操作)

MERGE 是 Oracle 特有的语法,可根据条件决定 INSERT 还是 UPDATE,相当于「有则更新,无则插入」。

sql 复制代码
MERGE INTO employees e
USING new_employees n
ON (e.id = n.id)
WHEN MATCHED THEN
    UPDATE SET e.name = n.name, e.salary = n.salary
WHEN NOT MATCHED THEN
    INSERT (id, name, salary) VALUES (n.id, n.name, n.salary);

2.2 INSERT ALL(多表插入)

sql 复制代码
-- 无条件多表插入(每行插入到两个表)
INSERT ALL
    INTO sales_history VALUES (id, amount, date)
    INTO sales_archive VALUES (id, amount, date)
SELECT id, amount, date FROM current_sales;

-- 有条件多表插入
INSERT FIRST
    WHEN amount > 10000 THEN INTO large_orders VALUES (id, amount)
    WHEN amount > 5000 THEN INTO medium_orders VALUES (id, amount)
    ELSE INTO small_orders VALUES (id, amount)
SELECT id, amount FROM orders;

2.3 RETURNING 子句

获取 DML 操作影响的数据。

sql 复制代码
-- 获取插入后的值
INSERT INTO employees (id, name) VALUES (1, '张三')
RETURNING id, name INTO :out_id, :out_name;

-- 获取更新后的值(Oracle 特有)
UPDATE employees SET salary = salary * 1.1
WHERE dept_id = 10
RETURNING salary INTO :new_salary;

-- 获取删除前的值
DELETE FROM employees WHERE id = 100
RETURNING name, salary INTO :old_name, :old_salary;

三、高级数据处理与多表输出

Oracle 在处理"一源多目标"的数据流转时,提供了比标准 SQL 更强大的语法,以及 PL/SQL 层面的批量处理能力。

3.1 多表插入的高级变种

除了基础的 INSERT ALL,还有以下细节:

3.1.1 插入到远程表(DB Link)

sql 复制代码
INSERT ALL
    INTO local_orders VALUES (id, amount)
    INTO orders@remote_db VALUES (id, amount)
SELECT id, amount FROM staging_orders;

3.1.2 结合 WITH子句的复杂多表插入

sql 复制代码
WITH processed_data AS (
    SELECT id, amount, 
           CASE WHEN amount > 10000 THEN 'HIGH' ELSE 'NORMAL' END AS level
    FROM raw_sales
)
INSERT ALL
    INTO high_value_sales SELECT id, amount FROM processed_data WHERE level = 'HIGH'
    INTO normal_sales SELECT id, amount FROM processed_data WHERE level = 'NORMAL'
SELECT * FROM processed_data;

3.1.3 多表插入中的序列使用

sql 复制代码
INSERT ALL
    INTO table_a (id, name) VALUES (seq_a.NEXTVAL, name)
    INTO table_b (id, name) VALUES (seq_b.NEXTVAL, name)
SELECT name FROM source_table;

3.2 PL/SQL 批量拆分(FORALL + BULK COLLECT)

当需要根据复杂的业务逻辑将数据拆分到多个表时,使用 PL/SQL 的批量绑定是性能最高的方式(比单条 INSERT 快 10-100 倍)。

场景 :将 source_table 的数据根据 type 字段拆分到 type_a_tabletype_b_table

sql 复制代码
DECLARE
    TYPE t_rec IS RECORD (
        id       NUMBER,
        data_val VARCHAR2(100),
        type     VARCHAR2(10)
    );
    TYPE t_tab IS TABLE OF t_rec;
    
    v_data t_tab;
    v_type_a_data t_tab := t_tab();
    v_type_b_data t_tab := t_tab();
BEGIN
    -- 1. 批量获取数据到内存(BULK COLLECT)
    SELECT id, data_val, type BULK COLLECT INTO v_data FROM source_table;
    
    -- 2. 在内存中拆分数据(无需上下文切换)
    FOR i IN 1..v_data.COUNT LOOP
        IF v_data(i).type = 'A' THEN
            v_type_a_data.EXTEND;
            v_type_a_data(v_type_a_data.LAST) := v_data(i);
        ELSIF v_data(i).type = 'B' THEN
            v_type_b_data.EXTEND;
            v_type_b_data(v_type_b_data.LAST) := v_data(i);
        END IF;
    END LOOP;
    
    -- 3. 批量插入到表 A(FORALL)
    FORALL i IN 1..v_type_a_data.COUNT
        INSERT INTO type_a_table VALUES v_type_a_data(i);
        
    -- 4. 批量插入到表 B(FORALL)
    FORALL i IN 1..v_type_b_data.COUNT
        INSERT INTO type_b_table VALUES v_type_b_data(i);
        
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE;
END;
/

3.3 客户端多格式输出(SQL*Plus / SQLcl)

在 Oracle 客户端工具中,将结果以多表格或特定格式输出。

3.3.1 拆分输出到文件(SPOOL)

sql 复制代码
SET PAGESIZE 50000 SET LINESIZE 200 SET FEEDBACK OFF
-- 输出表1
SPOOL /tmp/output_table1.txt
SELECT * FROM employees WHERE dept_id = 10;
SPOOL OFF
-- 输出表2
SPOOL /tmp/output_table2.txt
SELECT * FROM departments;
SPOOL OFF

3.3.2 生成 CSV 格式

sql 复制代码
SET HEAD OFF SET PAGES 0 SET FEED OFF SET COLSEP ','
SELECT 'ID,NAME,SALARY' FROM DUAL
UNION ALL
SELECT id, name, salary FROM employees;

3.3.3 动态列转行(PIVOT XML)

使用 PIVOT XML 生成包含所有动态列的 XML,方便程序解析。

sql 复制代码
SELECT *
FROM (SELECT dept_id, job, salary FROM employees)
PIVOT XML (
    SUM(salary)
    FOR job IN (ANY) 
);

四、Oracle 层次查询

Oracle 使用 CONNECT BYSTART WITH 来处理树形结构数据。

4.1 基本层次查询

sql 复制代码
SELECT employee_id, first_name, manager_id, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR employee_id = manager_id;

4.2 层次查询函数

sql 复制代码
SELECT employee_id, name,
       SYS_CONNECT_BY_PATH(name, '/') AS path,
       CONNECT_BY_ROOT name AS root_name
FROM employees
START WITH manager_id IS NULL
CONNECT BY NOCYCLE PRIOR employee_id = manager_id;

4.3 递归 WITH 子句(标准 SQL,Oracle 11g+)

sql 复制代码
WITH emp_tree (employee_id, name, manager_id, lvl) AS (
    SELECT employee_id, name, manager_id, 1 FROM employees WHERE manager_id IS NULL
    UNION ALL
    SELECT e.employee_id, e.name, e.manager_id, t.lvl + 1
    FROM employees e JOIN emp_tree t ON e.manager_id = t.employee_id
)
SELECT * FROM emp_tree;

五、Oracle 分析函数(窗口函数)

5.1 基本语法

sql 复制代码
function_name(column) OVER (
    [PARTITION BY 分组列]
    [ORDER BY 排序列]
    [ROWS|RANGE BETWEEN 起点 AND 终点]
)

5.2 排序与取值函数

sql 复制代码
SELECT 
    employee_id, salary,
    RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank,
    DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dense_rank,
    ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num,
    LAG(salary, 1, 0) OVER (ORDER BY hire_date) AS prev_salary,
    LEAD(salary, 1, 0) OVER (ORDER BY hire_date) AS next_salary
FROM employees;

5.3 累积与分组合并

sql 复制代码
SELECT 
    sale_date, amount,
    SUM(amount) OVER (ORDER BY sale_date) AS cumulative_sales,
    AVG(amount) OVER (ORDER BY sale_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_avg_7d,
    LISTAGG(employee_name, ',') WITHIN GROUP (ORDER BY employee_name) AS employees_list
FROM sales
GROUP BY department_id;

六、Oracle 特有查询功能

6.1 闪回查询(Flashback Query)

sql 复制代码
-- 查询15分钟前的数据
SELECT * FROM employees AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '15' MINUTE);

-- 查询行变化历史
SELECT versions_starttime, versions_operation, e.*
FROM employees VERSIONS BETWEEN TIMESTAMP SYSDATE - 1 AND SYSDATE
WHERE id = 100;

6.2 模型子句(MODEL Clause)

像 Excel 一样进行复杂计算。

sql 复制代码
SELECT month, sales, predicted_sales
FROM sales_forecast
MODEL
    PARTITION BY () 
    DIMENSION BY (month)
    MEASURES (sales, 0 AS predicted_sales)
    RULES (
        predicted_sales[FOR month FROM 2 TO 12] = sales[CV(month) - 1] * 1.1
    );

6.3 行转列(PIVOT)与列转行(UNPIVOT)

sql 复制代码
-- PIVOT 写法
SELECT * FROM (SELECT dept_id, job, salary FROM employees)
PIVOT (SUM(salary) FOR job IN ('SALESMAN' AS salesman_sal, 'CLERK' AS clerk_sal));

-- UNPIVOT 写法
SELECT dept_id, job_type, salary
FROM salary_matrix
UNPIVOT (salary FOR job_type IN (salesman_sal AS 'SALESMAN', clerk_sal AS 'CLERK'));

七、PL/SQL 程序开发

7.1 PL/SQL 块结构

sql 复制代码
DECLARE
    v_name VARCHAR2(50);
    e_no_data EXCEPTION;
BEGIN
    SELECT name INTO v_name FROM employees WHERE id = 100;
    DBMS_OUTPUT.PUT_LINE('姓名:' || v_name);
EXCEPTION
    WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('数据不存在');
    WHEN OTHERS THEN RAISE;
END;
/

7.2 存储过程、函数、触发器、包

(此处保留原始文档中的存储过程、函数、触发器和包的完整代码示例,结构不变)

八、性能优化与调优

8.1 执行计划分析

sql 复制代码
EXPLAIN PLAN FOR SELECT * FROM employees WHERE department_id = 10;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

8.2 优化器提示(Hints)

sql 复制代码
SELECT /*+ INDEX(employees idx_emp_name) */ * FROM employees WHERE name = '张三';
SELECT /*+ FULL(employees) */ * FROM employees;
SELECT /*+ PARALLEL(employees, 4) */ COUNT(*) FROM employees;

8.3 批量 SQL 与批量绑定(高性能)

sql 复制代码
DECLARE
    TYPE emp_table_type IS TABLE OF employees%ROWTYPE;
    v_emp_data emp_table_type;
BEGIN
    SELECT * BULK COLLECT INTO v_emp_data FROM employees WHERE department_id = 10;
    
    FORALL i IN 1..v_emp_data.COUNT
        UPDATE employees SET salary = salary * 1.1 
        WHERE employee_id = v_emp_data(i).employee_id;
    
    COMMIT;
END;
/

8.4 函数调用性能优化

sql 复制代码
-- 高效:先去重再执行函数
SELECT SQRT(department_id) 
FROM (SELECT DISTINCT department_id FROM employees);

九、Oracle 与 MySQL 语法差异速查

功能 Oracle MySQL
字符串拼接 `
分页查询 ROWNUM / OFFSET FETCH LIMIT
自增列 SEQUENCE + TRIGGER AUTO_INCREMENT
当前日期 SYSDATE NOW()
空值处理 NVL(expr, default) IFNULL(expr, default)
递归查询 CONNECT BY / WITH RECURSIVE WITH RECURSIVE

十、常用数据字典视图

视图 说明
USER_TABLES 当前用户拥有的表
USER_TAB_COLUMNS 当前用户表的列信息
USER_CONSTRAINTS 当前用户的约束信息
USER_INDEXES 当前用户的索引信息
USER_VIEWS 当前用户的视图
USER_SOURCE 当前用户的存储过程/函数源码
V$SESSION 数据库会话信息
V$SQL SQL 语句执行统计

十一、快速参考:常用 Oracle 特有语法

功能类别 核心语法 使用场景
层次查询 CONNECT BY PRIOR 树形结构(组织架构、菜单)
分析函数 RANK(), LAG(), LISTAGG() 排名、环比、字符串拼接
多表插入 INSERT ALL / FIRST 一源多目标插入
合并操作 MERGE INTO UPSERT(有则更新无则插入)
闪回查询 AS OF TIMESTAMP 查询历史数据
MODEL 子句 MODEL ... RULES Excel 风格计算
行列转换 PIVOT, UNPIVOT 行转列/列转行
批量处理 BULK COLLECT, FORALL 大规模 ETL 性能优化

十二、欢迎交流指正

相关推荐
qq_392690662 小时前
如何处理MongoDB分片集群的连接池耗尽危机_客户端连接与mongos到shard的连接乘数效应
jvm·数据库·python
叶小鸡2 小时前
Java 篇-项目实战-天机学堂(从0到1)-day8
数据库·oracle
qq_372154232 小时前
Python异步爬虫如何应对封IP_结合asyncio与代理池实现轮询请求
jvm·数据库·python
abc123456sdggfd2 小时前
php怎么处理跨域请求_php如何设置header解决跨域问题详解
jvm·数据库·python
zhangchaoxies2 小时前
如何在CSS中正确加载本地JPG背景图片
jvm·数据库·python
阿坤带你走近大数据2 小时前
Oracle报错-锁问题
数据库·oracle
zhougl9962 小时前
Redis 防止丢数据
java·redis·mybatis
旺仔小拳头..2 小时前
JDBC 基础: API、SQL 注入问题,事务、连接池
数据库·sql
NineData2 小时前
玖章算术NineData成功入选杭州市“新雏鹰”企业
运维·数据库·后端