【无标题】

SQL 语句语法详解(完整学习手册)

以 SQL Server(Transact-SQL / T-SQL)为主线,兼顾通用标准 SQL语法,覆盖从基础查询到高级特性、事务、索引、视图、存储过程、函数、触发器、动态 SQL 等。绝大多数语法在不同数据库中通用,差异处会特别标注。


目录

文章目录

  • [SQL 语句语法详解(完整学习手册)](#SQL 语句语法详解(完整学习手册))
    • 目录
    • [一、SQL 概述与分类](#一、SQL 概述与分类)
    • 二、数据库与表的基本操作(DDL)
      • [2.1 数据库](#2.1 数据库)
      • [2.2 创建表](#2.2 创建表)
      • [2.3 常见数据类型](#2.3 常见数据类型)
      • [2.4 修改表结构(ALTER)](#2.4 修改表结构(ALTER))
      • [2.5 删除 / 清空表](#2.5 删除 / 清空表)
    • 三、数据操纵(DML)
      • [3.1 INSERT 插入](#3.1 INSERT 插入)
      • [3.2 UPDATE 更新](#3.2 UPDATE 更新)
      • [3.3 DELETE 删除](#3.3 DELETE 删除)
    • 四、基础查询(SELECT)
      • [4.1 基本语法](#4.1 基本语法)
      • [4.2 查询列](#4.2 查询列)
    • 五、条件过滤与逻辑运算
      • [5.1 比较运算符](#5.1 比较运算符)
      • [5.2 逻辑运算符](#5.2 逻辑运算符)
      • [5.3 范围 BETWEEN](#5.3 范围 BETWEEN)
      • [5.4 集合 IN / NOT IN](#5.4 集合 IN / NOT IN)
      • [5.5 模糊匹配 LIKE](#5.5 模糊匹配 LIKE)
      • [5.6 空值判断 IS NULL](#5.6 空值判断 IS NULL)
      • [5.7 EXISTS](#5.7 EXISTS)
    • 六、排序、分页与去重
      • [6.1 排序 ORDER BY](#6.1 排序 ORDER BY)
      • [6.2 返回记录数 / 分页](#6.2 返回记录数 / 分页)
    • 七、聚合函数与分组
      • [7.1 聚合函数](#7.1 聚合函数)
      • [7.2 GROUP BY 分组](#7.2 GROUP BY 分组)
      • [7.3 HAVING 分组后过滤](#7.3 HAVING 分组后过滤)
    • 八、多表连接(JOIN)
      • [8.1 连接类型](#8.1 连接类型)
      • [8.2 连接示例](#8.2 连接示例)
      • [8.3 传统写法(隐式连接)](#8.3 传统写法(隐式连接))
    • 九、子查询与集合运算
      • [9.1 子查询](#9.1 子查询)
      • [9.2 集合运算](#9.2 集合运算)
    • 十、常用函数
      • [10.1 数学函数](#10.1 数学函数)
      • [10.2 聚合与空值处理](#10.2 聚合与空值处理)
      • [10.3 类型转换](#10.3 类型转换)
    • 十一、字符串与日期处理
      • [11.1 字符串函数](#11.1 字符串函数)
      • [11.2 日期函数](#11.2 日期函数)
    • 十二、约束与完整性
      • [12.1 约束类型](#12.1 约束类型)
      • [12.2 示例](#12.2 示例)
      • [12.3 给已有表加约束](#12.3 给已有表加约束)
    • 十三、索引
      • [13.1 索引类型](#13.1 索引类型)
      • [13.2 创建索引](#13.2 创建索引)
      • [13.3 注意事项](#13.3 注意事项)
    • 十四、视图
    • 十五、事务与锁
    • 十六、存储过程与函数
      • [16.1 存储过程](#16.1 存储过程)
      • [16.2 用户自定义函数(UDF)](#16.2 用户自定义函数(UDF))
    • 十七、触发器
    • 十八、游标
    • [十九、动态 SQL](#十九、动态 SQL)
    • 二十、最佳实践与常见坑
      • [20.1 最佳实践](#20.1 最佳实践)
      • [20.2 常见坑](#20.2 常见坑)
    • [附:SQL 关键字速查表](#附:SQL 关键字速查表)

一、SQL 概述与分类

SQL(Structured Query Language,结构化查询语言)是关系型数据库的标准语言,主要有四大子分类:

分类 全称 作用 关键字示例
DDL Data Definition Language 定义/修改数据库结构 CREATE ALTER DROP TRUNCATE
DML Data Manipulation Language 操作数据 SELECT INSERT UPDATE DELETE
DCL Data Control Language 权限控制 GRANT REVOKE
TCL Transaction Control Language 事务控制 BEGIN COMMIT ROLLBACK SAVEPOINT

有些分类将 SELECT 单独归为 DQL(Data Query Language,数据查询语言)。


二、数据库与表的基本操作(DDL)

2.1 数据库

sql 复制代码
-- 创建数据库
CREATE DATABASE MyDB;

-- 创建数据库(指定字符集与排序规则)
CREATE DATABASE MyDB
COLLATE Chinese_PRC_CI_AS;  -- SQL Server

-- 查看所有数据库
SELECT name FROM sys.databases;   -- SQL Server
SHOW DATABASES;                   -- MySQL

-- 切换 / 使用数据库
USE MyDB;                         -- SQL Server / MySQL

-- 删除数据库(慎用)
DROP DATABASE MyDB;

2.2 创建表

sql 复制代码
CREATE TABLE Students (
    StudentID   INT           NOT NULL,        -- 学号
    Name        NVARCHAR(50)  NOT NULL,        -- 姓名
    Gender      CHAR(1)       NULL,            -- 性别 M/F
    Age         TINYINT       NULL,            -- 年龄
    Birthday    DATE          NULL,            -- 生日
    Score       DECIMAL(5,2)  DEFAULT 0,       -- 成绩(两位小数)
    CreateTime  DATETIME      DEFAULT GETDATE(),
    PRIMARY KEY (StudentID)                     -- 主键
);

2.3 常见数据类型

类别 类型 说明
整数 TINYINT / SMALLINT / INT / BIGINT 1/2/4/8 字节整数
小数 DECIMAL(p,s) / NUMERIC(p,s) 精确小数,p 总位数、s 小数位
浮点 FLOAT / REAL 近似值
字符 CHAR(n) / VARCHAR(n) 定长 / 变长(非 Unicode)
字符 NCHAR(n) / NVARCHAR(n) Unicode 定长 / 变长
文本 TEXT / NTEXT / VARCHAR(MAX) / NVARCHAR(MAX) 大文本
日期 DATE / TIME / DATETIME / DATETIME2 / SMALLDATETIME 日期时间
二进制 BINARY / VARBINARY / IMAGE 二进制数据
布尔 BIT(SQL Server)/ BOOLEAN(PostgreSQL) 0/1 或 true/false
其他 UNIQUEIDENTIFIER(GUID)/ XML / JSON 特殊类型

2.4 修改表结构(ALTER)

sql 复制代码
-- 添加列
ALTER TABLE Students ADD Email NVARCHAR(100);

-- 修改列数据类型
ALTER TABLE Students ALTER COLUMN Name NVARCHAR(100);  -- SQL Server

-- 删除列
ALTER TABLE Students DROP COLUMN Email;

-- 重命名列(SQL Server)
EXEC sp_rename 'Students.Name', 'FullName', 'COLUMN';

2.5 删除 / 清空表

sql 复制代码
-- 删除表结构 + 数据
DROP TABLE Students;

-- 清空表数据(保留结构,不可回滚,速度快)
TRUNCATE TABLE Students;

-- 清空表数据(可回滚,逐行删除)
DELETE FROM Students;

TRUNCATEDELETE 区别:TRUNCATE 不记录逐行日志、不可回滚、不会触发触发器、会重置自增列;DELETE 反之。


三、数据操纵(DML)

3.1 INSERT 插入

sql 复制代码
-- 插入全部列
INSERT INTO Students VALUES (1, '张三', 'M', 20, '2004-01-01', 88.5, GETDATE());

-- 插入指定列
INSERT INTO Students (StudentID, Name, Gender) VALUES (2, '李四', 'F');

-- 一次插入多行
INSERT INTO Students (StudentID, Name, Gender, Age)
VALUES
    (3, '王五', 'M', 21),
    (4, '赵六', 'F', 22),
    (5, '孙七', 'M', 19);

-- 从另一张表复制插入
INSERT INTO Students_backup (StudentID, Name)
SELECT StudentID, Name FROM Students WHERE Age > 20;

-- 插入并返回自增 ID(SQL Server)
INSERT INTO Students (Name) OUTPUT INSERTED.StudentID VALUES ('小明');

3.2 UPDATE 更新

sql 复制代码
-- 更新单列
UPDATE Students SET Score = 90 WHERE StudentID = 1;

-- 更新多列
UPDATE Students SET Age = 21, Score = 95 WHERE StudentID = 1;

-- 通过子查询更新
UPDATE Students
SET Score = (SELECT AVG(Score) FROM Students)
WHERE StudentID = 2;

务必带上 WHERE,否则会更新全表!

3.3 DELETE 删除

sql 复制代码
-- 删除满足条件的行
DELETE FROM Students WHERE StudentID = 5;

-- 删除所有行(保留结构)
DELETE FROM Students;

四、基础查询(SELECT)

4.1 基本语法

sql 复制代码
-- 语法顺序(注意与执行顺序不同)
SELECT [DISTINCT] 列名列表
FROM 表名
[WHERE 条件]
[GROUP BY 分组列]
[HAVING 分组后条件]
[ORDER BY 排序列 [ASC|DESC]]
[LIMIT / TOP / OFFSET...];

逻辑执行顺序 (理解 SQL 的关键):

FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT/TOP

4.2 查询列

sql 复制代码
-- 查询所有列
SELECT * FROM Students;

-- 查询指定列
SELECT Name, Age FROM Students;

-- 列别名(AS 可省略)
SELECT Name AS 姓名, Age AS 年龄 FROM Students;

-- 计算列
SELECT Name, Score * 1.1 AS 加分后成绩 FROM Students;

-- 常量列
SELECT Name, '学生' AS 类型 FROM Students;

-- 去重
SELECT DISTINCT Gender FROM Students;

五、条件过滤与逻辑运算

5.1 比较运算符

= <>!= > >= < <=

sql 复制代码
SELECT * FROM Students WHERE Age >= 20;
SELECT * FROM Students WHERE Gender <> 'M';

5.2 逻辑运算符

AND OR NOT

sql 复制代码
SELECT * FROM Students WHERE Age >= 20 AND Gender = 'M';
SELECT * FROM Students WHERE Age < 18 OR Age > 30;
SELECT * FROM Students WHERE NOT (Gender = 'M');

优先级:NOT > AND > OR,必要时用括号明确。

5.3 范围 BETWEEN

sql 复制代码
-- 等价于 Age >= 18 AND Age <= 25
SELECT * FROM Students WHERE Age BETWEEN 18 AND 25;

SELECT * FROM Students WHERE Birthday BETWEEN '2000-01-01' AND '2005-12-31';

5.4 集合 IN / NOT IN

sql 复制代码
SELECT * FROM Students WHERE StudentID IN (1, 2, 3);
SELECT * FROM Students WHERE Gender NOT IN ('M');

5.5 模糊匹配 LIKE

通配符 含义
% 任意长度字符(含 0 个)
_ 单个字符
[abc] 方括号内任一字符
[^abc] / [!abc] 不在方括号内的任意字符
sql 复制代码
SELECT * FROM Students WHERE Name LIKE '张%';        -- 以"张"开头
SELECT * FROM Students WHERE Name LIKE '%三';        -- 以"三"结尾
SELECT * FROM Students WHERE Name LIKE '%小%';       -- 包含"小"
SELECT * FROM Students WHERE Name LIKE '王_';        -- "王"开头且共两字
SELECT * FROM Students WHERE Name LIKE '[张李]%';    -- 张或李开头
SELECT * FROM Students WHERE Name LIKE '[^张]%';     -- 非张开头

5.6 空值判断 IS NULL

sql 复制代码
SELECT * FROM Students WHERE Email IS NULL;
SELECT * FROM Students WHERE Email IS NOT NULL;

NULL 不能直接用 =<> 比较,必须用 IS NULLNULL 与任何值运算结果都是 NULL

5.7 EXISTS

sql 复制代码
-- 存在相关记录则返回
SELECT * FROM Students s
WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.StudentID = s.StudentID);

六、排序、分页与去重

6.1 排序 ORDER BY

sql 复制代码
SELECT * FROM Students ORDER BY Age ASC;          -- 升序(默认)
SELECT * FROM Students ORDER BY Age DESC;         -- 降序
SELECT * FROM Students ORDER BY Gender, Age DESC; -- 多列排序

6.2 返回记录数 / 分页

sql 复制代码
-- SQL Server:TOP
SELECT TOP 10 * FROM Students ORDER BY Age DESC;

-- MySQL / PostgreSQL:LIMIT
SELECT * FROM Students ORDER BY Age DESC LIMIT 10;

-- MySQL 分页:LIMIT 偏移, 数量
SELECT * FROM Students ORDER BY StudentID LIMIT 10, 10;  -- 第11~20行

-- SQL Server 分页:OFFSET...FETCH(SQL Server 2012+)
SELECT * FROM Students ORDER BY StudentID
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

-- 通用分页:ROW_NUMBER() 窗口函数
SELECT * FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY StudentID) AS rn
    FROM Students
) t WHERE rn BETWEEN 11 AND 20;

七、聚合函数与分组

7.1 聚合函数

函数 说明
COUNT(*) 统计行数(含 NULL)
COUNT(列) 统计非空值个数
SUM(列) 求和
AVG(列) 平均值
MAX(列) / MIN(列) 最大值 / 最小值
sql 复制代码
SELECT COUNT(*) AS 总人数 FROM Students;
SELECT AVG(Score) AS 平均分 FROM Students;
SELECT MAX(Score) AS 最高分, MIN(Score) AS 最低分 FROM Students;

7.2 GROUP BY 分组

sql 复制代码
-- 按性别统计人数
SELECT Gender, COUNT(*) AS 人数
FROM Students
GROUP BY Gender;

-- 按性别统计平均分
SELECT Gender, AVG(Score) AS 平均分
FROM Students
GROUP BY Gender;

规则:SELECT 中出现的非聚合列,必须出现在 GROUP BY 中。

7.3 HAVING 分组后过滤

HAVING 过滤分组后的结果(WHERE 过滤分组前的行)。HAVING 中可使用聚合函数。

sql 复制代码
-- 查询平均分高于 85 的性别分组
SELECT Gender, AVG(Score) AS 平均分
FROM Students
GROUP BY Gender
HAVING AVG(Score) > 85;

WHEREHAVING 区别:WHERE 作用于原始行、不能用聚合函数;HAVING 作用于分组结果、可用聚合函数。


八、多表连接(JOIN)

8.1 连接类型

类型 说明
INNER JOIN 内连接,只返回两表匹配的行
LEFT JOIN 左外连接,返回左表全部 + 右表匹配(无匹配为 NULL)
RIGHT JOIN 右外连接,返回右表全部 + 左表匹配
FULL JOIN 全外连接,返回两表所有(MySQL 不支持)
CROSS JOIN 交叉连接,笛卡尔积

示意图(左表 A、右表 B):

连接 结果集
INNER A ∩ B 的匹配行
LEFT A 全部 + 匹配的 B
RIGHT B 全部 + 匹配的 A
FULL A ∪ B 全部
CROSS A × B 全部组合

8.2 连接示例

假设有 Students(学生)和 Orders(订单)两张表,通过 StudentID 关联。

sql 复制代码
-- 内连接(有订单的学生)
SELECT s.Name, o.OrderID, o.Amount
FROM Students s
INNER JOIN Orders o ON s.StudentID = o.StudentID;

-- 左连接(所有学生,含无订单的)
SELECT s.Name, o.OrderID
FROM Students s
LEFT JOIN Orders o ON s.StudentID = o.StudentID;

-- 右连接
SELECT s.Name, o.OrderID
FROM Students s
RIGHT JOIN Orders o ON s.StudentID = o.StudentID;

-- 全连接(SQL Server / PostgreSQL)
SELECT s.Name, o.OrderID
FROM Students s
FULL JOIN Orders o ON s.StudentID = o.StudentID;

-- 交叉连接(笛卡尔积)
SELECT * FROM Students CROSS JOIN Classes;

-- 多表连接
SELECT s.Name, c.ClassName, o.Amount
FROM Students s
INNER JOIN Classes c ON s.ClassID = c.ClassID
INNER JOIN Orders o ON s.StudentID = o.StudentID;

-- 自连接(同一张表关联,如员工与上级)
SELECT e1.Name AS 员工, e2.Name AS 上级
FROM Employees e1
LEFT JOIN Employees e2 ON e1.ManagerID = e2.EmployeeID;

8.3 传统写法(隐式连接)

sql 复制代码
-- 等价于 INNER JOIN(不推荐,可读性差)
SELECT s.Name, o.OrderID
FROM Students s, Orders o
WHERE s.StudentID = o.StudentID;

九、子查询与集合运算

9.1 子查询

sql 复制代码
-- 标量子查询(返回单个值)
SELECT Name FROM Students
WHERE Score = (SELECT MAX(Score) FROM Students);

-- 多行子查询(IN)
SELECT Name FROM Students
WHERE StudentID IN (SELECT StudentID FROM Orders);

-- EXISTS 相关子查询
SELECT s.Name
FROM Students s
WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.StudentID = s.StudentID);

-- 派生表 / 子查询作为表
SELECT * FROM (
    SELECT Gender, AVG(Score) AS avg_score FROM Students GROUP BY Gender
) t WHERE avg_score > 80;

9.2 集合运算

sql 复制代码
-- UNION:合并并去重
SELECT Name FROM StudentsA
UNION
SELECT Name FROM StudentsB;

-- UNION ALL:合并不去重(性能更好)
SELECT Name FROM StudentsA
UNION ALL
SELECT Name FROM StudentsB;

-- INTERSECT:交集
SELECT StudentID FROM StudentsA
INTERSECT
SELECT StudentID FROM StudentsB;

-- EXCEPT / MINUS:差集(前者有而后者无)
SELECT StudentID FROM StudentsA
EXCEPT   -- SQL Server / PostgreSQL
SELECT StudentID FROM StudentsB;

UNION 去重、会排序,性能稍低;UNION ALL 不去重、更快。


十、常用函数

10.1 数学函数

sql 复制代码
ABS(-5)          -- 5 绝对值
CEILING(3.2)     -- 4 向上取整(MySQL 用 CEIL)
FLOOR(3.8)       -- 3 向下取整
ROUND(3.14159, 2)-- 3.14 四舍五入
POWER(2, 10)     -- 1024 幂
SQRT(16)         -- 4 平方根
RAND()           -- 0~1 随机数

10.2 聚合与空值处理

sql 复制代码
-- 空值替换
ISNULL(Score, 0)      -- SQL Server
COALESCE(Score, 0)    -- 标准 SQL,返回第一个非空值
NVL(Score, 0)         -- Oracle
IFNULL(Score, 0)      -- MySQL

-- 条件判断(CASE)
SELECT Name,
    CASE
        WHEN Score >= 90 THEN '优秀'
        WHEN Score >= 60 THEN '及格'
        ELSE '不及格'
    END AS 等级
FROM Students;

-- CASE 简单表达式
SELECT Name,
    CASE Gender
        WHEN 'M' THEN '男'
        WHEN 'F' THEN '女'
        ELSE '未知'
    END AS 性别
FROM Students;

10.3 类型转换

sql 复制代码
CAST(Score AS VARCHAR(10))               -- 标准
CONVERT(VARCHAR(10), Score)              -- SQL Server
CAST('2024-01-01' AS DATE)               -- 字符串转日期
CAST(StudentID AS VARCHAR) + Name        -- 数值转字符串拼接

十一、字符串与日期处理

11.1 字符串函数

sql 复制代码
-- 长度
LEN(Name)                 -- SQL Server
LENGTH(Name) / CHAR_LENGTH(Name) -- MySQL / PostgreSQL

-- 拼接
CONCAT('Hello', ' ', Name)          -- 标准
'Hello ' + Name                     -- SQL Server 用 +

-- 大小写
UPPER(Name) / LOWER(Name)

-- 截取
SUBSTRING(Name, 1, 2)     -- SQL Server:位置1起取2个字符(1-based)
SUBSTR(Name, 1, 2)        -- MySQL / Oracle

-- 去除空格
LTRIM(Name) / RTRIM(Name) / TRIM(Name)

-- 替换
REPLACE(Name, '张', '李')

-- 查找位置
CHARINDEX('张', Name)     -- SQL Server(返回1-based位置)
INSTR(Name, '张')         -- MySQL / Oracle
LOCATE('张', Name)        -- MySQL

-- 左右补位
LEFT(Name, 2) / RIGHT(Name, 2)

11.2 日期函数

sql 复制代码
GETDATE()                        -- SQL Server:当前日期时间
GETDATE()                        -- MySQL:NOW() / CURRENT_TIMESTAMP

-- 提取部分
YEAR(Birthday) / MONTH(Birthday) / DAY(Birthday)   -- SQL Server
DATEADD(YEAR, 1, Birthday)       -- 加 1 年
DATEDIFF(DAY, Birthday, GETDATE()) -- 相差天数

-- MySQL 等价
DATE_ADD(Birthday, INTERVAL 1 YEAR)
DATEDIFF(NOW(), Birthday)

-- 格式化
CONVERT(VARCHAR(10), GETDATE(), 120)   -- SQL Server:yyyy-mm-dd
DATE_FORMAT(Birthday, '%Y-%m-%d')      -- MySQL
TO_CHAR(Birthday, 'YYYY-MM-DD')        -- Oracle / PostgreSQL

十二、约束与完整性

12.1 约束类型

约束 说明
PRIMARY KEY 主键,唯一 + 非空
FOREIGN KEY 外键,参照完整性
UNIQUE 唯一(允许一个 NULL)
NOT NULL 非空
CHECK 检查约束
DEFAULT 默认值

12.2 示例

sql 复制代码
CREATE TABLE Orders (
    OrderID     INT PRIMARY KEY,          -- 主键
    StudentID   INT NOT NULL,
    Amount      DECIMAL(10,2) CHECK (Amount > 0),  -- 检查约束
    Status      NVARCHAR(20) DEFAULT '待处理',       -- 默认值
    CreateTime  DATETIME DEFAULT GETDATE(),
    -- 外键
    CONSTRAINT FK_Orders_Students
        FOREIGN KEY (StudentID) REFERENCES Students(StudentID),
    -- 唯一约束
    CONSTRAINT UQ_OrderNo UNIQUE (OrderID)
);

-- 表级主键 / 复合主键
CREATE TABLE Enrollment (
    StudentID INT,
    ClassID   INT,
    PRIMARY KEY (StudentID, ClassID)   -- 复合主键
);

12.3 给已有表加约束

sql 复制代码
ALTER TABLE Students ADD CONSTRAINT PK_Students PRIMARY KEY (StudentID);
ALTER TABLE Students ADD CONSTRAINT UQ_Email UNIQUE (Email);
ALTER TABLE Students ADD CONSTRAINT CK_Age CHECK (Age >= 0 AND Age <= 150);
ALTER TABLE Orders ADD CONSTRAINT FK_Orders_Students
    FOREIGN KEY (StudentID) REFERENCES Students(StudentID);

-- 删除约束
ALTER TABLE Orders DROP CONSTRAINT FK_Orders_Students;

十三、索引

索引用于加速查询,如同书的目录。

13.1 索引类型

类型 说明
聚集索引(Clustered) 每表只能一个,数据物理按索引顺序存储
非聚集索引(Non-clustered) 可多个,指向数据的指针
唯一索引(Unique) 值唯一
覆盖索引 / 组合索引 多列组成的索引

13.2 创建索引

sql 复制代码
-- 普通索引
CREATE INDEX idx_students_name ON Students(Name);

-- 唯一索引
CREATE UNIQUE INDEX idx_students_email ON Students(Email);

-- 组合索引
CREATE INDEX idx_students_name_age ON Students(Name, Age);

-- 删除索引
DROP INDEX idx_students_name ON Students;   -- SQL Server
DROP INDEX idx_students_name;               -- MySQL

13.3 注意事项

  • 经常出现在 WHEREJOINORDER BY 中的列适合建索引。
  • 索引过多会降低 INSERT/UPDATE/DELETE 性能(需维护索引)。
  • 区分度低的列(如 Gender)建索引收益小。
  • 遵循「最左前缀」原则使用组合索引。
  • 避免在索引列上使用函数或计算(WHERE YEAR(Birthday)=2004 无法走索引)。

十四、视图

视图(View)是虚拟表,封装查询逻辑,简化复杂查询、增强安全。

sql 复制代码
-- 创建视图
CREATE VIEW v_学生成绩 AS
SELECT s.StudentID, s.Name, s.Gender, s.Score,
       CASE WHEN s.Score >= 60 THEN '及格' ELSE '不及格' END AS 状态
FROM Students s;

-- 使用视图(如同查表)
SELECT * FROM v_学生成绩 WHERE Score > 80;

-- 修改视图
ALTER VIEW v_学生成绩 AS
SELECT StudentID, Name, Score FROM Students;

-- 删除视图
DROP VIEW v_学生成绩;

SQL Server 可对简单视图做 DML 操作;复杂视图(含 JOIN、聚合)默认不可更新。


十五、事务与锁

事务(Transaction)是原子性操作的集合,满足 ACID 特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

sql 复制代码
BEGIN TRANSACTION;                 -- 开始事务(SQL Server)
-- START TRANSACTION;              -- MySQL

    UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
    UPDATE Accounts SET Balance = Balance + 100 WHERE AccountID = 2;

    IF @@ERROR <> 0                -- 判断是否有错误(SQL Server)
        ROLLBACK TRANSACTION;      -- 回滚
    ELSE
        COMMIT TRANSACTION;        -- 提交

-- 保存点
BEGIN TRANSACTION;
    UPDATE ... ;
    SAVE TRANSACTION sp1;          -- 设置保存点
    UPDATE ... ;
    ROLLBACK TRANSACTION sp1;      -- 回滚到保存点
    COMMIT;

MySQL 使用 COMMIT / ROLLBACK,并可用 SET autocommit = 0 关闭自动提交。

隔离级别

级别 脏读 不可重复读 幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
sql 复制代码
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;  -- SQL Server / MySQL

十六、存储过程与函数

16.1 存储过程

存储过程是预编译的 SQL 代码块,可接收参数、返回结果。

sql 复制代码
-- 创建存储过程(SQL Server)
CREATE PROCEDURE usp_GetStudentsByAge
    @minAge TINYINT,
    @maxAge TINYINT
AS
BEGIN
    SELECT * FROM Students
    WHERE Age BETWEEN @minAge AND @maxAge;
END;

-- 调用
EXEC usp_GetStudentsByAge 18, 25;

-- 带输出参数
CREATE PROCEDURE usp_GetCount
    @gender CHAR(1),
    @total INT OUTPUT
AS
BEGIN
    SELECT @total = COUNT(*) FROM Students WHERE Gender = @gender;
END;

DECLARE @cnt INT;
EXEC usp_GetCount 'M', @cnt OUTPUT;
SELECT @cnt AS 男生人数;

-- 删除
DROP PROCEDURE usp_GetStudentsByAge;

MySQL 存储过程语法:

sql 复制代码
DELIMITER $$
CREATE PROCEDURE usp_GetByAge(IN minAge INT, IN maxAge INT)
BEGIN
    SELECT * FROM Students WHERE Age BETWEEN minAge AND maxAge;
END $$
DELIMITER ;

CALL usp_GetByAge(18, 25);

16.2 用户自定义函数(UDF)

sql 复制代码
-- 标量函数
CREATE FUNCTION fn_GetLevel(@score DECIMAL(5,2))
RETURNS NVARCHAR(10)
AS
BEGIN
    DECLARE @level NVARCHAR(10);
    SET @level = CASE
        WHEN @score >= 90 THEN '优秀'
        WHEN @score >= 60 THEN '及格'
        ELSE '不及格' END;
    RETURN @level;
END;

-- 调用(需带架构名)
SELECT Name, dbo.fn_GetLevel(Score) AS 等级 FROM Students;

-- 表值函数
CREATE FUNCTION fn_StudentsByGender(@gender CHAR(1))
RETURNS TABLE
AS
RETURN (SELECT * FROM Students WHERE Gender = @gender);

SELECT * FROM dbo.fn_StudentsByGender('M');

函数中不能有 INSERT/UPDATE/DELETE(修改数据的操作),存储过程中可以。


十七、触发器

触发器在表发生 INSERT/UPDATE/DELETE 时自动执行。SQL Server 用 inserted / deleted 虚拟表,MySQL 用 NEW / OLD

sql 复制代码
-- SQL Server:审计日志触发器
CREATE TABLE Students_Audit (
    AuditID INT IDENTITY PRIMARY KEY,
    StudentID INT,
    Action NVARCHAR(10),
    ActionTime DATETIME DEFAULT GETDATE()
);

CREATE TRIGGER trg_Students_Insert
ON Students
AFTER INSERT
AS
BEGIN
    INSERT INTO Students_Audit (StudentID, Action)
    SELECT StudentID, 'INSERT' FROM inserted;
END;

-- UPDATE 触发器
CREATE TRIGGER trg_Students_Update
ON Students
AFTER UPDATE
AS
BEGIN
    INSERT INTO Students_Audit (StudentID, Action)
    SELECT StudentID, 'UPDATE' FROM inserted;
END;

-- 删除触发器
DROP TRIGGER trg_Students_Insert;

MySQL 触发器:

sql 复制代码
CREATE TRIGGER trg_Students_insert
AFTER INSERT ON Students
FOR EACH ROW
BEGIN
    INSERT INTO audit_log VALUES (NEW.StudentID, 'INSERT', NOW());
END;

触发器会带来隐式副作用、难以调试,应谨慎使用,能用约束/应用层逻辑替代时优先替代。


十八、游标

游标用于逐行处理结果集(一般能用集合操作就不要用游标,性能差)。

sql 复制代码
-- SQL Server 游标
DECLARE @id INT, @name NVARCHAR(50);
DECLARE cur CURSOR FOR SELECT StudentID, Name FROM Students WHERE Age > 20;

OPEN cur;
FETCH NEXT FROM cur INTO @id, @name;
WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT '处理学生:' + CAST(@id AS VARCHAR) + ' ' + @name;
    FETCH NEXT FROM cur INTO @id, @name;
END;

CLOSE cur;
DEALLOCATE cur;

十九、动态 SQL

动态 SQL 在运行时拼接并执行 SQL 字符串,常用于参数可变的查询、动态建表等。

sql 复制代码
-- SQL Server:EXEC
DECLARE @sql NVARCHAR(MAX);
DECLARE @tableName NVARCHAR(50) = 'Students';
SET @sql = 'SELECT * FROM ' + @tableName;
EXEC(@sql);

-- SQL Server:sp_executesql(支持参数化,防注入)
DECLARE @sql NVARCHAR(MAX);
SET @sql = 'SELECT * FROM Students WHERE Age >= @age';
EXEC sp_executesql @sql, N'@age INT', @age = 20;

-- MySQL:PREPARE
SET @sql = 'SELECT * FROM Students WHERE Age >= ?';
PREPARE stmt FROM @sql;
SET @age = 20;
EXECUTE stmt USING @age;
DEALLOCATE PREPARE stmt;

务必防范 SQL 注入 :优先使用参数化查询(sp_executesql / 预编译语句),不要直接拼接用户输入。


二十、最佳实践与常见坑

20.1 最佳实践

  1. 禁止 SELECT *,明确列出所需列。

  2. UPDATE/DELETE 必须带 WHERE ,先 SELECT 验证再操作。

  3. 使用参数化查询,防范 SQL 注入。

  4. 合理使用索引,避免全表扫描。

  5. EXPLAIN / 执行计划分析慢查询

    sql 复制代码
    EXPLAIN SELECT * FROM Students WHERE Age > 20;   -- MySQL
    -- SQL Server:SET STATISTICS IO ON / 查看执行计划
  6. 事务尽量短小,避免长事务锁表。

  7. 命名规范:表名、列名语义化,统一大小写/下划线风格。

20.2 常见坑

  • NULL 陷阱WHERE Score = NULL 永远查不到,要用 IS NULLNULL 参与聚合会被忽略;COUNT(列) 不计 NULL,COUNT(*) 计。
  • = 简单比较忽略了 NULLNOT IN 子查询若含 NULL 可能返回空结果。
  • 隐式类型转换 :字符串比较数字可能导致索引失效(如 WHERE Phone = 13800138000 当 Phone 是字符串)。
  • BETWEEN 含边界BETWEEN 1 AND 10 包含 1 和 10。
  • OFFSET/FETCH 必须先 ORDER BY
  • GROUP BY 后不能用 WHERE 过滤聚合结果 ,要用 HAVING
  • LIKE '%xx%' 无法走索引(前导通配符)。
  • 自增列被 DELETE 不会回补TRUNCATE 会重置。
  • 表别名一旦使用,全程必须用别名SELECT s.Name FROM Students s WHERE Students.Age > 20 会报错。

附:SQL 关键字速查表

关键字 用途
SELECT 查询数据
FROM 指定来源表
WHERE 行级过滤
GROUP BY 分组
HAVING 分组过滤
ORDER BY 排序
LIMIT / TOP / OFFSET 限制/分页
JOIN 连接表
UNION 合并结果集
INSERT / UPDATE / DELETE 增删改
CREATE / ALTER / DROP 结构定义
DISTINCT 去重
AS 别名
CASE WHEN 条件表达式
BEGIN / COMMIT / ROLLBACK 事务控制
GRANT / REVOKE 权限

相关推荐
Logintern091 小时前
数据库分区表的索引实际上包含本地索引和全局索引两种类型
数据库·分区索引
阿沐沐,1 小时前
PowerShell 里改了 config.toml,Codex CLI 仍用旧值:先分清该改哪一层
java·服务器·数据库·人工智能·ai·ai编程
JacksonMx2 小时前
企业级幂等方案
数据库·spring boot·spring·oracle
Lucifer三思而后行2 小时前
我准备写 26 篇文章,带 DBA 系统学一遍 AI
数据库·人工智能·dba
for_ever_love__2 小时前
SQL学习: SQL入门与DDL
数据库·sql·学习
Irene19912 小时前
生产级探查脚本:数据探查 SQL 示例(无需DBA权限、跨Oracle版本(11g/19c)的表结构探查脚本方案)
oracle
Python私教12 小时前
从表格到管理系统:别先写页面,先补齐权限、流程和审计
数据库·后端·架构
陈皮波比茶12 小时前
Redis学习
数据库·redis·学习
—Miss. Z—13 小时前
备份与恢复
数据库·oracle