数据库学习记录
📦 第03章 基本的SELECT语句:数据世界的「望远镜」
🎯 SQL家族三大门派
/*
🔧 DDL(数据建筑师):
CREATE 建房子 | ALTER 装修 | DROP 拆房子
RENAME 改门牌号 | TRUNCATE 清空仓库
✏️ DML(数据搬运工):
INSERT 进货 | DELETE 扔垃圾 | UPDATE 调库存
SELECT 查账本(本章主角!)
🔐 DCL(数据保安):
COMMIT 锁保险柜 | ROLLBACK 时光倒流
SAVEPOINT 存档点 | GRANT 发门禁卡 | REVOKE 收卡
*/
🔭 基础SELECT三连击
1. 数据望远镜初体验
-- 看伪表DUAL的虚拟舞台表演
SELECT 1+1, 3*2 FROM DUAL; -- 输出:2 | 6
-- 掏出员工表全家福
SELECT * FROM employees;
-- 精准观察三列数据
SELECT employee_id, last_name, salary FROM employees;
2. 列の「艺名」设置指南
-- 给列起外号的三种姿势
SELECT
employee_id AS 工号, -- 标准艺名
last_name "神秘代号", -- 带空格的艺名要加引号
department_id 部门ID -- 最简写法(小心关键字!)
FROM employees;
3. 数据去重神器
-- 普通青年查部门:107条重复数据
SELECT department_id FROM employees;
-- 极客青年查部门:12个不重复ID
SELECT DISTINCT department_id FROM employees; -- DISTINCT去重buff
⚠️ 空值(Null)生存指南
空值运算的坑
-- 错误示范:年终奖可能为null的员工
SELECT
employee_id,
salary * (1 + commission_pct) * 12 "年工资" -- null会导致结果变null!
FROM employees;
-- 正确姿势:给null上保险
SELECT
employee_id,
salary * (1 + IFNULL(commission_pct, 0)) * 12 "年工资" -- 用IFNULL兜底
FROM employees;
🛡️ 特殊符号生存技巧
防冲突护甲(着重号)
-- 当表名撞名SQL关键字时
SELECT * FROM `order`; -- 用反引号包裹危险表名
数据表「体检报告」
-- 查看employees表的结构说明书
DESCRIBE employees; -- 全写版
DESC employees; -- 极简版(同样有效!)
🔍 数据过滤器WHERE
精准定位90号部门
-- 查找部门90的同事
SELECT *
FROM employees
WHERE department_id = 90; -- 等号过滤
-- 寻找叫King的大佬(MySQL不区分大小写!)
SELECT *
FROM employees
WHERE last_name = 'King'; -- Oracle严格区分,MySQL很随意
💡 冷知识加油站
-
伪表DUAL:
虚拟舞台,专门用于不需要真实表的计算表演 -
常数查询:
SELECT '我是常数', employee_id FROM employees; -- 每行都带这个常数
-
MySQL大小写哲学:
- 表名/列名 区分大小写(Linux系统)
- 字符串内容 不区分大小写('King’和’king’等价)
-
DESC双面人生:
既是「降序排序」的关键词,也是「查看表结构」的命令!
总结:SELECT就像数据世界的望远镜,掌握这些技巧让你想看哪里点哪里!🎯
📊 第04章 运算符:数据世界的「魔法公式」
🧙♂️ 算术符的奇妙世界:+ - * / DIV % MOD
基础算术表演
-- 数字与字符串的暧昧(隐式转换)
SELECT 100 + '1' AS 暧昧相加, -- 101('1'被温柔地转为数字)
100 + 'a' AS 乱码相加 -- 100('a'被当作舔狗0处理)
FROM DUAL;
-- NULL的隐身术:任何运算遇到NULL都会消失!
SELECT 100 + NULL AS 消失的结果 -- NULL(像被施了隐身咒)
FROM DUAL;
实战:寻找双号员工
SELECT employee_id, last_name, salary
FROM employees
WHERE employee_id % 2 = 0; -- % 是取余神器
🔍 比较符侦探社:= <=> != < > …
字符串破案指南
-- 当数字遇见字符串(大型隐式转换现场)
SELECT 1 = '1' AS 穿越相爱, -- 1(true)
1 = 'a' AS 单相思, -- 0(false)
0 = 'a' AS 舔狗の爱 -- 1(true)
FROM DUAL;
NULL侦探事务所
-- 普通等于遇到NULL就懵逼
SELECT 1 = NULL AS 迷茫结果, -- NULL(未知)
NULL = NULL AS 量子纠缠 -- NULL(仍是未知)
FROM DUAL;
-- 安全等于<=>:唯一能看透NULL的X光眼
SELECT last_name, salary
FROM employees
WHERE commission_pct <=> NULL; -- 精准捕捉NULL值
🕵️♂️ 特殊搜查技巧包
IS NULL侦探
-- 查找没有提成的打工人
SELECT last_name, salary
FROM employees
WHERE commission_pct IS NULL; -- ISNULL(commission_pct) 也可
IN/ NOT IN 多选神器
-- 部门10/20/30的同事集合!
SELECT last_name, department_id
FROM employees
WHERE department_id IN (10, 20, 30);
-- 拒绝6K/7K/8K工资的叛逆选择
SELECT last_name, salary
FROM employees
WHERE salary NOT IN (6000, 7000, 8000);
🔮 通配符魔法:LIKE的咒语书
% 魔法:任意长度替身
-- 名字里藏着小a的巫师
SELECT last_name
FROM employees
WHERE last_name LIKE '%a%'; -- 像*通配符一样强大
-- a开头名字的预言(魔杖一挥)
SELECT last_name
FROM employees
WHERE last_name LIKE 'a%'; -- 首字母检测术
_ 魔法:精准定位符
-- 第二个字符是a的密语
SELECT last_name
FROM employees
WHERE last_name LIKE '_a%'; -- _代表一个任意字符
-- 查找_和a的羁绊(需要转义符\)
SELECT last_name
FROM employees
WHERE last_name LIKE '_\_a%'; -- 用\解除_的魔法效果
❄️ 冷知识暴风雪
-
DIV 与 /
DIV
是整数除法(5 DIV 2 = 2)/
是精确除法(5/2 = 2.5)
-
MOD 的替身
%
和MOD
是双胞胎,8 MOD 3 = 2,8 % 3 = 2 -
LIKE 的隐藏Boss
-- 同时包含a和e的名字(两种写法) WHERE last_name LIKE '%a%' AND last_name LIKE '%e%'; WHERE last_name LIKE '%a%e%' OR last_name LIKE '%e%a%';
-
安全等于的真相
<=>
可以处理NULL比较,但会降低SQL性能,非必要不滥用
口诀记忆:
“算术比较加LIKE,NULL处理要勤快;
IN把多个条件带,通配符里藏精彩!” 🪄
📚 第05章 排序与分页:数据の排队术与翻页魔法
🎩 排序:给数据队伍喊口令!
1. 基础排队法(ORDER BY)
-- 默认按工资从低到高排队(偷偷用了ASC)
SELECT employee_id, last_name, salary
FROM employees
ORDER BY salary; -- 像军训教官的默认口令:"向前看齐!"
-- 明确喊出口令:工资从高到低排队!
SELECT employee_id, last_name, salary
FROM employees
ORDER BY salary DESC; -- DESC是"向后转!"
2. 别名の妙用(WHERE看了会流泪)
-- 用annual_sal别名排序(WHERE中不能用别名!)
SELECT employee_id, salary, salary*12 annual_sal
FROM employees
ORDER BY annual_sal; -- 给年薪起外号后排序
3. 排序优先级法则
-- 先按部门倒序排,同部门按工资升序排
SELECT employee_id, department_id, salary
FROM employees
ORDER BY department_id DESC, salary ASC; -- 像先按班级排,再按身高排
⚠️ 重要顺序口诀:
FROM → WHERE → ORDER BY → LIMIT
(查表→过滤→排队→截取)
📖 分页:数据界的「自动翻书术」
1. LIMIT基础公式
-- 第1页:前20条(从0号位开始)
SELECT employee_id, last_name
FROM employees
LIMIT 0, 20; -- 悄悄数数:0,1,2...19
-- 第2页:接着20条(数学课代表上线)
SELECT employee_id, last_name
FROM employees
LIMIT 20, 20; -- (2-1)*20=20
万能分页公式:
LIMIT (页码-1)*每页条数, 每页条数
2. 花式分页技巧
-- 高薪Top10榜单
SELECT employee_id, last_name, salary
FROM employees
WHERE salary > 6000
ORDER BY salary DESC
LIMIT 10; -- LIMIT 0,10 的简写
-- 精准翻到第32条(想直接看某条数据?)
SELECT employee_id, last_name
FROM employees
LIMIT 31, 1; -- 位置偏移从0开始计数!
3. MySQL8.0 新姿势:OFFSET
-- 新语法更符合人类直觉
SELECT employee_id, last_name
FROM employees
LIMIT 2 OFFSET 31; -- 先跳过31条,再取2条
❄️ 冷知识暴风雪
-
隐式排序陷阱
不加ORDER BY时"按添加顺序显示"只是假象!数据物理存储顺序可能变化,必须显式排序才能保证顺序稳定 -
别名禁用区
WHERE中不能使用SELECT列的别名(因为执行顺序WHERE在SELECT之前!) -
LIMIT性能玄学
深分页(如LIMIT 100000,20)会变慢,推荐使用WHERE id > 上页最大ID 优化 -
OFFSET的逆袭
MySQL8.0的LIMIT ... OFFSET
更易读,但和老语法LIMIT 偏移量,数量
完全等价
口诀记忆:
“排序口令ORDER BY,DESC倒序ASC上;
LIMIT分页数学强,新语法OFFSET更香!” 🪄
📊 第06章 多表查询:数据表的「联谊派对」
🚨 笛卡尔积:大型数据相亲翻车现场
错误示范:全员乱组CP
-- 107个员工 × 27个部门 = 2889对无效CP!
SELECT employee_id, department_name
FROM employees, departments; -- 忘记牵红线(连接条件)
症状:每个员工和每个部门强行配对,数据爆炸!
治疗药方:添加WHERE
连接条件,给数据牵红线❤️
💡 正确联谊姿势:数据CP配对指南
等值连接:门当户对的爱情
-- 用部门ID牵线成功
SELECT emp.employee_id, dept.department_name
FROM employees emp, departments dept
WHERE emp.department_id = dept.department_id;
联谊小贴士:
- 多表同名字段必须带前缀(如
emp.department_id
) - 表别名是派对的VIP铭牌(
employees emp
) - N个表联谊至少需要N-1个连接条件!
🌈 多表CPの花式配对法
1. 自连接:我领导我自己
-- 查员工和他们的上司(都是同一张表!)
SELECT 打工人.employee_id AS "小弟ID",
上司.last_name AS "老大名字"
FROM employees 打工人, employees 上司
WHERE 打工人.manager_id = 上司.employee_id;
2. 非等值连接:工资段位匹配
-- 看看工资在哪个等级区间
SELECT e.last_name, e.salary,
j.grade_level AS "财富段位"
FROM employees e, job_grades j
WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal;
3. 三表联查:部门地址追踪
-- 员工+部门+地址三表联动
SELECT e.employee_id, d.department_name, l.city
FROM employees e, departments d, locations l
WHERE e.department_id = d.department_id
AND d.location_id = l.location_id;
🔄 内外连接:包容性大不同
内连接:只展示成功CP
-- 只显示有部门的员工(SQL92写法)
SELECT e.last_name, d.department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id;
-- SQL99更优雅的写法
SELECT e.last_name, d.department_name
FROM employees e
INNER JOIN departments d -- INNER可省略
ON e.department_id = d.department_id;
外连接:给单身狗留位置
-- 左外连接:所有员工都要显示(即使没部门)
SELECT e.last_name, d.department_name
FROM employees e
LEFT JOIN departments d -- 左表是宇宙中心
ON e.department_id = d.department_id;
-- 右外连接:所有部门都要显示(即使没员工)
SELECT e.last_name, d.department_name
FROM employees e
RIGHT JOIN departments d -- 右表是主角
ON e.department_id = d.department_id;
⚡ SQL92 vs SQL99 语法Battle
SQL92的局限
-- MySQL不支持这种外连接写法(Oracle可以)
SELECT e.last_name, d.department_name
FROM employees e, departments d
WHERE e.department_id(+) = d.department_id; -- ❌
SQL99的优势
-- 清晰的JOIN...ON结构(MySQL推荐)
SELECT e.last_name, d.department_name
FROM employees e
LEFT OUTER JOIN departments d -- OUTER可省略
ON e.department_id = d.department_id
JOIN locations l -- 轻松连接第三张表
ON d.location_id = l.location_id;
🧩 满外连接:UNION来救场
UNION vs UNION ALL
-- 左外 + 右外 - 去重 = 满外(MySQL没有FULL JOIN)
(SELECT e.last_name FROM employees e LEFT JOIN departments d ...)
UNION
(SELECT e.last_name FROM employees e RIGHT JOIN departments d ...);
-- 高性能版(不去重)
(SELECT ...) UNION ALL (SELECT ...);
冷知识:
UNION
去重但效率低UNION ALL
保留所有结果,效率更高!
💣 避坑指南 & 冷知识
-
多表查询顺序
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
-
别名禁区
表别名一旦声明,SELECT和WHERE中必须使用别名! -
NULL的暧昧
多表连接时,NULL值不会自动匹配,需用外连接显示 -
满外连接替代方案
通过LEFT JOIN + RIGHT JOIN + UNION
组合实现
口诀记忆:
“多表连接要红线,别名前缀防混乱;
内连只要成功配,外连照顾单身汉!” 🎯
知识地图
🌟 SQL99标准连接详解:用JOIN…ON玩转多表关系
📌 内连接(INNER JOIN) —— 只匹配成功的CP
核心特点
- 精准匹配:仅返回两个表中完全匹配的行
- 隐式过滤:自动过滤掉无匹配项的数据
- 性能优势:通常执行效率较高
语法示例
-- 查询有部门的员工信息(显式写法)
SELECT e.last_name, d.department_name
FROM employees e
INNER JOIN departments d -- INNER可省略
ON e.department_id = d.department_id;
-- 多表内连接(部门+地址)
SELECT e.employee_id, l.city
FROM employees e
JOIN departments d ON e.department_id = d.department_id
JOIN locations l ON d.location_id = l.location_id;
适用场景
- 需要精确匹配的关联数据查询
- 当确定关联字段不存在NULL值时
🚪 左外连接(LEFT OUTER JOIN) —— 左表是宇宙中心
核心特点
- 左表特权:保留左表所有行,右表无匹配则显示NULL
- 数据完整性:确保左表数据不丢失
- OUTER可省略:
LEFT JOIN
等价于LEFT OUTER JOIN
语法示例
-- 查询所有员工(包括无部门人员)
SELECT e.last_name, d.department_name
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id;
-- 左外连接三表(员工+部门+地址)
SELECT e.employee_id, l.city
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
LEFT JOIN locations l ON d.location_id = l.location_id;
适用场景
- 需要保留主表全部数据(如员工表为主)
- 分析存在空值的数据分布(如统计未分配部门员工)
🚪 右外连接(RIGHT OUTER JOIN) —— 右表是绝对主角
核心特点
- 右表特权:保留右表所有行,左表无匹配则显示NULL
- 数据透视:常用于维度表分析
- OUTER可省略:
RIGHT JOIN
等价于RIGHT OUTER JOIN
语法示例
-- 查询所有部门(包括无员工的部门)
SELECT d.department_name, e.last_name
FROM employees e
RIGHT JOIN departments d
ON e.department_id = d.department_id;
-- 右外连接三表(反向关联)
SELECT d.department_name, l.city
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id
RIGHT JOIN locations l ON d.location_id = l.location_id;
适用场景
- 需要保留维度表全部数据(如展示所有部门)
- 检查未被使用的数据(如查找从未被分配的部门)
🌌 满外连接(FULL OUTER JOIN) —— 数据全宇宙
核心特点
- 数据全保留:返回左右表所有行,无匹配则对方字段为NULL
- MySQL不支持:需通过
UNION
模拟实现 - 性能注意:大数据量时谨慎使用
语法实现
-- 模拟满外连接(员工+部门)
(SELECT e.last_name, d.department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id)
UNION -- 去重合并
(SELECT e.last_name, d.department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id);
-- 高性能版(不去重)
(SELECT ...) UNION ALL (SELECT ...) WHERE ...;
适用场景
- 需要完整数据全景分析
- 数据差异对比(如对比两个表差异记录)
🎯 对比总结表
连接类型 | 关键字 | 保留数据范围 | MySQL支持 | 典型场景 |
---|---|---|---|---|
内连接 | INNER JOIN 或 JOIN | 仅匹配成功的行 | ✅ | 精准关联查询 |
左外连接 | LEFT [OUTER] JOIN | 左表全部 + 右表匹配行 | ✅ | 主表数据保全 |
右外连接 | RIGHT [OUTER] JOIN | 右表全部 + 左表匹配行 | ✅ | 维度表分析 |
满外连接 | FULL OUTER JOIN | 左右表全部行 | ❌ | 数据全景展示(需UNION模拟) |
🔥 高阶技巧点拨
1. ON vs WHERE 的区别
-- ON 是连接条件(先过滤后连接)
SELECT e.last_name, d.department_name
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id
AND d.department_name = 'IT' -- 连接时过滤
-- WHERE 是最终过滤(先连接后过滤)
SELECT e.last_name, d.department_name
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_name = 'IT'; -- 连接后过滤
2. 自连接外连接
-- 查询员工及其领导(包括无领导的CEO)
SELECT 员工.last_name AS 员工,
领导.last_name AS 领导
FROM employees 员工
LEFT JOIN employees 领导
ON 员工.manager_id = 领导.employee_id;
3. 多表连接顺序优化
/* 推荐写法:主表在前,关联表按层级连接 */
SELECT e.last_name, l.city
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
LEFT JOIN locations l ON d.location_id = l.location_id;
黄金法则:
“内连精准匹配快,左外右外保一方,满外UNION来实现,ON在前来WHERE后!” 🚀