Oracle SQL 基础入门教程:表的增删改查、视图、同义词与权限授权

如果你刚接触 Oracle 数据库,最核心的能力可以浓缩为四件事:建表与约束 → 数据的增删改查(DML)→ 用视图/同义词抽象访问层 → 用权限授权做安全隔离。本文按「从建表到授权」的完整主线,给你一份零废话、可直接复制运行的基础入门速查,配合经典的 HR 样例表(employees / departments / jobs)展开讲解。


TL;DR(先给结论)

  • 建表:优先用 VARCHAR2(变长字符串)、NUMBER(p,s)(数字)、DATE / TIMESTAMP(日期时间)、CLOB / BLOB(大对象);主键用 GENERATED ALWAYS AS IDENTITY(12c+)或 SEQUENCE + TRIGGER
  • 增删改查INSERT / SELECT / UPDATE / DELETE 是四大金刚;批量操作考虑 INSERT /*+ APPEND */ 直插、MERGE 做 UPSERT。
  • 分页:12c+ 统一用 OFFSET ... FETCH NEXT ... ROWS ONLY,比 ROWNUM 更直观。
  • 视图(View):把复杂查询封装成「虚表」,简化调用 + 做列级权限隔离;WITH READ ONLY 防止误改基础表。
  • 同义词(Synonym):给对象起别名,屏蔽 schema 前缀与数据库链路细节;私有同义词属个人,公共同义词全局可见。
  • 授权:系统权限(CREATE TABLE / CREATE VIEW 等)用 GRANT ... TO 用户;对象权限(SELECT / INSERT / UPDATE 某表)精确到对象;用 ROLE 把权限打包,少写一堆重复 GRANT

1) 建表与约束:把数据结构先定好

1.1 常用数据类型速查

类型说明示例
VARCHAR2(n)变长字符串(必选长度,单位字节或字符)VARCHAR2(100 CHAR)
CHAR(n)定长字符串(自动补空格)CHAR(10)
NUMBER(p,s)数字;p 总位数,s 小数位NUMBER(10,2) 金额、NUMBER(6) 整数
DATE日期时间(精度到秒,含年月日时分秒)DATE
TIMESTAMP(n)高精度时间(n 小数秒,0~9)TIMESTAMP(3)
TIMESTAMP WITH TIME ZONE带时区的时间戳跨时区业务
CLOB字符大对象(最大 4G-1),存长文本文章正文、日志
BLOB二进制大对象图片、文件
RAW(n)原始二进制,不做字符集转换UUID、哈希值

Oracle 小贴士:VARCHAR 是预留类型,实际统一用 VARCHAR2NUMBER 是「一个类型包打天下」,不像 MySQL 有 INT / BIGINT / DECIMAL 之分。

1.2 建表:主键 + 约束 + 默认值 + 注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE TABLE departments (
dept_id NUMBER(4)
GENERATED ALWAYS AS IDENTITY -- 12c+ 自增主键(推荐)
CONSTRAINT pk_departments PRIMARY KEY,
dept_name VARCHAR2(50 CHAR)
CONSTRAINT nn_dept_name NOT NULL
CONSTRAINT uq_dept_name UNIQUE,
location VARCHAR2(100 CHAR),
created_at TIMESTAMP(3) DEFAULT SYSTIMESTAMP,
updated_at TIMESTAMP(3)
);

COMMENT ON TABLE departments IS '部门表';
COMMENT ON COLUMN departments.dept_id IS '部门ID,自增主键';
COMMENT ON COLUMN departments.dept_name IS '部门名称,唯一';
COMMENT ON COLUMN departments.location IS '办公地点';

