SQL Server 核心语法+进阶知识点大全(小白版)
前言
本文整理了SQL Server从基础语法 到高阶实战 的全量核心内容,涵盖数据查询、操作、表结构管理、聚合分组、多表联查、索引、事务、存储过程等,全程搭配易懂示例+注释,兼顾新手入门和日常开发查阅,适合快速上手并掌握实际开发所需的95%+语法。
一、基础查询(SELECT):核心数据检索
1. 基础语法
sql
SELECT [ALL | DISTINCT] 列名1, 列名2, ... -- ALL默认显示所有行,DISTINCT去重
FROM 表名
[WHERE 筛选条件] -- 过滤行数据
[ORDER BY 列名 [ASC | DESC]] -- 排序,ASC升序(默认),DESC降序
[TOP n [PERCENT]]; -- 限制返回行数,PERCENT表示百分比
2. 实战示例
sql
-- 1. 查询所有列(开发中尽量少用*,指定列更高效)
SELECT * FROM Student;
-- 2. 查询指定列,去重,按成绩降序取前10条
-- 标准正确写法(所有SQL Server版本都支持)
SELECT DISTINCT TOP 10 Name, Score
FROM Student
WHERE Age > 18
ORDER BY Score DESC;
-- 3. 取前20%的学生姓名
SELECT TOP 20 PERCENT Name FROM Student;
二、数据操作(DML):增删改表数据
1. 插入数据(INSERT)
sql
-- 格式1:指定列插入(推荐,表结构变更不影响)
INSERT INTO 表名 (列1, 列2, ...)
VALUES (值1, 值2, ...);
-- 格式2:批量插入(高效,减少数据库交互)
INSERT INTO 表名 (列1, 列2)
VALUES (值1, 值2), (值3, 值4), (值5, 值6);
-- 示例
INSERT INTO Student (Name, Age, Score)
VALUES ('张三', 20, 90), ('李四', 19, 85);
2. 更新数据(UPDATE)
sql
-- 语法:WHERE条件必加!否则更新全表数据
UPDATE 表名
SET 列1 = 值1, 列2 = 值2, ...
[WHERE 筛选条件];
-- 示例:更新张三的成绩为95
UPDATE Student
SET Score = 95
WHERE Name = '张三';
3. 删除数据(DELETE)
sql
-- 语法:WHERE条件必加!否则删除全表数据
DELETE FROM 表名
[WHERE 筛选条件];
-- 示例:删除年龄小于18的学生
DELETE FROM Student
WHERE Age < 18;
-- 清空表(自增列重置,速度远快于DELETE,不可回滚)
TRUNCATE TABLE Student;
核心区别 :DELETE可回滚、可加条件;TRUNCATE不可回滚、无条件,适合清空全表。
三、表结构操作(DDL):创建/修改/删除表
1. 创建表(CREATE TABLE)
sql
CREATE TABLE 表名 (
列名1 数据类型 [约束],
列名2 数据类型 [约束],
...
);
-- 示例:学生表(含主键、自增、非空、默认值约束)
CREATE TABLE Student (
Id INT IDENTITY(1,1) PRIMARY KEY, -- IDENTITY(起始值,步长):自增列,主键默认是聚集索引
Name VARCHAR(50) NOT NULL, -- 非空约束
Age INT CHECK (Age >= 0), -- 检查约束:年龄非负
Score DECIMAL(5,2), -- 小数:总位数5,小数位2(如99.99)
CreateTime DATETIME DEFAULT GETDATE(), -- 默认值:当前系统时间
ClassId INT -- 关联班级表,后续加外键
);
2. 修改表(ALTER TABLE)
sql
-- 1. 添加列
ALTER TABLE 表名 ADD 列名 数据类型 [约束];
ALTER TABLE Student ADD Gender CHAR(1) DEFAULT '男';
-- 2. 修改列(数据类型/长度)
ALTER TABLE 表名 ALTER COLUMN 列名 新数据类型;
ALTER TABLE Student ALTER COLUMN Name VARCHAR(100);
-- 3. 删除列
ALTER TABLE 表名 DROP COLUMN 列名;
ALTER TABLE Student DROP COLUMN Gender;
-- 4. 添加外键约束(关联两张表)
ALTER TABLE Student ADD CONSTRAINT FK_Student_Class
FOREIGN KEY (ClassId) REFERENCES Class(Id);
3. 删除表(DROP TABLE)
sql
-- IF EXISTS:SQL Server2016+支持,避免表不存在时报错
DROP TABLE [IF EXISTS] 表名;
DROP TABLE IF EXISTS Student;
四、聚合函数+分组(GROUP BY/HAVING):数据统计分析
1. 常用聚合函数
| 函数 | 作用 | 注意点 |
|---|---|---|
| COUNT(*) | 统计总行数 | 包含NULL值 |
| COUNT(列名) | 统计指定列非NULL值行数 | 排除NULL值 |
| SUM(列名) | 求指定列数值和 | 仅支持数值类型 |
| AVG(列名) | 求指定列平均值 | 仅支持数值类型,排除NULL |
| MAX(列名) | 求指定列最大值 | 支持数值/日期/字符串 |
| MIN(列名) | 求指定列最小值 | 支持数值/日期/字符串 |
2. 语法与示例
sql
-- 格式:WHERE筛选分组前数据,HAVING筛选分组后数据
SELECT 分组列, 聚合函数(列名) AS 别名
FROM 表名
[WHERE 分组前条件]
GROUP BY 分组列 -- 分组列必须与SELECT非聚合列一致
[HAVING 聚合函数条件];
-- 示例:按年龄分组,统计及格(≥60)学生数,且每组人数>2
SELECT Age, COUNT(*) AS StudentCount
FROM Student
WHERE Score >= 60
GROUP BY Age
HAVING COUNT(*) > 2;
五、多表联查(JOIN):基础→高阶实战
1. 基础连接类型(2表联查)
核心 :通过关联列(如主键/外键)拼接多张表的数,结果集按匹配规则返回。
| 连接类型 | 通俗解释 | 结果集 |
|---|---|---|
| INNER JOIN | 只找「两边都有」的数据 | 仅返回两表匹配的行 |
| LEFT JOIN | 左表全要,右表补NULL | 左表所有行 + 右表匹配行(无则NULL) |
| RIGHT JOIN | 右表全要,左表补NULL | 右表所有行 + 左表匹配行(无则NULL) |
| FULL JOIN | 两边数据全要 | 两表所有行(无匹配则补NULL) |
2. 2表联查示例
先创建关联表用于测试:
sql
-- 班级表
CREATE TABLE Class (
Id INT IDENTITY(1,1) PRIMARY KEY,
ClassName VARCHAR(50) NOT NULL,
Teacher VARCHAR(50)
);
-- 成绩表
CREATE TABLE Score (
Id INT IDENTITY(1,1) PRIMARY KEY,
StudentId INT FOREIGN KEY REFERENCES Student(Id),
Subject VARCHAR(20) NOT NULL,
Score DECIMAL(5,2) NOT NULL
);
联查实战:
sql
-- 1. INNER JOIN:查询有成绩的学生姓名+科目+成绩(仅返回两边匹配数据)
SELECT s.Name, sc.Subject, sc.Score
FROM Student s -- 表别名:简化代码
INNER JOIN Score sc ON s.Id = sc.StudentId; -- ON:关联条件
-- 2. LEFT JOIN:查询所有学生+成绩(无成绩的学生也显示,成绩列补NULL)
SELECT s.Name, sc.Subject, sc.Score
FROM Student s
LEFT JOIN Score sc ON s.Id = sc.StudentId;
-- 3. RIGHT JOIN:查询所有成绩+对应学生(无学生的成绩也显示,学生列补NULL)
SELECT s.Name, sc.Subject, sc.Score
FROM Student s
RIGHT JOIN Score sc ON s.Id = sc.StudentId;
3. 高阶:3表及以上联查(开发高频场景)
核心逻辑 :先联查2张表,再将结果与第3张表联查,关联条件依次叠加。
sql
-- 示例:查询「学生姓名+所属班级+科目+成绩」(3表联查:Student+Class+Score)
SELECT s.Name, c.ClassName, sc.Subject, sc.Score
FROM Student s
-- 第一步:学生表联班级表(通过ClassId)
LEFT JOIN Class c ON s.ClassId = c.Id
-- 第二步:结果联成绩表(通过StudentId)
LEFT JOIN Score sc ON s.Id = sc.StudentId
-- 额外筛选:只看数学成绩
WHERE sc.Subject = '数学';
六、子查询:嵌套查询的核心用法
1. 什么是子查询
将一个查询结果 作为另一个查询的条件/数据源/列值 ,用()包裹,适用于复杂的分步查询,小白可理解为「先算内层,再算外层」。
2. 常用场景示例
sql
-- 场景1:子查询作为WHERE条件(IN/EXISTS/比较运算符)
-- 示例:查询成绩大于数学平均分的学生姓名
SELECT Name
FROM Student
WHERE Id IN (
-- 内层:查数学成绩大于平均分的StudentId
SELECT StudentId
FROM Score
WHERE Subject = '数学' AND Score > (
-- 内层的内层:查数学平均分
SELECT AVG(Score) FROM Score WHERE Subject = '数学'
)
);
-- 场景2:子查询作为数据源(派生表,需加别名)
-- 示例:查询各年龄组的学生数,且仅显示人数>1的组
SELECT t.Age, t.StudentCount
FROM (
-- 内层:按年龄分组统计人数,作为派生表t
SELECT Age, COUNT(*) AS StudentCount
FROM Student
GROUP BY Age
) AS t
WHERE t.StudentCount > 1;
-- 场景3:子查询作为列值
-- 示例:查询学生姓名+最新一次的数学成绩
SELECT
Name,
-- 内层:查该学生的最新数学成绩(按成绩降序取1条)
(SELECT TOP 1 Score FROM Score WHERE StudentId = s.Id AND Subject = '数学' ORDER BY Score DESC) AS LatestMathScore
FROM Student s;
七、常用内置函数:字符串/日期/数值(开发高频)
1. 字符串函数
sql
SELECT
CONCAT('姓名:', Name) AS 拼接结果, -- 字符串拼接
SUBSTRING(Name, 1, 2) AS 截取前2位, -- 截取(起始位置从1开始)
REPLACE(Name, '张', '章') AS 替换结果, -- 字符串替换
LEN(Name) AS 字符串长度, -- 获取长度
UPPER(Name) AS 转大写, -- 转大写
LOWER(Name) AS 转小写; -- 转小写
FROM Student;
2. 日期函数
sql
SELECT
GETDATE() AS 当前时间, -- 含日期+时间(常用)
SYSDATETIME() AS 高精度时间, -- 比GETDATE()精度高
FORMAT(GETDATE(), 'yyyy-MM-dd HH:mm:ss') AS 格式化时间, -- 自定义格式
DATEADD(DAY, 7, GETDATE()) AS 7天后, -- 日期加减(DAY可换MONTH/YEAR/HOUR)
DATEDIFF(DAY, '2026-01-01', GETDATE()) AS 天数差, -- 计算两个日期的差值
YEAR(GETDATE()) AS 年份, -- 提取年份
MONTH(GETDATE()) AS 月份, -- 提取月份
DAY(GETDATE()) AS 日期; -- 提取日期
3. 数值函数
sql
SELECT
ROUND(89.63, 1) AS 四舍五入, -- 保留1位小数
FLOOR(89.9) AS 向下取整, -- 结果89
CEILING(89.1) AS 向上取整, -- 结果90
ABS(-89) AS 绝对值, -- 结果89
SQRT(16) AS 平方根; -- 结果4
FROM Score;
八、索引:给查询装「加速器」(进阶核心)
1. 什么是索引(小白版)
索引就像书籍的目录:
- 无索引:查询时逐行扫描全表(翻遍整本书),数据量越大越慢;
- 有索引:通过索引直接定位数据位置(按目录找页码),查询速度提升百倍。
2. 索引的分类(常用2种)
| 索引类型 | 核心特点 | 数量限制 | 适用场景 |
|---|---|---|---|
| 聚集索引(Clustered) | 数据物理存储顺序与索引顺序一致,主键默认创建 | 表中仅1个 | 主键列(如Id) |
| 非聚集索引(Nonclustered) | 索引单独存储,记录指向数据行的指针 | 表中最多999个 | WHERE条件、JOIN关联、ORDER BY列 |
3. 索引的创建/删除/查看
sql
-- 1. 创建非聚集索引(默认NONCLUSTERED,可省略)
-- 格式1:单列索引
CREATE [NONCLUSTERED] INDEX 索引名
ON 表名 (列名 [ASC/DESC]);
-- 格式2:联合索引(多列,按查询频率排序)
CREATE INDEX 索引名
ON 表名 (列名1, 列名2);
-- 格式3:唯一索引(保证列值无重复,比普通索引查询更快)
CREATE UNIQUE INDEX 索引名
ON 表名 (列名);
-- 示例1:给Score表的StudentId+Subject建联合索引(开发高频)
CREATE INDEX IX_Score_StudentId_Subject
ON Score (StudentId, Subject);
-- 示例2:给Student表的IdCard建唯一索引(身份证号唯一)
CREATE UNIQUE INDEX IX_Student_IdCard
ON Student (IdCard);
-- 2. 删除索引
DROP INDEX 索引名 ON 表名;
DROP INDEX IX_Score_StudentId_Subject ON Score;
-- 3. 查看表的所有索引(系统存储过程)
EXEC sp_helpindex 'Student';
4. 索引使用「黄金法则」(小白必看,避坑关键)
✅ 该建索引的场景:
- 频繁作为WHERE条件的列(如用户ID、订单号);
- 用于JOIN关联的列(如StudentId、ClassId);
- 用于ORDER BY/GROUP BY的列;
- 数据量超1000行的表(小表建索引无意义)。
❌ 不该建索引的场景:
- 数据量极少的表(如配置表,几十行);
- 频繁插入/更新/删除的列(如日志表的创建时间,维护索引耗时);
- 重复值极高的列(如性别列,只有男/女,索引失效);
- 很少被查询的列。
九、事务(TRANSACTION):保证数据一致性
1. 什么是事务
保证多步数据库操作 的原子性 :要么全部成功 ,要么全部失败,适用于转账、订单创建等需要多步操作的场景(比如转账:扣减A账户→增加B账户,两步必须同时成功)。
2. 事务的核心属性(ACID)
- 原子性(Atomic):操作要么全做,要么全不做;
- 一致性(Consistent):操作前后数据总量不变(如转账总金额不变);
- 隔离性(Isolated):多个事务互不干扰;
- 持久性(Durable):事务提交后,数据永久保存。
3. 事务实战语法(TRY/CATCH版,开发推荐)
sql
BEGIN TRANSACTION; -- 开始事务
BEGIN TRY
-- 执行多步操作(示例:转账,扣减ID1,增加ID2)
UPDATE Account SET Balance = Balance - 100 WHERE Id = 1;
UPDATE Account SET Balance = Balance + 100 WHERE Id = 2;
COMMIT TRANSACTION; -- 提交事务:所有操作生效
PRINT '操作成功,事务已提交';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION; -- 回滚事务:撤销所有操作
PRINT '操作失败,事务已回滚';
-- 可选:打印错误信息
PRINT '错误信息:' + ERROR_MESSAGE();
END CATCH;
十、存储过程:封装SQL逻辑(可重复调用)
1. 什么是存储过程
将一段常用的SQL逻辑 封装成一个「函数」,可通过调用名 重复执行,支持参数传入/返回,提升开发效率和代码复用性,是数据库开发的核心知识点。
2. 存储过程的创建/调用/删除
sql
-- 1. 创建存储过程(带输入参数)
CREATE PROCEDURE 存储过程名
@参数1 数据类型, -- 输入参数
@参数2 数据类型 = 默认值 -- 带默认值的参数
AS
BEGIN
SET NOCOUNT ON; -- 关闭「受影响的行数」提示,可选
-- 封装的SQL逻辑
SELECT * FROM Student WHERE Age BETWEEN @参数1 AND @参数2;
END;
-- 示例:创建查询指定年龄范围学生的存储过程
CREATE PROCEDURE GetStudentByAge
@MinAge INT = 18, -- 默认最小年龄18
@MaxAge INT = 30 -- 默认最大年龄30
AS
BEGIN
SET NOCOUNT ON;
SELECT Id, Name, Age, Score FROM Student WHERE Age BETWEEN @MinAge AND @MaxAge;
END;
-- 2. 调用存储过程
-- 方式1:按参数顺序调用
EXEC GetStudentByAge 18, 25;
-- 方式2:按参数名调用(推荐,参数顺序变更不影响)
EXEC GetStudentByAge @MinAge = 18, @MaxAge = 25;
-- 方式3:使用默认参数(只传部分)
EXEC GetStudentByAge @MaxAge = 22;
-- 3. 修改存储过程
ALTER PROCEDURE GetStudentByAge
@MinAge INT = 18,
@MaxAge INT = 30
AS
BEGIN
SET NOCOUNT ON;
-- 新增筛选:及格学生
SELECT Id, Name, Age, Score FROM Student WHERE Age BETWEEN @MinAge AND @MaxAge AND Score >= 60;
END;
-- 4. 删除存储过程
DROP PROCEDURE [IF EXISTS] 存储过程名;
DROP PROCEDURE IF EXISTS GetStudentByAge;
十一、核心总结(小白速记)
1. 基础必掌握
- 查:
SELECT + WHERE + ORDER BY + TOP; - 改:
INSERT/UPDATE/DELETE(UPDATE/DELETE必加WHERE); - 建表:
CREATE TABLE+ 常用约束(主键、非空、默认值、外键)。
2. 进阶必掌握
- 多表联查:
LEFT JOIN(开发最高频)+ 3表联查逻辑; - 统计:聚合函数 +
GROUP BY + HAVING(区分WHERE和HAVING的筛选时机); - 优化:索引(联合索引+唯一索引)+ 事务(保证数据一致性)。
3. 开发必掌握
- 复用:存储过程(封装常用逻辑,带参数);
- 函数:字符串/日期/数值内置函数(提升开发效率);
- 子查询:嵌套查询(分步解决复杂业务问题)。
4. 避坑关键
UPDATE/DELETE不加WHERE会操作全表,开发前可先加SELECT验证条件;- 索引不是越多越好,频繁增删的表少建索引;
- 多表联查优先用
LEFT JOIN,避免笛卡尔积(无关联条件导致数据爆炸); - 大表查询避免用
SELECT *,指定所需列减少IO操作。