第三章:SQL 基础与数据类型
文档信息
- 适用版本:Oracle 12c / 18c / 19c / 21c
- 阅读时间:约 45 分钟
- 前置知识:第一章内容
3.1 SQL 概述
3.1.1 SQL 语句分类
| 分类 |
语句 |
说明 |
| DDL |
CREATE, ALTER, DROP, TRUNCATE |
数据定义语言 |
| DML |
SELECT, INSERT, UPDATE, DELETE |
数据操作语言 |
| DCL |
GRANT, REVOKE |
数据控制语言 |
| TCL |
COMMIT, ROLLBACK, SAVEPOINT |
事务控制语言 |
-- DDL 示例
CREATE TABLE employees (
emp_id NUMBER(10),
name VARCHAR2(100)
);
-- DML 示例
INSERT INTO employees VALUES (1, '张三');
COMMIT;
-- TCL 示例
SAVEPOINT sp1;
DELETE FROM employees WHERE emp_id = 1;
ROLLBACK TO sp1;
3.1.2 Oracle 与 MySQL 语法差异
| 功能 |
Oracle |
MySQL |
| 字符串引号 |
单引号 ' |
单引号 ' 或双引号 " |
| 字符串拼接 |
|| 或 CONCAT() |
CONCAT() 或 + |
| 别名引号 |
双引号 " |
反引号 ` 或双引号 |
| 分页查询 |
OFFSET ... ROWS FETCH NEXT |
LIMIT ... OFFSET |
| 空值处理 |
NVL(), COALESCE() |
IFNULL(), COALESCE() |
| 当前时间 |
SYSDATE, SYSTIMESTAMP |
NOW(), CURDATE() |
3.2 数据类型
3.2.1 数值类型
| 类型 |
范围 |
说明 |
| NUMBER(p,s) |
1~38 位精度 |
通用数值类型 |
| FLOAT |
变长 |
浮点数(Oracle 遗留) |
| BINARY_FLOAT |
32位 |
IEEE 754 单精度 |
| BINARY_DOUBLE |
64位 |
IEEE 754 双精度 |
-- 数值类型示例
CREATE TABLE numeric_demo (
int_col NUMBER(10), -- 整数,10 位
dec_col NUMBER(10,2), -- 小数,总 10 位,小数 2 位
float_col FLOAT(53), -- 浮点数
percent NUMBER(5,2) -- 百分比:99.99
);
INSERT INTO numeric_demo VALUES (1234567890, 12345.67, 3.14159, 99.99);
3.2.2 字符类型
| 类型 |
最大长度 |
说明 |
| CHAR(n) |
2000 字节 |
固定长度,不足补空格 |
| VARCHAR2(n) |
4000 字节 |
可变长度(推荐) |
| NCHAR(n) |
2000 字节 |
Unicode 固定长度 |
| NVARCHAR2(n) |
4000 字节 |
Unicode 可变长度 |
| CLOB |
128TB |
字符大对象 |
| LONG |
2GB |
旧版本兼容(不推荐) |
-- 字符类型示例
CREATE TABLE char_demo (
fixed_code CHAR(10), -- 固定 10 字符
name VARCHAR2(100), -- 可变长度姓名
bio CLOB -- 简历等长文本
);
-- 插入示例
INSERT INTO char_demo VALUES ('EMP', '张三', 'Oracle DBA,10年经验...');
3.2.3 日期时间类型
| 类型 |
范围 |
说明 |
| DATE |
公元前 4712 ~ 公元 9999 |
日期+时间(秒精度) |
| TIMESTAMP(p) |
- |
日期+时间(微秒精度) |
| TIMESTAMP WITH TIME ZONE |
- |
带时区 |
| TIMESTAMP WITH LOCAL TIME ZONE |
- |
本地时区 |
| INTERVAL YEAR TO MONTH |
- |
年月间隔 |
| INTERVAL DAY TO SECOND |
- |
天时分秒间隔 |
-- 日期时间示例
CREATE TABLE datetime_demo (
create_date DATE, -- DATE 类型
create_time TIMESTAMP(6), -- 毫秒精度
create_tz TIMESTAMP WITH TIME ZONE, -- 带时区
work_period INTERVAL YEAR(2) TO MONTH, -- 工作年限
break_time INTERVAL DAY(3) TO SECOND(2) -- 休息时长
);
-- 插入日期
INSERT INTO datetime_demo VALUES (
SYSDATE,
SYSTIMESTAMP,
CURRENT_TIMESTAMP,
INTERVAL '5-3' YEAR TO MONTH, -- 5年3个月
INTERVAL '3 02:30:15' DAY TO SECOND -- 3天2小时30分15秒
);
-- 日期运算
SELECT
SYSDATE + 7 AS next_week,
SYSDATE - 30 AS last_month,
SYSDATE + NUMTODSINTERVAL(2, 'HOUR') AS two_hours_later
FROM dual;
3.2.4 LOB 类型
| 类型 |
最大长度 |
说明 |
| BLOB |
128TB |
二进制大对象(图片、文件) |
| CLOB |
128TB |
字符大对象 |
| NCLOB |
128TB |
Unicode CLOB |
| BFILE |
外部文件 |
存储文件指针 |
-- LOB 类型示例
CREATE TABLE lob_demo (
id NUMBER PRIMARY KEY,
name VARCHAR2(100),
photo BLOB, -- 员工照片
resume CLOB, -- 简历
contract BFILE -- 合同 PDF
);
-- 使用绑定变量插入 LOB
DECLARE
v_blob BLOB;
v_clob CLOB;
BEGIN
INSERT INTO lob_demo (id, name, photo, resume)
VALUES (1, '张三', EMPTY_BLOB(), EMPTY_CLOB())
RETURNING photo, resume INTO v_blob, v_clob;
-- 写入 BLOB
DBMS_LOB.WRITE(v_blob, length('二进制数据'), 1, UTL_RAW.CAST_TO_RAW('二进制数据'));
END;
/
3.3 常用运算符
3.3.1 算术运算符
| 运算符 |
说明 |
示例 |
| + |
加法 |
10 + 5 = 15 |
| - |
减法 |
10 - 5 = 5 |
| * |
乘法 |
10 * 5 = 50 |
| / |
除法 |
10 / 5 = 2 |
| MOD(m,n) |
取模 |
MOD(10, 3) = 1 |
| POWER(m,n) |
幂运算 |
POWER(2, 3) = 8 |
3.3.2 比较运算符
| 运算符 |
说明 |
| = |
等于 |
| <>, != |
不等于 |
| > |
大于 |
| < |
小于 |
| >= |
大于等于 |
| <= |
小于等于 |
| BETWEEN…AND |
在范围内 |
| IN |
在列表中 |
| LIKE |
模式匹配 |
| IS NULL |
为空 |
3.3.3 逻辑运算符
-- AND: 所有条件都为 TRUE
SELECT * FROM employees WHERE salary > 5000 AND department = 'IT';
-- OR: 任一条件为 TRUE
SELECT * FROM employees WHERE department = 'IT' OR department = 'HR';
-- NOT: 条件取反
SELECT * FROM employees WHERE NOT (department = 'IT');
-- 组合使用
SELECT * FROM employees
WHERE (salary > 5000 OR commission > 1000)
AND hire_date > DATE '2020-01-01';
3.4 常用函数
3.4.1 字符函数
| 函数 |
说明 |
示例 |
| CONCAT(a, b) |
拼接字符串 |
CONCAT('Hello', 'World') |
| || |
字符串拼接 |
'Hello' || ' ' || 'World' |
| LENGTH() |
字符串长度 |
LENGTH('Oracle') |
| UPPER/LOWER/INITCAP |
大小写转换 |
UPPER('oracle') |
| TRIM/LTRIM/RTRIM |
去除空格 |
TRIM(' hello ') |
| SUBSTR() |
截取字符串 |
SUBSTR('Oracle', 2, 3) |
| INSTR() |
查找子串位置 |
INSTR('HelloWorld', 'World') |
| REPLACE() |
替换字符串 |
REPLACE('Hello', 'l', 'x') |
| LPAD/RPAD |
填充字符串 |
LPAD('5', 5, '0') |
| REGEXP_REPLACE() |
正则替换 |
正则表达式替换 |
-- 字符函数示例
SELECT
CONCAT('Mr.', name) AS full_name, -- 拼接
LENGTH(name) AS name_len, -- 长度
UPPER(name) AS name_upper, -- 大写
SUBSTR(name, 1, 1) || '***' AS masked -- 脱敏
FROM employees;
-- 字符串截取详解
SELECT
SUBSTR('Oracle Database', 1, 6) AS from_start, -- Oracle
SUBSTR('Oracle Database', -8) AS from_end, -- Database
SUBSTR('Oracle Database', 8, 4) AS middle, -- Data
INSTR('Oracle Database', 'a') AS first_pos, -- 4
INSTR('Oracle Database', 'a', 5) AS next_pos -- 7
FROM dual;
3.4.2 数值函数
| 函数 |
说明 |
示例 |
| ABS() |
绝对值 |
ABS(-10) |
| ROUND(n, d) |
四舍五入 |
ROUND(3.14159, 3) |
| TRUNC(n, d) |
截断 |
TRUNC(3.14159, 3) |
| CEIL/CEILING |
向上取整 |
CEIL(3.1) |
| FLOOR |
向下取整 |
FLOOR(3.9) |
| MOD() |
取模 |
MOD(10, 3) |
| SQRT() |
平方根 |
SQRT(16) |
| POWER() |
幂运算 |
POWER(2, 10) |
| LOG() |
对数 |
LOG(10, 100) |
| LN/EXP |
自然对数/指数 |
EXP(1) |
3.4.3 日期函数
| 函数 |
说明 |
示例 |
| SYSDATE |
当前日期时间 |
服务器当前时间 |
| CURRENT_DATE |
当前日期 |
会话时区日期 |
| ADD_MONTHS() |
日期加月 |
ADD_MONTHS(SYSDATE, 6) |
| MONTHS_BETWEEN() |
月份差 |
MONTHS_BETWEEN(d1, d2) |
| LAST_DAY() |
月末日期 |
LAST_DAY(SYSDATE) |
| NEXT_DAY() |
下个星期几 |
NEXT_DAY(SYSDATE, 'MONDAY') |
| EXTRACT() |
提取年/月/日 |
EXTRACT(YEAR FROM SYSDATE) |
| TRUNC() |
日期截断 |
TRUNC(SYSDATE, 'MM') |
| TO_CHAR() |
日期转字符串 |
TO_CHAR(SYSDATE, 'YYYY-MM-DD') |
| TO_DATE() |
字符串转日期 |
TO_DATE('2024-01-01', 'YYYY-MM-DD') |
-- 日期函数示例
SELECT
SYSDATE, -- 2024-01-15 10:30:00
ADD_MONTHS(SYSDATE, 3), -- 3 个月后
ADD_MONTHS(SYSDATE, -6), -- 6 个月前
MONTHS_BETWEEN('2024-12-31', '2024-01-01'), -- 月份差
LAST_DAY(SYSDATE), -- 月末
NEXT_DAY(SYSDATE, 'FRIDAY'), -- 下个周五
EXTRACT(YEAR FROM SYSDATE), -- 年份
TRUNC(SYSDATE, 'IW'), -- 本周一
TRUNC(SYSDATE, 'YYYY') -- 年初
FROM dual;
-- 日期格式化
SELECT
TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') AS datetime1,
TO_CHAR(SYSDATE, 'YYYY"年"MM"月"DD"日"') AS datetime2,
TO_CHAR(SYSDATE, 'DAY') AS weekday,
TO_CHAR(SYSDATE, 'Q') AS quarter
FROM dual;
3.4.4 转换函数
| 函数 |
说明 |
示例 |
| TO_CHAR() |
转为字符串 |
TO_CHAR(1234.56, '9999.99') |
| TO_NUMBER() |
转为数字 |
TO_NUMBER('1234') |
| TO_DATE() |
转为日期 |
TO_DATE('2024-01-01', 'YYYY-MM-DD') |
| TO_TIMESTAMP() |
转为时间戳 |
带时区的时间戳 |
| CAST() |
类型转换 |
CAST('123' AS NUMBER) |
| NVL(expr1, expr2) |
空值替换 |
NVL(null, 'N/A') |
| NVL2(expr, val1, val2) |
条件空值 |
非空返 val1,空返 val2 |
| COALESCE() |
返回首个非空 |
COALESCE(a, b, c, 'default') |
-- 转换函数示例
SELECT
-- 数字格式化
TO_CHAR(1234567.89, '999,999,999.99') AS formatted_num,
-- 数字转字符串
TO_CHAR(1234) || ' items',
-- 空值处理
NVL(commission, 0) AS commission,
NVL2(commission, '有提成', '无提成') AS commission_status,
COALESCE(phone, email, '无联系方式') AS contact
FROM employees;
-- 日期格式模型
| 格式 | 说明 | 示例 |
|------|------|------|
| YYYY | 4位年份 | 2024 |
| MM | 2位月份 | 01-12 |
| DD | 2位日期 | 01-31 |
| HH24 | 24小时制 | 00-23 |
| HH12 | 12小时制 | 01-12 |
| MI | 分钟 | 00-59 |
| SS | 秒 | 00-59 |
| DAY | 星期几名称 | MONDAY |
| MONTH | 月份名称 | JANUARY |
| Q | 季度 | 1-4 |
| WW | 年中第几周 | 1-53 |
3.4.5 聚合函数
| 函数 |
说明 |
忽略 NULL |
| COUNT() |
计数 |
COUNT(*) 包含 NULL |
| SUM() |
求和 |
是 |
| AVG() |
平均值 |
是 |
| MAX() |
最大值 |
否 |
| MIN() |
最小值 |
否 |
| LISTAGG() |
字符串聚合 |
可配置 |
-- 聚合函数示例
SELECT
COUNT(*) AS total_count, -- 总行数
COUNT(DISTINCT department) AS dept_cnt, -- 部门数
SUM(salary) AS total_salary, -- 工资总和
AVG(salary) AS avg_salary, -- 平均工资
MAX(salary) AS max_salary, -- 最高工资
MIN(salary) AS min_salary -- 最低工资
FROM employees;
-- LISTAGG 字符串聚合(Oracle 11g+)
SELECT
department,
LISTAGG(last_name, ',') WITHIN GROUP (ORDER BY hire_date) AS employees
FROM employees
GROUP BY department;
-- 结果示例:
-- DEPARTMENT | EMPLOYEES
-- -----------+--------------------
-- IT | Smith,Jones,Lee
-- HR | Brown,Wilson
3.4.6 分析函数(窗口函数)
-- 分析函数示例
SELECT
employee_id,
department_id,
salary,
-- 部门内排名
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank,
-- 部门内工资占比
ROUND(salary / SUM(salary) OVER (PARTITION BY department_id) * 100, 2) AS dept_pct,
-- 累计求和
SUM(salary) OVER (ORDER BY employee_id ROWS UNBOUNDED PRECEDING) AS cumulative_sum,
-- 移动平均(最近3个月)
AVG(salary) OVER (ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM employees
ORDER BY department_id, salary DESC;
3.5 分页查询
3.5.1 Oracle 12c 之前的分页
-- 分页查询(使用 ROWNUM)
SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC
) a WHERE ROWNUM <= 20
) WHERE rn > 10;
-- 解释:
-- 1. 内层:完整查询 + 排序
-- 2. 中层:ROWNUM 限制行数(必须在外层包装)
-- 3. 外层:过滤起始行
3.5.2 Oracle 12c+ 的分页
-- Oracle 12c+ 简化语法
SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
-- FETCH 子句选项
OFFSET n ROWS -- 跳过前 n 行
FETCH FIRST n ROWS ONLY -- 取前 n 行
FETCH FIRST n ROWS WITH TIES -- 包括并列的最后几行
FETCH FIRST n PERCENT ROWS ONLY -- 取前 n%
3.6 层级查询
3.6.1 CONNECT BY 递归查询
-- 组织架构层级查询
SELECT
LEVEL, -- 层级
employee_id,
first_name || ' ' || last_name AS name,
manager_id,
PRIIOR first_name || ' ' || PRIOR last_name AS manager_name
FROM employees
START WITH manager_id IS NULL -- 从 CEO 开始
CONNECT BY PRIOR employee_id = manager_id; -- 递归条件
-- 格式化显示层级
SELECT
LPAD(' ', 2 * (LEVEL - 1)) || first_name AS org_chart
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR employee_id = manager_id;
-- SYS_CONNECT_BY_PATH 路径
SELECT
SYS_CONNECT_BY_PATH(first_name, '/') AS path
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR employee_id = manager_id;
-- 查找叶子节点和根节点
SELECT *
FROM employees
WHERE LEVEL > 1
CONNECT BY PRIOR employee_id = manager_id
START WITH employee_id = 100
AND LEVEL <= 3;
3.7 本章小结
✅ SQL 分为 DDL、DML、DCL、TCL 四大类
✅ 掌握数值、字符、日期、Lob 等核心数据类型
✅ 熟练使用各种运算符和表达式
✅ 记住常用函数:字符串、数值、日期、转换函数
✅ 理解分析函数(窗口函数)的强大功能
✅ 掌握 Oracle 分页查询和层级查询
📖 下章预告:数据库与表操作
下一步:学习 第四章:数据库与表操作