再建一张员工表(含外键 + 检查约束):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CREATE TABLE employees (
emp_id NUMBER(6) GENERATED ALWAYS AS IDENTITY CONSTRAINT pk_employees PRIMARY KEY,
first_name VARCHAR2(30 CHAR),
last_name VARCHAR2(30 CHAR) CONSTRAINT nn_emp_last NOT NULL,
email VARCHAR2(50 CHAR) CONSTRAINT uq_emp_email UNIQUE,
phone VARCHAR2(20 CHAR),
hire_date DATE DEFAULT TRUNC(SYSDATE) CONSTRAINT nn_emp_hire NOT NULL,
job_id VARCHAR2(20 CHAR) CONSTRAINT nn_emp_job NOT NULL,
salary NUMBER(8,2) CONSTRAINT ck_emp_sal CHECK (salary > 0),
dept_id NUMBER(4),
created_at TIMESTAMP(3) DEFAULT SYSTIMESTAMP,
updated_at TIMESTAMP(3),
-- 外键
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES departments(dept_id) ON DELETE SET NULL
);

COMMENT ON TABLE employees IS '员工表';
COMMENT ON COLUMN employees.salary IS '月薪,必须 > 0';
COMMENT ON COLUMN employees.dept_id IS '所属部门ID,删除部门时置空';

常用约束速记:

  • PRIMARY KEY = 非空 + 唯一
  • UNIQUE:唯一(允许一个 NULL)
  • NOT NULL:非空
  • CHECK:自定义表达式校验(salary > 0status IN ('A','B') 等)
  • FOREIGN KEY:外键;ON DELETE CASCADE 级联删除、ON DELETE SET NULL 置空、缺省「禁止删有子行的父行」

1.3 老版本自增:SEQUENCE + TRIGGER(11g 及之前)

如果你的环境还没到 12c,用序列 + 行级触发器模拟自增:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE SEQUENCE seq_emp_id
START WITH 1
INCREMENT BY 1
NOMAXVALUE
NOCACHE; -- 生产建议 CACHE 20 / 100 提升性能

CREATE OR REPLACE TRIGGER trg_emp_bi
BEFORE INSERT ON employees
FOR EACH ROW
WHEN (NEW.emp_id IS NULL)
BEGIN
:NEW.emp_id := seq_emp_id.NEXTVAL;
END;
/

1.4 修改表结构:ALTER TABLE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 加列
ALTER TABLE employees ADD address VARCHAR2(200 CHAR);

-- 改列类型/长度
ALTER TABLE employees MODIFY first_name VARCHAR2(50 CHAR);

-- 改列名
ALTER TABLE employees RENAME COLUMN address TO home_address;

-- 删列
ALTER TABLE employees DROP COLUMN home_address;

-- 加/删约束
ALTER TABLE employees ADD CONSTRAINT ck_emp_email CHECK (email LIKE '%@%');
ALTER TABLE employees DROP CONSTRAINT ck_emp_email;

-- 表重命名
ALTER TABLE departments RENAME TO depts;
ALTER TABLE depts RENAME TO departments;

2) 数据的增删改查(DML)

2.1 插入(INSERT)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 单行插入(列出列名是好习惯,表改字段也不怕错位)
INSERT INTO departments(dept_name, location)
VALUES ('Engineering', 'Shanghai - Lujiazui');

-- 一次插多行(Oracle 12c+ 也支持多行 VALUES,更通用写法是 INSERT ALL 或 UNION ALL)
INSERT ALL
INTO departments(dept_name, location) VALUES ('Sales', 'Beijing - CBD')
INTO departments(dept_name, location) VALUES ('Marketing', 'Shenzhen - Nanshan')
INTO departments(dept_name, location) VALUES ('HR', 'Shanghai - Pudong')
SELECT * FROM dual;

-- 从一张表复制到另一张表(CREATE TABLE AS 也能建表+导入)
INSERT INTO employees(first_name, last_name, email, job_id, salary, dept_id)
SELECT first_name, last_name, LOWER(first_name||'.'||last_name||'@corp.com'), job_id, salary, department_id
FROM hr.employees
WHERE department_id IN (60, 80);

