数据库SQL学习

SQL是通往数据世界的核心语言,掌握它不仅能让你高效操作数据库,更能深入理解数据存储、查询优化和系统架构的精髓。下面,我将按照认知规律,把学习过程拆解为三个逐步深入的阶段,为你详细讲解通用SQL知识。


引言:为什么SQL是数据领域的"硬通货"?

无论你是后端开发、数据分析师,还是专职DBA,SQL(Structured Query Language,结构化查询语言)都是必须熟练掌握的技能。它是操作关系型数据库的通用语言,几乎所有的业务系统最终都需要通过SQL与数据交互。

学习SQL就像学习一门新语言:先掌握基本词汇和语法(基础篇),再学习复杂句式和修辞(进阶篇),最后达到能写诗、能演讲的境界(精通篇)。本文将从这三个层次,带你一步步征服SQL。


基础篇------从零开始搭建数据操作能力

目标:掌握SQL的基本语法,能够独立完成单表的增删改查,理解数据库的核心概念。

1.1 SQL与数据库基础

关系型数据库(如MySQL、PostgreSQL、Oracle)将数据存储在由行和列组成的"表"中,表之间通过关系(如外键)连接。SQL就是用来与这些表对话的语言,它主要分为几类:

  • DDL(数据定义语言):定义数据库和表的结构。
  • DML(数据操作语言):操作表中的数据。
  • DQL(数据查询语言):查询数据(通常归入DML,但常单独强调)。
  • DCL(数据控制语言):管理权限。

书写规范

  • 关键字建议大写,表名/字段名小写,提高可读性。
  • 每条语句以分号结尾。
  • 使用缩进和空格让复杂查询更清晰。

1.2 DDL:搭建数据容器

数据库操作
sql 复制代码
-- 创建数据库,指定字符集(避免中文乱码)
CREATE DATABASE school CHARACTER SET utf8mb4;

-- 查看所有数据库
SHOW DATABASES;

-- 选择当前数据库
USE school;

