SQL是Oracle数据库与上层业务交互的标准语言,无论是应用开发人员,还是DBA运维工程师,都必须熟练掌握DDL、DML、DQL、PL/SQL整套语法体系。我是风哥,在大量项目实施工作中,很多生产故障根源来自不规范的SQL编写:约束缺失引发脏数据、索引设计不合理造成全表扫描、长事务忘记提交引发锁阻塞、PL/SQL游标泄露产生ORA‑01000报错。风哥 itpux-com
本文基于Oracle19c单机数据库,硬件规格为**单节点64G内存、8CPU**,数据库实例与数据库名称`fgedudb`,业务操作用户`fgedu`,本地软件路径全部统一替换为`/fgedudb`。教程完整覆盖SQL语言分类、数据类型、DDL对象管理、DML事务控制、DQL各类查询、内置函数、多表连接、子查询、分析函数、PL/SQL编程(匿名块、存储过程、函数、触发器、程序包),配套大量可直接运行的实战SQL脚本,兼顾业务开发规范与DBA运维视角。为后续SQL性能调优、故障排查打下扎实基础。
**内容大纲:**
1. Oracle SQL与PL/SQL体系介绍,64G/8CPU实例相关参数基线,实验环境准备
2. 基础理论:SQL五大语言分类、Oracle数据类型、表与约束原理、索引分类原理、视图/同义词/序列原理、事务与锁机制、PL/SQL程序单元原理
3. 查询理论:基础查询、过滤排序、聚合分组、多表连接、子查询、分析窗口函数、集合运算原理
4. PL/SQL理论:变量、游标、异常处理、存储过程、函数、触发器、程序包原理
5. 完整实战操作:环境初始化、DDL建表/约束/索引/视图/同义词/序列、DML增删改事务演练、全套DQL查询实战、内置函数实战、PL/SQL匿名块、存储过程、函数、触发器、包开发、数据字典查询、执行计划分析
6. SQL开发生产规范与巡检脚本
7. 全文总结,生产SQL开发最佳实践
## 一、Oracle SQL开发基础理论
### 1.1 Oracle SQL与PL/SQL整体体系
Oracle结构化查询语言分为五大类别:DDL数据定义语言、DML数据操纵语言、DQL数据查询语言、DCL数据控制语言、TCL事务控制语言。
– **DDL(Data Definition Language)**:`CREATE / ALTER / DROP / TRUNCATE / RENAME`,管理数据库对象,DDL执行会隐式提交事务,执行完毕不可rollback回滚,生产执行DDL必须规划变更窗口。风哥教程 itpux_com
– **DML(Data Manipulation Language)**:`INSERT / UPDATE / DELETE / MERGE`,业务数据增删改,不会自动提交,依赖`COMMIT`提交、`ROLLBACK`回滚。
– **DQL(Data Query Language)**:`SELECT`查询语句,仅读取数据,不修改业务内容。
– **DCL(Data Control Language)**:`GRANT / REVOKE`权限授予回收。
– **TCL(Transaction Control Language)**:`COMMIT / ROLLBACK / SAVEPOINT`事务保存点控制。
PL/SQL是Oracle对SQL的过程化扩展,支持变量定义、条件判断、循环逻辑、异常捕获,可编写匿名块、存储过程、自定义函数、触发器、程序包,将业务逻辑封装在数据库服务端运行。网上搜索风哥教程可以学习全套数据库教程
>
> 基线环境硬件64G内存8CPU,实例`fgedudb`,与SQL开发相关关键spfile参数:
>
>
> | 参数 | 参数值 | 说明 |
> | — | — | — |
> | memory_target | 48G | 实例总内存,预留16G操作系统 |
> | processes | 2000 | 最大并发进程,适配业务大量会话连接 |
> | open_cursors | 500 | 单会话最大打开游标,防止游标泄露ORA‑01000报错 |
> | session_cached_cursors | 300 | 会话游标缓存,降低SQL软解析CPU消耗 |
> | undo_retention | 900 | undo保留900秒,闪回查询、一致性读依赖undo数据 |
> | parallel_max_servers | 16 | 并行执行进程上限,适配8CPU硬件规格 |
### 1.2 Oracle常用数据类型理论
1. **字符类型**
`VARCHAR2(n)`可变长字符串,Oracle19c最大支持32767字节;`CHAR(n)`定长字符;`CLOB`大文本对象,用于存储超长文本文档。
2. **数值类型**
`NUMBER(p,s)`,p总有效数字位数,s小数位;业务金额强烈推荐number类型,避免浮点数精度丢失;`INTEGER`属于number子类型。
3. **时间日期类型**
`DATE`保存年月日时分秒,业务系统首选;`TIMESTAMP`支持毫秒、微秒时间戳;`TIMESTAMP WITH TIME ZONE`带时区时间。
4. **二进制类型**
`BLOB`二进制大对象,保存图片、文件二进制字节流。
### 1.3 表、五类约束原理
表是schema最核心对象,约束保障业务数据完整性,分为五类约束:
1. **PRIMARY KEY主键**:非空+唯一,一张表仅允许一个主键;
2. **UNIQUE唯一约束**:字段值不可重复,允许NULL空值;
3. **NOT NULL非空约束**:列不允许存储NULL;
4. **CHECK检查约束**,自定义字段取值校验规则;
5. **FOREIGN KEY外键约束**,参照父表主键,维护主从表参照完整性。
>
> 生产提示:大批量数据导入场景,可临时disable约束,导入完成后enable validate,提升导入性能;外键会增加DML开销,大并发OLTP业务需要权衡。
### 1.4 索引原理理论
索引是优化查询性能的数据库对象,主流索引类型:
1. **B‑Tree普通索引**:Oracle默认索引,适合等值、范围过滤条件;
2. **复合索引**:多字段联合B‑Tree索引,遵循前缀匹配原则;
3. **函数索引**:对函数运算结果建立索引,解决where子句使用函数导致索引失效问题;
4. **唯一索引**:索引字段不允许重复;
5. **位图索引**:适合数据仓库低并发DML场景,OLTP业务禁止大量使用位图索引。
>
> 生产误区:索引不是越多越好,索引会加重insert/update/delete开销,DML频繁业务严控索引数量。
### 1.5 视图、同义词、序列理论
1. **视图VIEW**:本质是存储的SELECT语句,不存储物理数据;简化复杂多表查询,做权限隔离;分为普通视图、只读视图(WITH READ ONLY)。
2. **同义词SYNONYM**:对象别名,私有同义词仅当前schema可见,公共同义词全库可见,需要DBA权限创建。
3. **SEQUENCE序列**,生成自增数字,用于业务主键ID;`NEXTVAL`取下一个序列值,`CURRVAL`读取当前值;CACHE缓存序列值,减少数据库字典锁竞争,提升高并发性能。风哥数据库教程 itpux-com
### 1.6 事务、锁机制理论
Oracle事务从第一条DML语句开启,以commit或者rollback结束。未提交修改仅当前会话可见,其他会话读取undo回滚段旧数据,实现多版本一致性读。
DML操作产生行级锁,行锁不会阻塞普通SELECT查询;长事务不提交会持续持有行锁,引发业务会话阻塞等待。
`TRUNCATE`属于DDL语句,清空表全部数据,释放段空间,不生成undo,不可回滚,执行速度远高于delete全表删除,生产谨慎使用。
### 1.7 DQL查询语法结构理论
完整SELECT语法执行顺序:
`SELECT‑FROM‑WHERE‑GROUP BY‑HAVING‑ORDER BY`
– WHERE:分组**之前**做行过滤;
– HAVING:分组聚合**之后**过滤聚合结果;
多表连接分为:INNER JOIN内连接、LEFT/RIGHT外连接、FULL全外连接;推荐ANSI标准JOIN写法,可读性优于Oracle旧风格`(+)`写法。
子查询分为单行子查询、多行子查询IN/ANY/ALL、关联子查询、from派生表子查询。分析窗口函数OVER(),实现分组内排名、累计求和,是报表统计核心语法。
### 1.8 PL/SQL程序单元理论
1. **匿名块**:临时PL/SQL代码,不持久化存储数据库,执行完毕消失,用于脚本、调试;
2. **存储过程PROCEDURE**:封装业务逻辑,支持入参、出参,数据库持久保存;
3. **自定义FUNCTION函数**,必须有返回值,可以直接用于SQL语句;
4. **触发器TRIGGER**:表发生INSERT/UPDATE/DELETE时自动触发执行;
5. **程序包PACKAGE**:包头定义接口,包体实现具体逻辑,模块化管理过程、函数、变量。
PL/SQL核心要素:变量、%TYPE/%ROWTYPE类型、游标、异常捕获处理;生产必须注意游标关闭,防止游标泄露造成ORA‑01000报错。
## 二、生产完整实战操作
>
> 说明:操作系统RHEL7,硬件64G内存8CPU;数据库实例`fgedudb`,全部路径替换`/fgedudb`;sys用户执行环境初始化,业务操作切换`fgedu`用户;所有脚本务必先测试环境验证,生产执行前完成变更评审。
### 2.1 实验环境初始化(sys用户执行)
登录数据库
“`
su – oracle
export ORACLE_BASE=/fgedudb/app
export ORACLE_HOME=/fgedudb/app/product/19.0.0/dbhome_1
export ORACLE_SID=fgedudb
sqlplus / as sysdba
“`
创建业务表空间、临时表空间,业务用户fgedu,授予基础权限
“`
CREATE TABLESPACE fgedu_biz
DATAFILE ‘+DATA/fgedudb/fgedu_biz01.dbf’ SIZE 2G AUTOEXTEND ON NEXT 512M MAXSIZE 15G
EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;
CREATE TEMPORARY TABLESPACE fgedu_temp
TEMPFILE ‘+DATA/fgedudb/fgedu_temp01.tmp’ SIZE 1G AUTOEXTEND ON NEXT 256M MAXSIZE 10G;
CREATE USER fgedu IDENTIFIED BY Fgedu@123
DEFAULT TABLESPACE fgedu_biz
TEMPORARY TABLESPACE fgedu_temp
ACCOUNT UNLOCK;
GRANT CREATE SESSION,CREATE TABLE,CREATE VIEW,CREATE SEQUENCE,CREATE SYNONYM,CREATE PROCEDURE,CREATE TRIGGER TO fgedu;
GRANT CONNECT,RESOURCE TO fgedu;
GRANT UNLIMITED TABLESPACE TO fgedu;
ALTER SESSION SET CURRENT_SCHEMA=fgedu;
“`
### 2.2 DDL实战:建表与五类约束
业务场景:`fgedu_dept`部门主表,`fgedu_emp`员工从表,主外键关联。
“`
–部门主表
CREATE TABLE fgedu_dept (
dept_id NUMBER(8),
dept_name VARCHAR2(60) NOT NULL,
location VARCHAR2(80),
create_time DATE DEFAULT SYSDATE,
CONSTRAINT pk_fgedu_dept PRIMARY KEY(dept_id)
);
–员工从表,主键、非空、唯一、检查、外键五类约束完整示例
CREATE TABLE fgedu_emp (
emp_id NUMBER(10),
emp_name VARCHAR2(40) NOT NULL,
salary NUMBER(12,2),
job VARCHAR2(50),
dept_id NUMBER(8),
hire_date DATE,
email VARCHAR2(100),
CONSTRAINT pk_fgedu_emp PRIMARY KEY(emp_id),
CONSTRAINT ck_emp_salary CHECK(salary>0),
CONSTRAINT uk_emp_email UNIQUE(email),
CONSTRAINT fk_emp_dept FOREIGN KEY(dept_id) REFERENCES fgedu_dept(dept_id)
);
DESC fgedu_dept;
DESC fgedu_emp;
“`
alter修改表结构实战
“`
–新增字段
ALTER TABLE fgedu_emp ADD mobile VARCHAR2(30);
–修改字段长度
ALTER TABLE fgedu_emp MODIFY mobile VARCHAR2(40);
–重命名字段
ALTER TABLE fgedu_emp RENAME COLUMN mobile TO phone;
–删除字段
ALTER TABLE fgedu_emp DROP COLUMN phone;
–禁用/启用约束
ALTER TABLE fgedu_emp DISABLE CONSTRAINT fk_emp_dept;
ALTER TABLE fgedu_emp ENABLE CONSTRAINT fk_emp_dept;
“`
### 2.3 DDL实战:各类索引创建管理
“`
–普通B‑Tree索引
CREATE INDEX idx_fgedu_emp_dept ON fgedu_emp(dept_id);
–复合多列索引
CREATE INDEX idx_fgedu_emp_job_sal ON fgedu_emp(job,salary);
–函数索引,业务经常upper(emp_name)查询
CREATE INDEX idx_fgedu_emp_name_upper ON fgedu_emp(UPPER(emp_name));
–查询当前schema索引字典
SELECT index_name,table_name,index_type FROM user_indexes WHERE table_name IN(‘FGEDU_EMP’,’FGEDU_DEPT’);
–删除索引
DROP INDEX idx_fgedu_emp_job_sal;
“`
### 2.4 DDL实战:视图、同义词、序列
#### 2.4.1 视图
“`
–普通关联视图
CREATE VIEW v_fgedu_emp_dept AS
SELECT e.emp_id,e.emp_name,e.salary,e.job,d.dept_name
FROM fgedu_emp e
JOIN fgedu_dept d ON e.dept_id = d.dept_id;
–只读视图,禁止DML修改视图
CREATE VIEW v_fgedu_emp_readonly AS
SELECT emp_id,emp_name,salary,job FROM fgedu_emp
WITH READ ONLY;
SELECT * FROM v_fgedu_emp_dept WHERE ROWNUM<=10;
“`
#### 2.4.2 同义词
“`
CREATE SYNONYM syn_emp_view FOR v_fgedu_emp_dept;
SELECT * FROM syn_emp_view WHERE ROWNUM<=5;
DROP SYNONYM syn_emp_view;
“`
####2.4.3 序列,用于员工主键自增
“`
CREATE SEQUENCE seq_fgedu_emp_id
INCREMENT BY 1
START WITH 1001
MAXVALUE 99999999
NOCYCLE
CACHE 100;
SELECT seq_fgedu_emp_id.NEXTVAL FROM DUAL;
SELECT seq_fgedu_emp_id.CURRVAL FROM DUAL;
ALTER SEQUENCE seq_fgedu_emp_id CACHE 200;
“`
### 2.5 DML与事务实战:INSERT / UPDATE / DELETE / MERGE
>
> DML不会自动提交;commit永久生效;rollback撤销未提交变更;savepoint设置保存点。
“`
–插入部门数据
INSERT INTO fgedu_dept(dept_id,dept_name,location) VALUES(10,’研发部’,’一号研发大楼’);
INSERT INTO fgedu_dept(dept_id,dept_name,location) VALUES(20,’销售部’,’二号业务大楼’);
INSERT INTO fgedu_dept(dept_id,dept_name,location) VALUES(30,’人事部’,’行政中心’);
COMMIT;
–插入员工,使用序列生成主键emp_id
INSERT INTO fgedu_emp(emp_id,emp_name,salary,job,dept_id,hire_date,email)
VALUES(seq_fgedu_emp_id.NEXTVAL,’张三’,18000,’开发工程师’,10,TO_DATE(‘2022‑03‑15′,’yyyy‑mm‑dd’),’zhangsan@fgedu.com’);
INSERT INTO fgedu_emp(emp_id,emp_name,salary,job,dept_id,hire_date,email)
VALUES(seq_fgedu_emp_id.NEXTVAL,’李四’,15000,’测试工程师’,10,TO_DATE(‘2021‑08‑20′,’yyyy‑mm‑dd’),’lisi@fgedu.com’);
INSERT INTO fgedu_emp(emp_id,emp_name,salary,job,dept_id,hire_date,email)
VALUES(seq_fgedu_emp_id.NEXTVAL,’王五’,22000,’销售经理’,20,TO_DATE(‘2020‑11‑05′,’yyyy‑mm‑dd’),’wangwu@fgedu.com’);
COMMIT;
–update更新
UPDATE fgedu_emp SET salary=19000 WHERE emp_name=’张三’;
SAVEPOINT sp_01;
ROLLBACK TO sp_01;
–delete删除
DELETE FROM fgedu_emp WHERE emp_id=1003;
ROLLBACK;
–merge 合并语句,匹配更新,不匹配插入
MERGE INTO fgedu_dept t1
USING (SELECT 40 AS dept_id,’运维部’ AS dept_name,’运维中心’ AS location FROM DUAL) t2
ON (t1.dept_id = t2.dept_id)
WHEN MATCHED THEN UPDATE SET dept_name=t2.dept_name
WHEN NOT MATCHED THEN INSERT(dept_id,dept_name,location) VALUES(t2.dept_id,t2.dept_name,t2.location);
COMMIT;
“`
### 2.6 DQL完整查询实战
#### 2.6.1 基础查询、where过滤、order by排序、伪列rownum分页
“`
SELECT emp_id,emp_name,salary,job,dept_id FROM fgedu_emp;
–条件过滤
SELECT emp_name,salary,job FROM fgedu_emp WHERE salary >16000;
SELECT emp_name,salary,dept_id FROM fgedu_emp WHERE dept_id=10 AND salary>14000;
–模糊匹配
SELECT emp_name,email FROM fgedu_emp WHERE email LIKE ‘zhang%’;
–排序
SELECT emp_name,salary FROM fgedu_emp ORDER BY salary DESC;
–分页,工资最高2条
SELECT * FROM (SELECT emp_name,salary FROM fgedu_emp ORDER BY salary DESC) WHERE ROWNUM <=2;
“`
#### 2.6.2 聚合函数,group by分组,having过滤
常用聚合:`SUM,AVG,MAX,MIN,COUNT`
“`
SELECT dept_id,
AVG(salary) avg_sal,
MAX(salary) max_sal,
MIN(salary) min_sal,
COUNT(emp_id) emp_cnt
FROM fgedu_emp
GROUP BY dept_id
HAVING AVG(salary) > 14000;
“`
#### 2.6.3 ANSI标准多表连接
“`
–内连接INNER JOIN
SELECT e.emp_name,e.salary,d.dept_name
FROM fgedu_emp e
INNER JOIN fgedu_dept d ON e.dept_id = d.dept_id;
–左外连接LEFT JOIN,保留全部员工,部门不存在也展示员工
SELECT e.emp_name,e.salary,d.dept_name
FROM fgedu_emp e
LEFT JOIN fgedu_dept d ON e.dept_id = d.dept_id;
–右外连接RIGHT JOIN
SELECT e.emp_name,e.salary,d.dept_name
FROM fgedu_emp e
RIGHT JOIN fgedu_dept d ON e.dept_id = d.dept_id;
“`
#### 2.6.4 子查询实战
“`
–单行子查询,工资高于李四
SELECT emp_name,salary FROM fgedu_emp
WHERE salary > (SELECT salary FROM fgedu_emp WHERE emp_name=’李四’);
–多行子查询 IN
SELECT emp_name,salary FROM fgedu_emp
WHERE dept_id IN (SELECT dept_id FROM fgedu_dept WHERE dept_name IN(‘研发部’,’销售部’));
–关联子查询,高于本部门平均薪资员工
SELECT t.emp_name,t.salary,t.dept_id,t.dept_avg
FROM (
SELECT e.emp_name,e.salary,e.dept_id,d.avg_dept_sal
FROM fgedu_emp e
JOIN (SELECT dept_id,AVG(salary) avg_dept_sal FROM fgedu_emp GROUP BY dept_id) d
ON e.dept_id=d.dept_id
) t WHERE t.salary > t.avg_dept_sal;
“`
####2.6.5 分析窗口函数实战(报表统计常用)
“`
–部门内部薪资排名 RANK()
SELECT emp_name,salary,dept_id,
RANK() OVER(PARTITION BY dept_id ORDER BY salary DESC) sal_rank
FROM fgedu_emp;
–部门累计求和
SELECT emp_name,salary,dept_id,
SUM(salary) OVER(PARTITION BY dept_id ORDER BY salary ROWS UNBOUNDED PRECEDING) sum_acc
FROM fgedu_emp;
“`
### 2.7 Oracle常用内置函数实战
“`
–虚表dual测试函数
SELECT SYSDATE,SYSTIMESTAMP FROM DUAL;
–字符串函数 UPPER LOWER SUBSTR CONCAT
SELECT UPPER(emp_name),LOWER(emp_name),SUBSTR(emp_name,1,2) FROM fgedu_emp;
–日期函数 ADD_MONTHS TRUNC
SELECT emp_name,hire_date,ADD_MONTHS(hire_date,6) half_year FROM fgedu_emp;
–NVL空处理,DECODE条件判断
SELECT emp_name,NVL(mobile,’未填写’) mobile,
DECODE(job,’开发工程师’,’技术岗’,’销售经理’,’业务岗’,’其他岗位’) job_type
FROM fgedu_emp;
–数字函数ROUND
SELECT salary,ROUND(salary/1000,2) salary_k FROM fgedu_emp;
–CASE WHEN多条件判断
SELECT emp_name,salary,
CASE WHEN salary >=20000 THEN ‘高薪’
WHEN salary >=15000 THEN ‘中薪’
ELSE ‘基础薪资’ END AS salary_level
FROM fgedu_emp;
“`
### 2.8 PL/SQL实战:匿名块、游标、异常捕获
“`
DECLARE
v_dept_id NUMBER :=10;
v_emp_cnt NUMBER;
BEGIN
SELECT COUNT(emp_id) INTO v_emp_cnt FROM fgedu_emp WHERE dept_id=v_dept_id;
DBMS_OUTPUT.PUT_LINE(‘部门’||v_dept_id||’员工总数:’||v_emp_cnt);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(‘没有查询到数据’);
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE(‘返回多条记录异常’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(‘其他异常,错误码:’||SQLCODE||’,信息:’||SQLERRM);
END;
/
“`
带显式游标循环示例
“`
DECLARE
CURSOR cur_emp IS SELECT emp_name,salary FROM fgedu_emp WHERE dept_id=10;
v_emp_name fgedu_emp.emp_name%TYPE;
v_sal fgedu_emp.salary%TYPE;
BEGIN
OPEN cur_emp;
LOOP
FETCH cur_emp INTO v_emp_name,v_sal;
EXIT WHEN cur_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(‘员工:’||v_emp_name||’,薪资:’||v_sal);
END LOOP;
CLOSE cur_emp;
END;
/
“`
### 2.9 PL/SQL存储过程、自定义函数实战
####2.9.1 存储过程,传入部门ID,输出员工数量
“`
CREATE OR REPLACE PROCEDURE p_get_dept_emp_cnt(p_dept_id IN NUMBER,p_emp_count OUT NUMBER)
IS
BEGIN
SELECT COUNT(emp_id) INTO p_emp_count FROM fgedu_emp WHERE dept_id=p_dept_id;
END;
/
–调用存储过程
DECLARE
v_cnt NUMBER;
BEGIN
p_get_dept_emp_cnt(10,v_cnt);
DBMS_OUTPUT.PUT_LINE(‘部门员工总数:’||v_cnt);
END;
/
“`
####2.9.2 自定义函数,返回部门平均薪资
“`
CREATE OR REPLACE FUNCTION f_get_dept_avg_sal(p_dept_id IN NUMBER) RETURN NUMBER
IS
v_avg_sal NUMBER;
BEGIN
SELECT AVG(salary) INTO v_avg_sal FROM fgedu_emp WHERE dept_id=p_dept_id;
RETURN v_avg_sal;
END;
/
–函数可以直接在SQL中调用
SELECT dept_id,f_get_dept_avg_sal(dept_id) avg_sal FROM fgedu_dept;
“`
### 2.10 PL/SQL触发器实战:员工薪资变更日志触发器
模拟业务:更新员工薪资,自动写入变更日志表
“`
CREATE TABLE fgedu_emp_sal_log(
log_id NUMBER PRIMARY KEY,
emp_id NUMBER,
old_sal NUMBER(12,2),
new_sal NUMBER(12,2),
update_time DATE DEFAULT SYSDATE
);
CREATE SEQUENCE seq_log_id START WITH 1 INCREMENT BY 1 NOCACHE;
CREATE OR REPLACE TRIGGER tri_emp_sal_change
BEFORE UPDATE OF salary ON fgedu_emp
FOR EACH ROW
BEGIN
INSERT INTO fgedu_emp_sal_log(log_id,emp_id,old_sal,new_sal)
VALUES(seq_log_id.NEXTVAL,:OLD.emp_id,:OLD.salary,:NEW.salary);
END;
/
–测试触发器
UPDATE fgedu_emp SET salary=19500 WHERE emp_name=’张三’;
COMMIT;
SELECT * FROM fgedu_emp_sal_log;
“`
### 2.11 PL/SQL程序包PACKAGE实战
包头定义对外接口,包体实现逻辑
“`
–包头
CREATE OR REPLACE PACKAGE pkg_fgedu_emp_mgr AS
PROCEDURE p_query_dept_emp(p_dept_id IN NUMBER,p_cnt OUT NUMBER);
FUNCTION f_query_avg_sal(p_dept_id IN NUMBER) RETURN NUMBER;
END pkg_fgedu_emp_mgr;
/
–包体
CREATE OR REPLACE PACKAGE BODY pkg_fgedu_emp_mgr AS
PROCEDURE p_query_dept_emp(p_dept_id IN NUMBER,p_cnt OUT NUMBER)
IS
BEGIN
SELECT COUNT(emp_id) INTO p_cnt FROM fgedu_emp WHERE dept_id=p_dept_id;
END p_query_dept_emp;
FUNCTION f_query_avg_sal(p_dept_id IN NUMBER) RETURN NUMBER
IS
v_avg NUMBER;
BEGIN
SELECT AVG(salary) INTO v_avg FROM fgedu_emp WHERE dept_id=p_dept_id;
RETURN v_avg;
END f_query_avg_sal;
END pkg_fgedu_emp_mgr;
/
–调用包内过程函数
DECLARE
v_c NUMBER;
BEGIN
pkg_fgedu_emp_mgr.p_query_dept_emp(10,v_c);
DBMS_OUTPUT.PUT_LINE(‘部门人数:’||v_c||’,平均薪资:’||pkg_fgedu_emp_mgr.f_query_avg_sal(10));
END;
/
“`
### 2.12 数据字典视图查询(开发DBA高频使用)
“`
–查询当前用户全部表
SELECT table_name FROM user_tables;
–约束信息
SELECT constraint_name,table_name,constraint_type FROM user_constraints;
–索引
SELECT index_name,table_name FROM user_indexes;
–视图
SELECT view_name FROM user_views;
–序列
SELECT sequence_name FROM user_sequences;
–存储过程、函数、包、触发器状态,检查是否INVALID失效
SELECT object_name,object_type,status FROM user_objects WHERE status!=’VALID’;
–查看存储过程源代码
SELECT text FROM user_source WHERE name=’P_GET_DEPT_EMP_CNT’;
“`
### 2.13 执行计划分析,SQL调优基础实战
“`
–生成执行计划
EXPLAIN PLAN FOR
SELECT e.emp_name,d.dept_name FROM fgedu_emp e JOIN fgedu_dept d ON e.dept_id=d.dept_id WHERE e.salary>15000;
–查看执行计划
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
“`
### 2.14 SQL开发综合巡检脚本,保存为`/fgedudb/soft/sql_check_fgedu.sh`
“`
#!/bin/bash
export ORACLE_BASE=/fgedudb/app
export ORACLE_HOME=/fgedudb/app/product/19.0.0/dbhome_1
export ORACLE_SID=fgedudb
export PATH=$ORACLE_HOME/bin:$PATH
echo “====================SQL对象有效性检查====================”
sqlplus -S / as sysdba <<EOF
set pagesize 100 linesize 160
ALTER SESSION SET CURRENT_SCHEMA=fgedu;
SELECT object_name,object_type,status FROM user_objects WHERE status<>’VALID’;
SELECT table_name FROM user_tables;
SELECT constraint_name,table_name,constraint_type FROM user_constraints;
EOF
echo “====================数据库实例SQL相关参数====================”
sqlplus -S / as sysdba <<EOF
SHOW PARAMETER open_cursors;
SHOW PARAMETER session_cached_cursors;
SHOW PARAMETER undo_retention;
EOF
“`
赋予执行权限运行
“`
chmod +x /fgedudb/soft/sql_check_fgedu.sh
./fgedudb/soft/sql_check_fgedu.sh
“`
## 三、总结
SQL与PL/SQL是Oracle数据库的基础能力,我是风哥,在多年项目实施中,大量业务性能故障、数据异常,根源都是不规范的SQL与PL/SQL代码:缺少约束产生脏数据,索引设计不合理引发全表扫描,长事务忘记commit造成锁阻塞,游标没有关闭引发游标泄露ORA‑01000,PL/SQL对象编译失效未发现直接上线。风哥 itpux-com
本文基于硬件规格**64G内存、8CPU**的Oracle19c单机实例,全部路径替换为`/fgedudb`,数据库实例`fgedudb`,业务用户`fgedu`。完整覆盖DDL对象管理、DML事务控制、DQL各类查询、内置函数、多表连接、子查询、窗口分析函数,以及PL/SQL匿名块、存储过程、函数、触发器、程序包全套内容,配套大量可直接复用SQL脚本,同时包含数据字典查询、执行计划分析、对象有效性巡检脚本。
生产环境必须遵守几条核心开发规范:
1. DDL语句会隐式提交事务,上线必须预留变更窗口,变更前做好备份;TRUNCATE属于DDL,严禁随意在业务库执行;
2. DML操作必须显式commit,避免长事务持有行锁;合理使用savepoint保存点;
3. 索引不是越多越好,OLTP业务控制索引数量,优先使用复合索引、函数索引解决索引失效场景;
4. PL/SQL开发务必处理异常,游标使用完毕必须关闭,防止游标泄露;上线前检查对象status为VALID;
5. 查询优先使用ANSI标准JOIN语法;多表关联、子查询注意ORA‑01427单行子查询返回多行等常见报错;
6. 上线前使用explain plan分析执行计划,提前识别全表扫描性能风险。网上搜索风哥教程可以学习全套数据库教程
掌握SQL语法只是起点,后续还需要深入学习SQL性能调优、锁与等待事件分析、分区表、物化视图、闪回技术。运维、开发人员不仅要会写出返回结果的SQL,更需要理解SQL在数据库内部的执行逻辑,才能写出高性能、健壮稳定的业务代码。
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