批量大数据直插:INSERT /*+ APPEND */ INTO ... SELECT ...,走直接路径加载,不走缓冲池,配合 NOLOGGING 更快;适用 ETL/历史归档等场景。

2.2 查询(SELECT):基础语法 + 常见写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 投影 + 过滤 + 排序
SELECT emp_id, first_name || ' ' || last_name AS full_name, salary, hire_date
FROM employees
WHERE dept_id = 1
AND salary > 5000
ORDER BY salary DESC, hire_date ASC;

-- 去重
SELECT DISTINCT job_id FROM employees;

-- 模糊匹配(% 任意多字符,_ 单个字符)
SELECT * FROM employees WHERE last_name LIKE 'S%';
SELECT * FROM employees WHERE last_name LIKE '_A%';

-- 范围查询
SELECT * FROM employees WHERE salary BETWEEN 5000 AND 15000;
SELECT * FROM employees WHERE dept_id IN (1, 2, 3);

-- 判空(注意:NULL 不能用 = / <>,必须 IS NULL / IS NOT NULL)
SELECT * FROM employees WHERE dept_id IS NULL;

2.3 多表连接(JOIN)

Oracle 支持标准 SQL 的 JOIN ... ON(推荐),也支持老旧的逗号连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 内连接:只保留两边都匹配的行
SELECT e.emp_id, e.last_name, e.salary, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;

-- 左外连接:左表全留,右表没匹配补 NULL
SELECT e.emp_id, e.last_name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id;

-- 右外 / 全外(RIGHT / FULL OUTER JOIN)同理

-- 自连接:员工 + 上级
SELECT e.last_name AS emp, m.last_name AS mgr
FROM employees e
LEFT JOIN employees m ON e.emp_id = m.emp_id; -- 实际按你的 manager_id 列关联

2.4 分组与聚合(GROUP BY)

1
2
3
4
5
6
7
8
9
10
11
-- 每个部门的人数、平均薪、最高/最低薪
SELECT dept_id,
COUNT(*) AS headcount,
ROUND(AVG(salary), 2) AS avg_sal,
MAX(salary) AS max_sal,
MIN(salary) AS min_sal,
SUM(salary) AS total_sal
FROM employees
GROUP BY dept_id
HAVING COUNT(*) >= 5 -- HAVING 过滤聚合结果(WHERE 过滤行)
ORDER BY headcount DESC;

黄金法则:SELECT 里除聚合函数外的列,必须全部出现在 GROUP BY 中。

2.5 分页(12c+ 标准写法)

1
2
3
4
SELECT emp_id, last_name, salary, hire_date
FROM employees
ORDER BY salary DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY; -- 第 2 页,每页 10 条

兼容 11g 及更老版本的 ROWNUM 写法:

1
2
3
4
5
SELECT *
FROM (SELECT t.*, ROWNUM rn
FROM (SELECT emp_id, last_name, salary FROM employees ORDER BY salary DESC) t
WHERE ROWNUM <= 20)
WHERE rn > 10;

2.6 更新(UPDATE)

1
2
3
4
5
6
7
8
9
10
11
12
-- 单列更新
UPDATE employees
SET salary = salary * 1.10,
updated_at = SYSTIMESTAMP
WHERE dept_id = 1;

-- 基于子查询 / 其它表更新(用 MERGE 更直观,见下节)
UPDATE employees e
SET (salary, updated_at) = (
SELECT e.salary * 1.05, SYSTIMESTAMP FROM dual
)
WHERE e.dept_id IN (SELECT dept_id FROM departments WHERE location LIKE 'Shanghai%');

2.7 删除(DELETE + TRUNCATE)

1
2
3
4
5
6
-- 删除满足条件的行(可回滚)
DELETE FROM employees WHERE dept_id IS NULL;
COMMIT; -- 或 ROLLBACK

-- 清空整张表(DDL,不可回滚,更快!)
-- TRUNCATE TABLE employees;