-- 删除数据库(生产环境务必谨慎!)
DROP DATABASE school;
表操作
sql 复制代码
-- 创建学生表
CREATE TABLE student (
    id INT PRIMARY KEY AUTO_INCREMENT,   -- 主键,自增
    stu_no VARCHAR(20) UNIQUE NOT NULL,  -- 学号,唯一约束,非空
    name VARCHAR(50) NOT NULL,
    gender ENUM('M','F'),
    birth_date DATE,
    class_id INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 查看表结构
DESC student;

-- 修改表(添加字段)
ALTER TABLE student ADD COLUMN phone VARCHAR(20);

-- 修改表(修改字段类型)
ALTER TABLE student MODIFY COLUMN phone VARCHAR(30);

-- 删除表
DROP TABLE student;

1.3 DML:数据的增删改

插入数据
sql 复制代码
-- 插入单条
INSERT INTO student (stu_no, name, gender, birth_date, class_id)
VALUES ('2024001', '张三', 'M', '2005-03-12', 101);

-- 插入多条
INSERT INTO student (stu_no, name, gender, birth_date, class_id) VALUES
('2024002', '李四', 'M', '2005-07-21', 101),
('2024003', '王芳', 'F', '2006-01-05', 102);

-- 从另一张表批量插入(常用于数据迁移)
INSERT INTO student_archive (stu_no, name)
SELECT stu_no, name FROM student WHERE class_id = 101;
更新数据
sql 复制代码
-- 一定要加WHERE条件,否则全表更新!
UPDATE student SET class_id = 103 WHERE stu_no = '2024002';
删除数据
sql 复制代码
-- 同样要加WHERE
DELETE FROM student WHERE stu_no = '2024003';

-- 删除所有数据但保留表结构(比DELETE快)
TRUNCATE TABLE student;

1.4 DQL:单表查询的核心语法

查询是SQL最常用也最灵活的部分,基本结构为:

sql 复制代码
SELECT 字段1, 字段2
FROM 表名
[WHERE 条件]
[GROUP BY 分组字段]
[HAVING 分组后条件]
[ORDER BY 排序字段 [ASC|DESC]]
[LIMIT 起始偏移, 行数];
基础查询示例
sql 复制代码
-- 查询所有字段
SELECT * FROM student;

-- 查询指定字段,并起别名
SELECT name AS 姓名, birth_date AS 出生日期 FROM student;

-- 去重查询
SELECT DISTINCT class_id FROM student;

-- 条件查询
SELECT * FROM student WHERE class_id = 101 AND gender = 'M';

-- 模糊查询(%代表任意多个字符,_代表单个字符)
SELECT * FROM student WHERE name LIKE '张%';

-- 范围查询
SELECT * FROM student WHERE birth_date BETWEEN '2005-01-01' AND '2005-12-31';

-- 排序
SELECT * FROM student ORDER BY birth_date DESC;

-- 限制返回行数(分页)
SELECT * FROM student LIMIT 0, 10;  -- 第1页,每页10条
SELECT * FROM student LIMIT 10, 10; -- 第2页
常用函数
  • 聚合函数COUNT(*), SUM(score), AVG(age), MAX(salary), MIN(salary)
  • 字符串函数CONCAT(first_name, last_name), SUBSTR(name,1,3), UPPER(email)
  • 日期函数YEAR(birth_date), DATEDIFF(NOW(), birth_date)
  • 数学函数ROUND(price,2), ABS(diff)

示例:统计每个班级的学生人数和平均年龄

sql 复制代码
SELECT class_id,
       COUNT(*) AS student_count,
       AVG(TIMESTAMPDIFF(YEAR, birth_date, CURDATE())) AS avg_age
FROM student
GROUP BY class_id;

进阶篇------掌握多表关联与复杂数据处理

目标:理解关系数据库的"关系"本质,能够编写多表连接、子查询,掌握视图、索引基础,初步认识事务。

2.1 多表连接(JOIN)

当数据分散在不同表时,需要通过连接将它们组合起来。假设还有班级表 class

sql 复制代码
CREATE TABLE class (
    id INT PRIMARY KEY,
    class_name VARCHAR(50),
    teacher_id INT
);
INNER JOIN(内连接)

只返回两表中匹配的行。

sql 复制代码
SELECT s.name, c.class_name
FROM student s
INNER JOIN class c ON s.class_id = c.id;
LEFT JOIN(左连接)

返回左表所有行,右表若无匹配则补NULL。

sql 复制代码
SELECT s.name, c.class_name
FROM student s
LEFT JOIN class c ON s.class_id = c.id;  -- 即使学生没有班级(class_id为NULL),也会显示学生信息
RIGHT JOIN 与 FULL JOIN
  • RIGHT JOINLEFT JOIN 方向相反。
  • FULL JOIN(MySQL不支持,可用 LEFT JOIN UNION RIGHT JOIN 模拟)返回左右表所有行。
多表连接技巧
  • 尽量使用表别名简化书写。
  • 明确连接条件,避免产生笛卡尔积(无连接条件时两表行数相乘,结果巨大且无意义)。

2.2 子查询

在一个查询内部嵌套另一个查询。

标量子查询(返回单个值)
sql 复制代码
-- 查询工资高于平均工资的员工
SELECT name, salary FROM employee
WHERE salary > (SELECT AVG(salary) FROM employee);
列子查询(返回一列)
sql 复制代码
-- 查询在"技术部"的所有员工
SELECT name FROM employee
WHERE dept_id IN (SELECT id FROM department WHERE name = '技术部');
行子查询(返回一行多列)
sql 复制代码
-- 查询与"张三"同部门同年龄的员工
SELECT * FROM employee
WHERE (dept_id, age) = (SELECT dept_id, age FROM employee WHERE name = '张三');
表子查询(返回多行多列,常用于FROM子句)
sql 复制代码
-- 查询各部门平均工资,然后找出平均工资大于5000的部门
SELECT dept_id, avg_salary
FROM (SELECT dept_id, AVG(salary) AS avg_salary FROM employee GROUP BY dept_id) AS dept_avg
WHERE avg_salary > 5000;
EXISTS 与 NOT EXISTS

检查子查询是否返回任何行,通常用于关联子查询。

sql 复制代码
-- 查询有员工的部门
SELECT * FROM department d
WHERE EXISTS (SELECT 1 FROM employee e WHERE e.dept_id = d.id);

2.3 分组与聚合进阶

HAVING 过滤分组
sql 复制代码
-- 查询平均年龄大于30岁的班级
SELECT class_id, AVG(age) AS avg_age
FROM student
GROUP BY class_id
HAVING avg_age > 30;
ROLLUP 实现小计与总计
sql 复制代码
-- 按班级和性别分组统计人数,同时生成班级小计和总计
SELECT class_id, gender, COUNT(*) AS cnt
FROM student
GROUP BY class_id, gender WITH ROLLUP;

2.4 视图(VIEW):虚拟表

视图是保存的查询逻辑,可以像表一样查询,但不存储数据。

sql 复制代码
-- 创建视图:显示学生姓名、班级名和班主任
CREATE VIEW v_student_class AS
SELECT s.name AS student_name, c.class_name, t.name AS teacher_name
FROM student s
JOIN class c ON s.class_id = c.id
JOIN teacher t ON c.teacher_id = t.id;

-- 使用视图
SELECT * FROM v_student_class WHERE class_name = '高三(1)班';

2.5 索引基础

索引是提升查询速度的关键,但会降低写入性能。

sql 复制代码
-- 创建普通索引
CREATE INDEX idx_stu_name ON student(name);

-- 创建唯一索引(自动保证唯一性)
CREATE UNIQUE INDEX idx_stu_no ON student(stu_no);

-- 创建联合索引(多个字段)
CREATE INDEX idx_class_age ON student(class_id, age);

-- 查看索引
SHOW INDEX FROM student;

-- 删除索引
DROP INDEX idx_stu_name ON student;

索引使用原则

  • 经常用于 WHEREJOINORDER BY 的字段考虑建索引。
  • 区分度低的字段(如性别)不适合建索引。
  • 联合索引遵循最左前缀原则,查询条件必须包含索引的最左列才能用到索引。

2.6 事务基础

事务确保一组操作要么全部成功,要么全部失败。

sql 复制代码
-- 开启事务
START TRANSACTION;

-- 执行操作
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;

-- 提交事务(成功)
COMMIT;

-- 如果出错,回滚事务
ROLLBACK;

事务的ACID特性:

  • 原子性:事务不可分割。
  • 一致性:数据从一种一致状态变为另一种一致状态。
  • 隔离性:并发事务互不干扰。
  • 持久性:提交后数据永久保存。

精通篇------深入性能调优与架构设计

目标:能够诊断并优化慢SQL,理解数据库底层机制,设计高可用、可扩展的数据架构。

3.1 查询优化与执行计划

使用 EXPLAIN 分析查询
sql 复制代码
EXPLAIN SELECT * FROM student WHERE name = '张三';

关键输出列解读:

  • type :访问类型,从好到坏依次为 system > const > eq_ref > ref > range > index > ALL(全表扫描)。
  • possible_keys:可能用到的索引。
  • key:实际用到的索引。
  • rows:预估扫描的行数。
  • Extra :额外信息,如 Using index(覆盖索引)、Using whereUsing filesort(需要优化排序)。
常见优化手段
  • **避免 SELECT ***:只取需要的列,减少IO。
  • 避免索引列上使用函数或计算WHERE YEAR(birth_date)=2005 应改为 WHERE birth_date BETWEEN '2005-01-01' AND '2005-12-31'
  • 使用覆盖索引:索引包含了查询所需的所有字段,避免回表。
  • 优化排序ORDER BY 字段尽量与索引顺序一致。
  • 拆分大查询:将复杂关联拆分成多次简单查询,有时反而更快。

3.2 索引深入

索引失效场景
  • 对索引列使用 LIKE '%xxx'(左模糊)。
  • 对索引列使用 !=<>(可能失效,取决于数据分布)。
  • 联合索引不满足最左前缀。
  • 数据类型隐式转换(如字符串字段不写引号)。
索引选择性与基数
  • 选择性 = 不同值的数量 / 总行数。选择性越高,索引越有效。
  • 对于低基数字段(如性别),索引效果差,可能不如全表扫描。

3.3 事务隔离级别与锁

四种隔离级别
  1. READ UNCOMMITTED:脏读、不可重复读、幻读都可能发生。
  2. READ COMMITTED:避免脏读,但可能不可重复读、幻读(Oracle默认)。
  3. REPEATABLE READ:避免脏读和不可重复读,但可能幻读(MySQL InnoDB默认,通过间隙锁一定程度上避免幻读)。
  4. SERIALIZABLE:全部避免,性能最低。

设置隔离级别:

sql 复制代码
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
锁机制
  • 共享锁SELECT ... LOCK IN SHARE MODE(读锁,允许其他事务读,但阻止写)。
  • 排他锁SELECT ... FOR UPDATE(写锁,阻止其他事务读写)。
  • 行锁表锁间隙锁(InnoDB特有,锁定范围防止幻读)。

死锁:两个事务互相等待对方释放锁,数据库会自动检测并回滚其中一个。

3.4 高级功能

分区表

将大表物理分割为多个小分区,提升查询维护效率。

sql 复制代码
CREATE TABLE orders (
    id INT,
    order_date DATE,
    amount DECIMAL(10,2)
) PARTITION BY RANGE (YEAR(order_date)) (
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);
存储过程与触发器
  • 存储过程 :预编译的SQL集合,减少网络开销,提高复用性。

    sql 复制代码
    DELIMITER //
    CREATE PROCEDURE GetStudentsByClass(IN c_id INT)
    BEGIN
        SELECT * FROM student WHERE class_id = c_id;
    END //
    DELIMITER ;
    CALL GetStudentsByClass(101);
  • 触发器 :在插入、更新、删除时自动执行。

    sql 复制代码
    CREATE TRIGGER before_student_insert
    BEFORE INSERT ON student
    FOR EACH ROW
    SET NEW.created_at = NOW();
窗口函数(SQL标准,MySQL 8.0+支持)

实现排名、移动平均等复杂分析。

sql 复制代码
SELECT name, salary,
       RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dept_rank
FROM employee;

3.5 高可用与分库分表

读写分离
  • 主库 处理写操作,从库处理读操作,通过主从复制同步数据。
  • 应用层或中间件(如ProxySQL)路由SQL。
分库分表
  • 垂直拆分:按业务将字段拆分到不同库表(如将用户基础信息和扩展信息分开)。
  • 水平拆分:按某种规则(如用户ID取模)将数据分散到多个结构相同的表或库中。
  • 常用中间件:ShardingSphere、MyCAT。

3.6 监控与诊断

DBA的日常工作离不开监控:

  • 慢查询日志 :记录执行时间超过 long_query_time 的SQL,定期分析优化。
  • SHOW PROCESSLIST:查看当前正在执行的线程,快速定位锁等待或长事务。
  • 性能视图 :如MySQL的 information_schemaperformance_schema,Oracle的 v$ 视图。
  • 工具:Prometheus + Grafana、pt-query-digest(Percona Toolkit)。

SQL 快速参考

SQL 语句 语法 说明
AND / OR SELECT column_name(s) FROM table_name WHERE condition AND/OR condition AND:表示逻辑与OR:表示逻辑或
ALTER TABLE ALTER TABLE table_name ADD column_name datatype ALTER TABLE table_name DROP COLUMN column_name 用于修改现有表的结构,添加或删除列。
AS (alias) SELECT column_name AS column_alias FROM table_name SELECT column_name FROM table_name AS table_alias 用于为列或表指定别名。
BETWEEN SELECT column_name(s) FROM table_name WHERE column_name BETWEEN value1 AND value2 用于筛选在指定范围内的记录。
CREATE DATABASE CREATE DATABASE database_name 用于创建新数据库。
CREATE TABLE CREATE TABLE table_name (column_name1 data_type, column_name2 data_type, ...) 用于创建新表,定义表的列和数据类型。
CREATE INDEX CREATE INDEX index_name ON table_name (column_name) CREATE UNIQUE INDEX index_name ON table_name (column_name) 用于在表的列上创建索引,以加速查询。
CREATE VIEW CREATE VIEW view_name AS SELECT column_name(s) FROM table_name WHERE condition 用于创建视图,以保存复杂查询的结果。
DELETE DELETE FROM table_name WHERE some_column=some_value DELETE FROM table_name DELETE * FROM table_name 用于删除表中的记录,DELETE FROM table_name 和 DELETE * FROM table_name 会删除所有记录。
DROP DATABASE DROP DATABASE database_name 用于删除数据库。
DROP INDEX DROP INDEX table_name.index_name (SQL Server) DROP INDEX index_name ON table_name (MS Access) DROP INDEX index_name (DB2/Oracle) ALTER TABLE table_name DROP INDEX index_name (MySQL) 用于删除表上的索引。
DROP TABLE DROP TABLE table_name 用于删除表及其所有数据。
GROUP BY SELECT column_name, aggregate_function(column_name) FROM table_name WHERE column_name operator value GROUP BY column_name 用于按一个或多个列对结果集进行分组。
HAVING SELECT column_name, aggregate_function(column_name) FROM table_name WHERE column_name operator value GROUP BY column_name HAVING aggregate_function(column_name) operator value 用于对分组后的结果集进行过滤。
IN SELECT column_name(s) FROM table_name WHERE column_name IN (value1, value2, ...) 用于筛选匹配集合中某一值的记录。
INSERT INTO INSERT INTO table_name VALUES (value1, value2, ...) INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...) 用于向表中插入新记录。
INNER JOIN SELECT column_name(s) FROM table_name1 INNER JOIN table_name2 ON table_name1.column_name=table_name2.column_name 用于返回两个表中匹配的记录。
LEFT JOIN SELECT column_name(s) FROM table_name1 LEFT JOIN table_name2 ON table_name1.column_name=table_name2.column_name 用于返回左表中的所有记录和右表中的匹配记录。
RIGHT JOIN SELECT column_name(s) FROM table_name1 RIGHT JOIN table_name2 ON table_name1.column_name=table_name2.column_name 用于返回右表中的所有记录和左表中的匹配记录。
FULL JOIN SELECT column_name(s) FROM table_name1 FULL JOIN table_name2 ON table_name1.column_name=table_name2.column_name 用于返回两个表中的所有记录,不论是否匹配。
LIKE SELECT column_name(s) FROM table_name WHERE column_name LIKE pattern 用于筛选匹配特定模式的记录。
ORDER BY SELECT column_name(s) FROM table_name ORDER BY column_name [ASC/DESC] 用于对结果集进行排序。ASC 表示升序排列(默认),DESC 表示降序排列。
SELECT SELECT column_name(s) FROM table_name 用于从表中选择数据。
SELECT SELECT * FROM table_name 用于选择表中的所有列。
SELECT DISTINCT SELECT DISTINCT column_name(s) FROM table_name 用于返回唯一不同的值。
SELECT INTO SELECT * INTO new_table_name [IN externaldatabase] FROM old_table_name SELECT column_name(s) INTO new_table_name [IN externaldatabase] FROM old_table_name 用于从一个表中选择数据并插入到新表中。
SELECT TOP SELECT TOP numbe/percent column_name(s) FROM table_name ORDER BY column_name [ASC/DESC] 从表中返回前指定数量的记录,可以指定绝对数量或百分比。
TRUNCATE TABLE TRUNCATE TABLE table_name 用于删除表中的所有数据,但不删除表结构。
UNION SELECT column_name(s) FROM table_name1 UNION SELECT column_name(s) FROM table_name2 用于合并两个或多个 SELECT 语句的结果集,不包含重复记录。
UNION ALL SELECT column_name(s) FROM table_name1 UNION ALL SELECT column_name(s) FROM table_name2 用于合并两个或多个 SELECT 语句的结果集,包含重复记录。
UPDATE UPDATE table_name SET column1=value, column2=value, ... WHERE some_column=some_value 用于修改表中的现有记录。
WHERE SELECT column_name(s) FROM table_name WHERE column_name operator value 用于过滤记录,指定查询条件。

