仅以本文帮助大家掌握数据约束原理与复杂查询技巧
一、数据约束
约束类型全解析
约束类型 | 作用 | 示例 |
---|---|---|
NOT NULL | 禁止存储NULL值 | CREATE TABLE users (id INT NOT NULL) |
UNIQUE | 确保列值唯一 | CREATE TABLE products (sku VARCHAR(20) UNIQUE) |
DEFAULT | 未赋值时使用默认值 | CREATE TABLE orders (status INT DEFAULT 0) |
PRIMARY KEY | 主键(唯一标识) | CREATE TABLE employees (id INT PRIMARY KEY AUTO_INCREMENT) |
FOREIGN KEY | 表间关联约束 | CREATE TABLE orders (product_id INT, FOREIGN KEY (product_id) REFERENCES products(id)) |
CHECK | 值范围校验(MySQL不生效) | CREATE TABLE students (age INT CHECK (age > 18)) |
主键深入解析
-- 联合主键
CREATE TABLE course_selection (
student_id INT,
course_id INT,
PRIMARY KEY (student_id, course_id)
);
-- 自增主键(只针对整数类型)
CREATE TABLE logs (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
content TEXT
);
- 主键特性:
- 每表只能有一个主键(单列或联合)
- 自增主键在MySQL中记录最大值持续递增
- 分布式系统常用字符串主键(UUID/雪花算法)
外键实战与解决方案
-- 创建外键约束
CREATE TABLE orders (
id INT PRIMARY KEY,
product_id INT,
FOREIGN KEY (product_id) REFERENCES products(id)
);
- 外键要点:
- 父表(被引用表)需要有索引
- 删除顺序:先子表后父表
- 被引用的父表记录不可直接删除
- 外键困境解决方案:
-- 逻辑删除替代物理删除
ALTER TABLE products ADD is_active BOOLEAN DEFAULT true;
-- 下架商品操作
UPDATE products SET is_active = false WHERE id = 101;
二、表设计:实体与关系建模
设计流程
关系处理方案
关系类型 | 解决方案 | 示例 |
---|---|---|
一对一 | 合并表或主键关联 | 用户表 - 身份证表 |
一对多 | 在多的一方添加外键 | 部门表 - 员工表 |
多对多 | 创建关联表 | 学生表 - 选课关联表 - 课程表 |
多对多关系实现:
CREATE TABLE students (id INT PRIMARY KEY, name VARCHAR(50));
CREATE TABLE courses (id INT PRIMARY KEY, title VARCHAR(50));
-- 关联表
CREATE TABLE student_courses (
student_id INT,
course_id INT,
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES students(id),
FOREIGN KEY (course_id) REFERENCES courses(id)
);
三、查询进阶技巧
1. 查询结果插入
-- 将查询结果插入新表
INSERT INTO inactive_users (id, name)
SELECT id, name FROM users
WHERE last_login < '2023-01-01';
要求:查询结果的列数、类型、顺序必须匹配目标表(列名可以不同)
2. 聚合查询
聚合函数:
函数 | 作用 | 空值处理 |
---|---|---|
COUNT(expr) | 计数 | COUNT(*) 计所有行,COUNT(列) 忽略NULL |
SUM(expr) | 求和 | 忽略NULL |
AVG(expr) | 平均值 | 忽略NULL |
MAX(expr) | 最大值 | 忽略NULL |
MIN(expr) | 最小值 | 忽略NULL |
GROUP BY 分组查询:
-- 按部门统计平均薪资
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 10000 -- 分组后过滤
ORDER BY avg_salary DESC;
SELECT 后的列要么在 GROUP BY 中,要么在聚合函数中
3. 联合查询(多表查询)
笛卡尔积原理:
-- 基础笛卡尔积
SELECT * FROM table1, table2;
-- 行数 = table1行数 × table2行数
-- 列数 = table1列数 + table2列数
两种多表查询写法:
/* 写法1:逗号分隔+WHERE */
SELECT e.name, d.department_name
FROM employees e, departments d
WHERE e.dept_id = d.id;
/* 写法2:JOIN ON */
SELECT e.name, d.department_name
FROM employees e
JOIN departments d ON e.dept_id = d.id;
连接类型对比:
连接类型 | 关键字 | 结果特征 |
---|---|---|
内连接 | INNER JOIN (INNER 可省略) | 仅返回两表匹配的行 |
左外连接 | LEFT JOIN | 左表全显示 + 匹配的右表 |
右外连接 | RIGHT JOIN | 右表全显示 + 匹配的左表 |
MySQL不支持全外连接
自连接实战:
-- 查找同一部门的员工对
SELECT e1.name AS employee1, e2.name AS employee2
FROM employees e1
JOIN employees e2 ON e1.dept_id = e2.dept_id
WHERE e1.id < e2.id; -- 避免重复和自匹配
- 需要给表前后分别起不同的别名,再进行连接
- 如果发现要查询的条件是针对两行而不是两列,就可以考虑使用自连接进行转换
4. 子查询
-- 单行子查询
SELECT name FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
-- 多行子查询(IN运算符)
SELECT name FROM products
WHERE category_id IN (
SELECT id FROM categories WHERE type = 'electronics'
);
注:要避免深层次嵌套子查询(性能杀手)
5. 合并查询
-- 合并两个查询结果(默认去重)
SELECT product FROM storeA
UNION
SELECT product FROM storeB;
-- 保留重复结果
SELECT product FROM storeA
UNION ALL
SELECT product FROM storeB;
要求:被合并的查询必须具有相同的列数、类型和顺序
结语:在数据世界中雕刻优雅
SQL如同雕刻之路——约束是刻刀的锋芒,精准地剔除杂质;表设计是原石的选材,奠定作品的骨架;复杂查询则是精雕细琢的技法,让沉睡的数据焕发生命。
当你用外键编制表间的纽带,用自连接解开行间的谜题,用聚合函数提炼信息的精髓,数据库不再是冰冷的存储容器,而成为流淌智慧的河流。