DELETE vs TRUNCATE:前者是 DML,一行行删,可回滚,会触发行级触发器;后者是 DDL,直接释放段,不可回滚,不触发器。生产删全表 优先 TRUNCATE(但要确认备份)。

2.8 MERGE:一把梭做 UPSERT(存在就更新,不存在就插入)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MERGE INTO employees t
USING (
SELECT 100 AS emp_id, 'New' AS first_name, 'Name' AS last_name,
'new.name@corp.com' AS email, 'ENGINEER' AS job_id,
12000 AS salary, 1 AS dept_id FROM dual
) s
ON (t.emp_id = s.emp_id)
WHEN MATCHED THEN
UPDATE SET t.first_name = s.first_name,
t.last_name = s.last_name,
t.email = s.email,
t.salary = s.salary,
t.dept_id = s.dept_id,
t.updated_at = SYSTIMESTAMP
WHEN NOT MATCHED THEN
INSERT (first_name, last_name, email, job_id, salary, dept_id)
VALUES (s.first_name, s.last_name, s.email, s.job_id, s.salary, s.dept_id);

3) 视图(View):把复杂查询「虚表化」

3.1 为什么用视图

  • 简化调用:把 JOIN / 聚合 / CASE 复杂 SQL 封成视图,业务代码只 SELECT * FROM v_xxx
  • 安全:屏蔽敏感列(如 salary 只给经理看);配合权限,做到「列级」授权。
  • 逻辑独立性:基础表重构时,视图对外接口不变,应用层不用改。

3.2 创建视图

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE OR REPLACE VIEW v_emp_dept AS
SELECT e.emp_id,
e.first_name || ' ' || e.last_name AS full_name,
e.email,
e.hire_date,
e.salary,
d.dept_id,
d.dept_name,
d.location
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id;

COMMENT ON TABLE v_emp_dept IS '员工-部门明细视图';

3.3 只读视图(防误改)

1
2
3
4
5
6
CREATE OR REPLACE VIEW v_emp_public (emp_id, full_name, dept_name, location)
AS
SELECT e.emp_id, e.first_name||' '||e.last_name, d.dept_name, d.location
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id
WITH READ ONLY;

加了 WITH READ ONLY,任何 INSERT/UPDATE/DELETE 都会被 Oracle 拒绝。对给报表 / 只读用户用的视图强烈推荐。

3.4 物化视图(Materialized View):真·缓存结果

普通视图是「虚表」,每次查询都要重跑定义 SQL。物化视图则把结果 实体化存储,定期刷新,适合报表 / 数仓聚合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE MATERIALIZED VIEW mv_dept_sal_summary
BUILD IMMEDIATE
REFRESH COMPLETE
START WITH SYSDATE NEXT SYSDATE + 1/24 -- 每小时刷新一次
AS
SELECT dept_id,
COUNT(*) AS headcount,
SUM(salary) AS total_sal,
AVG(salary) AS avg_sal
FROM employees
GROUP BY dept_id;

-- 手动刷新
BEGIN
DBMS_MVIEW.REFRESH('mv_dept_sal_summary', 'C'); -- C=Complete 全量、F=Fast 增量
END;
/

4) 同义词(Synonym):给对象起个「好名字」

4.1 解决什么问题

  • 跨 schema 访问不用写前缀:原来 hr.employees,建了同义词直接用 employees
  • 屏蔽底层对象变更:后台从 employees_old 迁到 employees_new,同义词改一下指向即可,应用 SQL 不动。
  • 配合 DB Link:同义词 = schema.object@dblink 的「隐藏别名」。

4.2 私有同义词(当前用户可见)

1
2
3
4
5
6
7
-- 先假设我们给只读用户建同义词(演示)
-- 登录 app_read 用户:
CREATE SYNONYM departments FOR hr.departments;
CREATE SYNONYM employees FOR hr.employees;

-- 之后直接:
SELECT COUNT(*) FROM employees; -- 等价于 SELECT COUNT(*) FROM hr.employees;