结语:SQL之路,行则将至

从基础的增删改查,到复杂的查询优化与架构设计,SQL的学习是一个循序渐进、理论与实践相结合的过程。在日常学习工作过程中建议:

  1. 多写多练:在实际业务中尝试不同的写法,对比执行计划。
  2. 关注原理:理解数据库的底层实现(如B+树、锁机制),能让你在调优时更有底气。
  3. 拥抱变化:数据库技术日新月异,NewSQL、云原生数据库不断涌现,但核心SQL知识永不过时。
相关推荐
jnrjian2 小时前
ORA-01017 查找机器名 用户名 以及library cache lock 参数含义
数据库·oracle
十月南城2 小时前
数据湖技术对比——Iceberg、Hudi、Delta的表格格式与维护策略
大数据·数据库·数据仓库·hive·hadoop·spark
Henry Zhu1232 小时前
数据库:并发控制基本概念
服务器·数据库
银发控、2 小时前
数据库隔离级别与三个问题(脏读、不可重复读、幻读)
数据库·面试
爱可生开源社区2 小时前
MySQL 性能优化:真正重要的变量
数据库·mysql
ZeroNews内网穿透3 小时前
谷歌封杀OpenClaw背后:本地部署或是出路
运维·服务器·数据库·安全
~远在太平洋~3 小时前
Linux 基础命令
linux·服务器·数据库
小马爱打代码3 小时前
MySQL性能优化核心:InnoDB Buffer Pool 详解
数据库·mysql·性能优化
OceanBase数据库官方博客3 小时前
解析 OceanBase 生态工具链 —— OAT / obd / OCP / obshell
数据库·oceanbase·分布式数据库