4.3 公共同义词(所有用户可见)

需要 CREATE PUBLIC SYNONYM 系统权限:

1
2
3
-- 用 DBA / 有授权账号执行
CREATE PUBLIC SYNONYM dept FOR hr.departments;
CREATE PUBLIC SYNONYM emp FOR hr.employees;

权限注意:同义词本身 ≠ 权限!建了同义词后,你仍需要对底层对象有 SELECT / INSERT ... 权限才能真正访问。

4.4 增删查同义词

1
2
3
4
5
6
7
8
9
-- 查看自己的同义词
SELECT synonym_name, table_owner, table_name FROM user_synonyms;

-- 改同义词 = 重建(OR REPLACE)
CREATE OR REPLACE SYNONYM emp FOR hr.employees;

-- 删同义词
DROP SYNONYM emp;
DROP PUBLIC SYNONYM emp; -- 公共的要加 PUBLIC

5) 用户、角色与权限授权

Oracle 权限体系分三层:用户(schema 载体)→ 角色(权限包)→ 系统/对象权限。先分清楚两类权限:

  • 系统权限(System Privilege):能做什么事。如 CREATE TABLE / CREATE SESSION / CREATE VIEW / CREATE ANY TABLE
  • 对象权限(Object Privilege):能对某对象做什么。如 SELECT ON hr.employees / UPDATE(salary) ON hr.employees

5.1 创建用户 + 表空间(简版)

1
2
3
4
5
6
-- DBA 执行
CREATE USER app_user
IDENTIFIED BY "StrongPassw0rd!"
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
QUOTA UNLIMITED ON users; -- 在 users 表空间可无限使用空间

Oracle 常识:用户(User)与 Schema 几乎是一回事——建了用户就等于建了同名 schema,该用户建的表都在自己 schema 下。

5.2 系统权限:先让用户「能登录、能建东西」

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 最小化:允许登录
GRANT CREATE SESSION TO app_user;

-- 开发账号常用(谨慎!ANY 权限范围很大,生产不要随便给)
GRANT
CREATE TABLE,
CREATE VIEW,
CREATE SEQUENCE,
CREATE PROCEDURE,
CREATE TRIGGER,
CREATE SYNONYM,
CREATE MATERIALIZED VIEW
TO app_user;

查看自己的系统权限:

1
SELECT * FROM user_sys_privs;

5.3 对象权限:精确到「谁对什么能干什么」

最常见的是把 HR schema 下的表给业务账号开权限:

1
2
3
4
5
6
7
8
9
-- 以 HR / 表拥有者身份执行(或有 GRANT ANY OBJECT PRIVILEGE)
GRANT SELECT, INSERT, UPDATE, DELETE ON hr.departments TO app_user;
GRANT SELECT ON hr.employees TO app_user;

-- 列级授权:只允许改某个字段(UPDATE / INSERT / REFERENCES 支持)
GRANT UPDATE(salary, dept_id) ON hr.employees TO app_mgr;

-- 允许被授权者再转授他人(慎用):WITH GRANT OPTION
GRANT SELECT ON hr.employees TO app_user WITH GRANT OPTION;

查看对象权限:

1
2
3
4
5
6
7
8
-- 我授予别人的
SELECT table_name, privilege, grantee FROM user_tab_privs_made;

-- 别人授予我的
SELECT table_name, owner, privilege FROM user_tab_privs_recd;

-- 当前用户对某列的列级权限
SELECT table_name, column_name, privilege FROM user_col_privs_recd;

回收权限:

1
2
REVOKE DELETE ON hr.departments FROM app_user;
REVOKE ALL ON hr.employees FROM app_user;

5.4 角色(Role):把权限打包、治理更干净

当权限一多,「给每个用户重复 GRANT 一遍」是噩梦。角色登场:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- 1) 建角色
CREATE ROLE r_app_read;
CREATE ROLE r_app_write;

-- 2) 把权限塞给角色
GRANT CREATE SESSION TO r_app_read;
GRANT SELECT ON hr.departments TO r_app_read;
GRANT SELECT ON hr.employees TO r_app_read;
GRANT SELECT ON hr.v_emp_public TO r_app_read;

GRANT SELECT, INSERT, UPDATE, DELETE ON hr.departments TO r_app_write;

-- 3) 把角色赋给用户(一条搞定,不用写一堆)
GRANT r_app_read TO app_user;
GRANT r_app_write TO app_mgr;

-- 4) 设为用户默认登录就启用的角色(可选)
ALTER USER app_user DEFAULT ROLE r_app_read;

查看自己拥有的角色:

1
SELECT * FROM user_role_privs;

6) 常用元数据查询(查自己有啥)

写 SQL 时经常要查「这张表有哪些列 / 有啥约束 / 建表语句长啥样」:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
-- 我有哪些表
SELECT table_name FROM user_tables ORDER BY table_name;

-- 某表的列(字段名/类型/长度/是否可空)
SELECT column_id, column_name, data_type,
CASE WHEN data_type IN ('VARCHAR2','CHAR') THEN data_length
WHEN data_type = 'NUMBER' THEN data_precision||','||data_scale
ELSE NULL END AS type_detail,
nullable
FROM user_tab_columns
WHERE table_name = 'EMPLOYEES'
ORDER BY column_id;

-- 某表的约束
SELECT constraint_name, constraint_type, status, search_condition
FROM user_constraints
WHERE table_name = 'EMPLOYEES';
-- c 类型:P=主键 U=唯一 C=检查/非空 R=外键

-- 外键列对应关系
SELECT c.constraint_name, col.column_name,
r_constraint_name AS fk_to, r_col.owner||'.'||r_col.table_name||'('||r_col.column_name||')' AS ref_col
FROM user_cons_columns col
JOIN user_constraints c ON c.constraint_name = col.constraint_name
LEFT JOIN user_cons_columns r_col ON r_col.constraint_name = c.r_constraint_name AND r_col.position = col.position
WHERE c.table_name = 'EMPLOYEES'
AND c.constraint_type = 'R';

-- 某对象的定义(视图/过程/函数/触发器源码)
SELECT text FROM user_source WHERE name = 'V_EMP_DEPT' ORDER BY line;

7) 基础实践最佳实践(踩过坑的总结)

  • 字段命名:全大写 + 下划线(Oracle 默认把未加双引号的标识符全部转大写,别给自己找事加双引号)。
  • 主键:业务表尽量用「无意义代理键」(自增 ID / 序列),不要把身份证号、手机号这种业务字段当主键。
  • 事务:DML 必须显式 COMMIT / ROLLBACK;养成「小事务 + 及时提交」的习惯,别让锁长持。
  • 删改前先 SELECTUPDATE / DELETE 前先用同样的 WHERE 跑一遍 SELECT 确认行数。
  • 权限最小化:不给 ANY、不给 DBA、把权限塞到 ROLE 里再分发;生产只读账号只给 SELECT
  • 字符集:新库统一选 AL32UTF8(Oracle 的 UTF-8)。
  • 分区 / 归档:超 1000 万行的大表规划分区(按时间最常见);冷数据用分区交换 + 表空间迁移下线。

参考与延伸阅读

  • Oracle SQL Language Reference(官方文档)
  • Oracle Database Security Guide(权限 / 角色 / 审计)
  • Oracle Database Administrator’s Guide(表空间、用户、物化视图)
  • Oracle Database Utilities(数据泵 expdp/impdp、SQL*Loader 等数据迁移工具)

Oracle SQL 基础入门教程:表的增删改查、视图、同义词与权限授权
https://www.pcboy.com.cn/2026/07/28/Oracle-SQL-基础入门教程:表增删改查视图同义词与权限授权/
作者
chituer
发布于
2026年7月28日
许可协议