简介:这是一份面向数据库初学者与SQL Server入门者的系统性学习笔记,聚焦关系型数据库核心概念与SQL Server实操要点,帮助读者快速掌握数据库管理、对象操作及权限控制等关键能力。资源为单个Word文档(.doc),大小499KB,内容结构清晰,覆盖数据库创建与管理、表/索引/触发器/存储过程等对象的语法与限制、C/S架构与编程接口支持、系统数据库功能解析、文件存储机制(.mdf/.ndf/.ldf)、关系模型与数据完整性约束(主键、外键、check、unique等)以及授权体系(登录→用户→角色)等完整知识链。笔记结合概念阐释与典型SQL语句示例,如CREATE/DROP/ALTER、INSERT/UPDATE/DELETE、GRANT/REVOKE等,并对实体-属性-码、元组-属性-主码等理论模型给出通俗说明,兼顾理论基础与工程实践。目前已有440人学习下载,适合作为自学提纲、课堂补充或考前速查参考。
1. SQL Server 学习笔记:不是抄命令,而是把数据库从“黑匣子”变成你手里的扳手
很多人打开 SQL Server Management Studio(SSMS),输完SELECT * FROM Users就以为自己会了——结果一到生产环境就卡在登录失败、连接超时、权限报错、备份还原失败、慢查询查不出原因。这不是学得不够多,而是没建立起「SQL Server 的运行逻辑链」:实例怎么启动、服务账户凭什么能读写磁盘、登录名和数据库用户怎么映射、T-SQL 执行计划里那堆嵌套箭头到底在说什么、为什么加个索引反而让查询更慢……
这篇笔记不按官方文档顺序罗列功能,而是按一个一线 DBA/后端工程师真实上手路径来组织:从 Windows 上装好第一个可连实例开始,到能独立排查Login failed for user 'sa'、Cannot open database requested by the login、The target principal name is incorrect这三类高频报错;能手动建库、设备份策略、写带事务的存储过程、看懂 Execution Plan 中的 Key Lookup 和 Nested Loops;最后落到日常最痛的「慢 SQL 优化」——不是靠SET STATISTICS IO ON看几行数字,而是用sys.dm_exec_query_stats+sys.dm_exec_sql_text定位真实拖垮系统的语句,再用CREATE INDEX+INCLUDE+WHERE三步闭环落地。适合刚转岗 DBA 的开发、需要直连 SQL Server 做数据服务的后端、或正在准备微软认证(如 DP-300)的备考者。
2. 本地环境搭建:用 SQL Server 2022 Developer 版跑通最小可用实例
SQL Server 不是装完就能用。它依赖 Windows 服务、本地组策略、TCP/IP 协议栈、SQL Server Browser 服务、以及最关键的——实例名与端口的绑定关系。很多初学者卡在“SSMS 连不上 localhost”,其实根本没意识到自己装的是命名实例(如MSSQLSERVER或SQLEXPRESS),而默认连接字符串里写的localhost实际指向的是默认实例(仅当实例名为MSSQLSERVER时才可省略)。本节带你用最简路径绕过所有安装陷阱,直接获得一个可远程连接、可执行 T-SQL、可配置备份的本地实例。
2.1 下载与静默安装:避开 UI 向导的权限陷阱
SQL Server 2022 Developer 免费版(非 Express)是学习首选:功能完整、无 10GB 数据库大小限制、支持 Always On、列存储、内存优化表。官网下载地址需搜索 “Microsoft SQL Server 2022 Developer download”,注意区分x64与ARM64(Windows 11 ARM 设备需单独选型)。
安装时必须以管理员身份运行 setup.exe,否则服务账户注册失败。推荐使用静默安装(避免 UI 向导跳过关键配置),命令如下:
setup.exe /Q /ACTION=Install /INSTANCENAME="MSSQL2022" /FEATURES=SQLEngine,Replication,FullText /UPDATEENABLED=FALSE /SQLSVCACCOUNT="NT Service\MSSQL$MSSQL2022" /SQLSVCPASSWORD="" /SQLSYSADMINACCOUNTS="BUILTIN\Administrators" /AGTSVCACCOUNT="NT Service\SQLAgent$MSSQL2022" /IACCEPTSQLSERVERLICENSETERMS说明:
/INSTANCENAME="MSSQL2022"显式指定命名实例名,避免默认实例冲突;/SQLSVCACCOUNT="NT Service\MSSQL$MSSQL2022"使用内置虚拟账户(比 LocalSystem 更安全,且无需手动配置磁盘权限);/SQLSYSADMINACCOUNTS="BUILTIN\Administrators"将本机管理员组设为 sysadmin,省去后续手动授权;/UPDATEENABLED=FALSE关闭自动更新,防止安装中途弹窗中断流程;- 静默安装日志默认存于
C:\Program Files\Microsoft SQL Server\160\Setup Bootstrap\Log\,失败时优先查Summary.txt。
安装完成后,不要急着打开 SSMS。先验证 Windows 服务是否启动:
Get-Service | Where-Object {$_.DisplayName -like "*SQL*2022*"} | Select-Object Name, Status, DisplayName应看到SQL Server (MSSQL2022)状态为Running。若为Stopped,右键服务 → “属性” → “登录”选项卡 → 确认“此账户”为NT Service\MSSQL$MSSQL2022,再点击“启动”。
2.2 连接字符串与 SSMS 配置:解决 90% 的“连不上”问题
SSMS 默认连接字符串是localhost,但你的实例叫MSSQL2022,正确写法是:
localhost\MSSQL2022或更明确的 TCP 方式(便于后续远程调试):
127.0.0.1,1433但注意:命名实例默认不监听 1433 端口,而是动态端口(如 51234)。要固定端口,必须启用 TCP/IP 协议并手动设置:
- 打开
SQL Server Configuration Manager→ 左侧展开SQL Server Network Configuration→ 点击Protocols for MSSQL2022; - 右键
TCP/IP→ “启用”; - 右键
TCP/IP→ “属性” → 切换到IP Addresses选项卡; - 拉到底部
IPAll区域,清空TCP Dynamic Ports,在TCP Port输入1433; - 重启
SQL Server (MSSQL2022)服务。
为什么必须设 1433?
很多应用(如 .NET Core 的SqlConnection、Python 的pyodbc)默认只尝试 1433。若用动态端口,每次重启实例端口都变,连接字符串就得同步改——这在自动化脚本里是灾难。固定端口是生产环境铁律,学习阶段就该养成。
验证连接:SSMS 新建查询 → 服务器名称填localhost\MSSQL2022→ 认证选Windows 身份验证→ 点“连接”。成功后,执行:
SELECT @@VERSION AS Version, @@SERVERNAME AS ServerName, SERVERPROPERTY('InstanceName') AS InstanceName;应返回类似:
Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64) ... DESKTOP-ABC123 MSSQL2022至此,最小可用实例跑通。下一步不是建表,而是先加固——因为接下来你要用sa登录,而默认sa是禁用状态。
3. 身份验证与权限体系:搞懂登录名、用户、角色三层映射
SQL Server 权限模型常被简化为“用户名密码”,实际是三层嵌套:登录名(Login)→ 用户(User)→ 角色(Role)。sa是登录名,但它在每个数据库里必须显式映射为用户,再被加入db_owner角色,才能操作该库。很多初学者执行ALTER LOGIN sa ENABLE后仍报Cannot open database "xxx",就是因为漏了数据库级映射。本节用真实命令串讲清每层作用,并给出安全底线配置。
3.1 启用 sa 并设强密码:绕过 Windows 身份验证的刚需场景
混合模式(SQL Server + Windows 身份验证)是开发测试必备,尤其当你需要从 Linux 机器(如 WSL2)、Python 脚本、或 Java 应用连接时。启用步骤:
-- 1. 切换到 master 数据库(必须) USE master; GO -- 2. 启用 sa 登录名 ALTER LOGIN sa ENABLE; GO -- 3. 为 sa 设置强密码(至少 8 位,含大小写字母+数字+符号) ALTER LOGIN sa WITH PASSWORD = 'Sql2022!SecurePass#123'; GO -- 4. 强制下次登录必须改密码(可选,增强安全性) ALTER LOGIN sa WITH MUST_CHANGE; GO参数说明:
MUST_CHANGE会让首次用sa登录时强制弹出改密窗口,适合团队共享环境;- 密码策略受 Windows 密码策略影响,若本地组策略启用了“密码必须符合复杂性要求”,则上述密码必须满足(否则报错
Password validation failed);- 执行后需重启 SQL Server 服务(或执行
SHUTDOWN WITH NOWAIT再手动启),否则部分连接仍可能拒绝sa。
验证:SSMS 新建连接 → 服务器名称localhost\MSSQL2022→ 认证选SQL Server 身份验证→ 登录名sa→ 密码填刚设的值 → 连接。成功后,执行:
SELECT SUSER_NAME() AS LoginName, USER_NAME() AS UserName, IS_SRVROLEMEMBER('sysadmin') AS IsSysAdmin;应返回sa,dbo,1(即sa是 sysadmin 角色成员)。
3.2 创建应用专用登录名:告别 sa,建立最小权限原则
sa是上帝账号,绝不该用于应用连接。创建专用登录名并授予权限的标准流程:
-- 1. 在 master 中创建登录名(范围:整个实例) CREATE LOGIN AppUser WITH PASSWORD = 'AppPass@2022!', DEFAULT_DATABASE = master, CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF; GO -- 2. 切换到目标数据库(如新建的 TestDB) USE TestDB; GO -- 3. 在当前数据库中创建用户(范围:仅 TestDB) CREATE USER AppUser FOR LOGIN AppUser; GO -- 4. 授予 db_datareader + db_datawriter(读写表,但不能建表/删库) ALTER ROLE db_datareader ADD MEMBER AppUser; ALTER ROLE db_datawriter ADD MEMBER AppUser; GO -- 5. 若需执行存储过程,额外授予 EXECUTE 权限 GRANT EXECUTE TO AppUser; GO关键逻辑:
CREATE LOGIN在master中执行,定义谁可以连进来;CREATE USER在具体数据库中执行,定义这个人在该库能做什么;db_datareader/db_datawriter是数据库角色,比逐条GRANT SELECT ON table更易维护;CHECK_POLICY = OFF关闭 Windows 密码策略(避免因本地策略导致密码设不上去),生产环境应设为ON并配合规密码。
此时,应用连接字符串中的User ID=AppUser;Password=AppPass@2022!即可访问TestDB,但无法访问master或model,也无法执行DROP DATABASE—— 这就是最小权限落地。
4. 避坑:登录失败、连接超时、SSL 加密报错的 5 类真实翻车现场
SQL Server 学习路上,80% 的时间花在解决连接类报错。这些错误看似随机,实则有固定根因。以下是我过去三年处理过的 5 类高频问题,按「现象 → 原因 → 解决」结构整理,全部来自真实工单(非模拟)。
4.1 现象:Login failed for user 'sa'. (Microsoft SQL Server, Error: 18456)
原因:错误代码18456后面的“状态码”才是关键。常见状态码:
State 1:用户不存在(拼错sa);State 5:用户存在但密码错误(大小写敏感,或复制粘贴带空格);State 8:密码错误(最常见,但需结合日志确认);State 9:密码已过期(CHECK_EXPIRATION = ON且未改密);State 11 or 12:用户已锁定(多次输错触发账户锁定)。
解决:
- 查 Windows 事件查看器 → Windows 日志 → 应用程序 → 筛选来源
MSSQLSERVER,找到对应时间戳的Error 18456事件,末尾有State: X; - 若
State=8,重置密码:ALTER LOGIN sa WITH PASSWORD = 'NewPass123!'; - 若
State=11/12,解锁:ALTER LOGIN sa WITH PASSWORD = 'NewPass123!' UNLOCK; - 永远不要用记事本存密码——它可能插入不可见 Unicode 字符(如
U+200E左向控制符),导致粘贴后密码无效。
4.2 现象:A network-related or instance-specific error occurred while establishing a connection...
原因:TCP/IP 协议未启用,或 SQL Server Browser 服务未启动(命名实例必需),或防火墙拦截 1433 端口。
解决:
- 确认
SQL Server (MSSQL2022)和SQL Server Browser两个服务均Running; - 在
SQL Server Configuration Manager中启用TCP/IP并设固定端口(见 2.2 节); - Windows 防火墙放行:
New-NetFirewallRule -DisplayName "SQL Server Port 1433" -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow; - 测试端口连通性:
Test-NetConnection localhost -Port 1433(PowerShell),返回TcpTestSucceeded : True即通。
4.3 现象:The target principal name is incorrect
原因:客户端尝试 Kerberos 认证,但 SQL Server 实例的 SPN(Service Principal Name)未注册,或注册错误。常见于域环境,或用localhost连接但 SPN 绑定的是机器全名。
解决:
- 查当前 SPN:
setspn -L MSSQLSvc/DESKTOP-ABC123:1433(替换为你机器名); - 若无输出,注册 SPN:
setspn -S MSSQLSvc/DESKTOP-ABC123:1433 DOMAIN\SQLServiceAccount(SQLServiceAccount是 SQL Server 服务账户,如NT Service\MSSQL$MSSQL2022); - 简单绕过法:SSMS 连接 → “选项” → “连接属性” → 勾选“连接到数据库引擎” → 在“数据库名称”填
master,可强制走 NTLM 而非 Kerberos。
4.4 现象:Driver cannot establish a secure SSL connection
原因:JDBC/ODBC 驱动强制要求加密,但 SQL Server 未配置证书,或客户端未信任服务器证书。
解决:
- 临时关闭加密(仅测试用):连接字符串加
encrypt=false;trustServerCertificate=true; - 生产环境正解:在 SQL Server 中启用证书(需企业版),或使用
sqlcmd测试:sqlcmd -S localhost\MSSQL2022 -U sa -P 'pwd' -Q "SELECT 1"—— 若sqlcmd能连,证明是驱动层 SSL 配置问题,非 SQL Server 本身故障。
4.5 现象:Login failed: token exchange failed: error sending request for url
原因:这是 Azure AD 认证报错,但本地 SQL Server 未启用 Azure AD 集成。用户误在连接字符串中加了Authentication=Active Directory Password,而实例是纯本地部署。
解决:
- 删除连接字符串中所有
Authentication=参数; - 确认 SQL Server 配置中未启用 Azure AD(
SELECT * FROM sys.dm_exec_connections WHERE auth_scheme = 'KERBEROS' OR auth_scheme = 'NTLM',不应出现AzureAD); - 若真需 Azure AD,必须用 SQL Server 2022 Enterprise + Azure AD DS 集成,学习阶段完全不需要。
5. T-SQL 实战:从建库到慢查询定位,一条命令一个目的
学 SQL Server,最终要落在写 T-SQL 上。但新手常陷入两个误区:一是死背语法(如INSERT INTO ... SELECT有几种写法),二是盲目优化(看到SELECT *就加索引)。本节聚焦三个真实高频场景:建库建表的最小安全模板、事务与错误处理的健壮写法、慢查询的精准定位链。每段代码都带生产环境验证过的注释和参数说明。
5.1 创建数据库:带文件组、初始大小、自动增长的防翻车模板
-- 创建数据库,显式指定数据文件和日志文件路径、大小、增长方式 CREATE DATABASE SalesDB ON PRIMARY ( NAME = N'SalesDB_Data', FILENAME = N'D:\SQLData\SalesDB.mdf', -- 建议 SSD 盘,勿放 C:\Program Files\ SIZE = 100MB, -- 初始大小,避免频繁自动增长 MAXSIZE = UNLIMITED, -- 生产环境建议设上限,如 500GB FILEGROWTH = 50MB -- 每次增长 50MB,而非默认 10%,防碎片 ) LOG ON ( NAME = N'SalesDB_Log', FILENAME = N'D:\SQLLog\SalesDB.ldf', -- 日志文件务必与数据文件分盘! SIZE = 20MB, MAXSIZE = 200GB, FILEGROWTH = 10MB ); GO -- 设置恢复模式为 FULL(支持时间点还原) ALTER DATABASE SalesDB SET RECOVERY FULL; GO -- 创建用户并授予权限(复用 3.2 节逻辑) USE SalesDB; GO CREATE USER AppUser FOR LOGIN AppUser; ALTER ROLE db_datareader ADD MEMBER AppUser; ALTER ROLE db_datawriter ADD MEMBER AppUser; GO为什么这样设?
FILEGROWTH设绝对值(如50MB)而非百分比(如10%),避免大库增长时一次扩几百 MB,引发 I/O 阻塞;- 数据文件与日志文件必须分物理磁盘,否则日志写入会与数据读写争抢磁盘队列;
RECOVERY FULL是生产标配,SIMPLE模式下无法做事务日志备份,意味着只能还原到最近完整备份,丢失所有中间事务。
5.2 带事务与错误捕获的存储过程:避免部分更新导致数据不一致
-- 创建订单插入存储过程,包含事务、错误捕获、回滚 CREATE PROCEDURE InsertOrder @CustomerID INT, @OrderDate DATETIME2 = NULL, @TotalAmount DECIMAL(18,2) AS BEGIN SET NOCOUNT ON; -- 关闭影响行数消息,提升性能 -- 初始化变量 DECLARE @TranCount INT = @@TRANCOUNT; BEGIN TRY -- 若外部已有事务,则不开启新事务 IF @TranCount = 0 BEGIN TRANSACTION; -- 插入主表 INSERT INTO Orders (CustomerID, OrderDate, TotalAmount) VALUES (@CustomerID, ISNULL(@OrderDate, GETDATE()), @TotalAmount); DECLARE @OrderID INT = SCOPE_IDENTITY(); -- 获取刚插入的 OrderID -- 插入明细表(示例:假设有 OrderDetails 表) -- INSERT INTO OrderDetails (OrderID, ProductID, Quantity) VALUES (@OrderID, 101, 2); -- 若外部无事务,则提交 IF @TranCount = 0 COMMIT TRANSACTION; END TRY BEGIN CATCH -- 发生错误时,仅回滚内部开启的事务 IF @TranCount = 0 AND XACT_STATE() <> 0 ROLLBACK TRANSACTION; -- 抛出详细错误信息(含行号、错误号) DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE(); DECLARE @ErrorSeverity INT = ERROR_SEVERITY(); DECLARE @ErrorState INT = ERROR_STATE(); DECLARE @ErrorLine INT = ERROR_LINE(); RAISERROR ('InsertOrder failed at line %d: %s', @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorMessage); END CATCH END GO关键设计点:
@@TRANCOUNT检查外部事务,避免嵌套事务COMMIT导致提前提交;XACT_STATE()判断事务是否可提交(1=可提交,-1=必须回滚,0=无事务);RAISERROR带参数化消息,让调用方能精准定位错误位置;SET NOCOUNT ON减少网络传输量,对高并发场景至关重要。
5.3 慢查询定位:不用 SSMS 图形化,用 DMV 精准抓出 TOP 5 拖垮系统的语句
-- 查询最近 1 小时内 CPU 消耗最高的 5 条语句(含执行计划、等待类型、IO 统计) SELECT TOP 5 qs.execution_count, qs.total_logical_reads / qs.execution_count AS avg_logical_reads, qs.total_elapsed_time / qs.execution_count AS avg_elapsed_time_ms, qs.total_worker_time / qs.execution_count AS avg_cpu_time_ms, qs.total_logical_reads, qs.total_elapsed_time, qs.total_worker_time, SUBSTRING(st.text, (qs.statement_start_offset/2) + 1, ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset END - qs.statement_start_offset)/2) + 1) AS statement_text, qp.query_plan FROM sys.dm_exec_query_stats AS qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp WHERE qs.last_execution_time > DATEADD(HOUR, -1, GETDATE()) ORDER BY qs.total_worker_time DESC;执行后你会看到什么?
avg_logical_reads > 10000:说明该语句频繁读取数据页,大概率缺索引;avg_elapsed_time_ms >> avg_cpu_time_ms:说明语句在等资源(如锁、IO),而非计算瓶颈;statement_text是实际执行的 SQL 片段(非完整存储过程),可直接复制优化;query_plan列点击可查看 XML 执行计划,重点找<RelOp NodeId="1" PhysicalOp="Clustered Index Scan"—— 全表扫描是索引缺失的铁证。
优化闭环:
- 复制
statement_text中的WHERE条件字段(如WHERE Status = 'Pending' AND CreatedDate > '2023-01-01'); - 在对应表上建覆盖索引:
CREATE NONCLUSTERED INDEX IX_Orders_Status_CreatedDate ON Orders(Status, CreatedDate) INCLUDE (OrderID, TotalAmount);; - 清空缓存(仅测试):
DBCC FREEPROCCACHE;,再执行原语句,对比avg_logical_reads是否下降 90%+。
6. 慢 SQL 优化实战:从执行计划读懂“为什么慢”,而不是“怎么加索引”
很多人学优化,止步于“看执行计划 → 找红色感叹号 → 加索引”。但真实世界里,90% 的慢查询问题不在索引,而在查询写法本身违背 SQL Server 的优化器假设。比如OR条件让索引失效、SELECT *强制回表、NOT IN触发全表扫描、参数嗅探导致计划复用错误。本节用一个真实电商订单查询为例,带你拆解执行计划的每一层含义,并给出可落地的改写方案。
6.1 场景还原:一个看似合理的查询,为何在 100 万订单表上跑 12 秒?
原始语句(来自某电商平台订单导出功能):
SELECT o.OrderID, o.CustomerID, o.TotalAmount, c.CustomerName, c.Email FROM Orders o INNER JOIN Customers c ON o.CustomerID = c.CustomerID WHERE o.Status IN ('Shipped', 'Delivered') AND o.CreatedDate >= '2023-01-01' AND (c.City = 'Beijing' OR c.Province = 'Beijing');执行计划显示:
Orders表走Clustered Index Scan(全表扫描),预计读 1,245,678 行;Customers表走Clustered Index Scan,预计读 89,432 行;Nested Loops连接,总耗时 12,345 ms。
表面看是缺索引,但建IX_Orders_Status_CreatedDate后,Orders表仍扫描 80 万行——因为IN ('Shipped','Delivered')虽能走索引,但CreatedDate范围太大(2023 年全年),SQL Server 估算走索引不如全表扫描快。
6.2 执行计划深度解读:三个关键节点告诉你瓶颈在哪
打开执行计划 XML,定位<RelOp>节点,重点关注三项:
| 节点属性 | 含义 | 本例值 | 诊断结论 |
|---|---|---|---|
EstimateRows | 优化器预估返回行数 | 823,456 | 远超实际业务量(北京客户仅 2,300 人),说明统计信息过期或谓词选择率估算错误 |
ActualRows | 实际返回行数 | 1,842 | 与EstimateRows差 447 倍,证明优化器选错了计划 |
EstimatedExecutionMode | 执行模式 | Row | 应为Batch(列存储加速),但当前是行模式,说明未启用列存储索引 |
为什么
EstimateRows错得离谱?
因为OR条件c.City = 'Beijing' OR c.Province = 'Beijing'让优化器无法准确估算选择率。它默认按0.1估算每个条件,OR后变成0.1 + 0.1 - 0.1*0.1 = 0.19,而实际北京客户占比仅0.002(2,300/89,432)。
6.3 三步改写法:不加索引,仅改写 SQL,性能提升 15 倍
Step 1:拆分OR为UNION ALL,让优化器分别估算
-- 改写后,每个分支都能走索引 SELECT o.OrderID, o.CustomerID, o.TotalAmount, c.CustomerName, c.Email FROM Orders o INNER JOIN Customers c ON o.CustomerID = c.CustomerID WHERE o.Status IN ('Shipped', 'Delivered') AND o.CreatedDate >= '2023-01-01' AND c.City = 'Beijing' UNION ALL SELECT o.OrderID, o.CustomerID, o.TotalAmount, c.CustomerName, c.Email FROM Orders o INNER JOIN Customers c ON o.CustomerID = c.CustomerID WHERE o.Status IN ('Shipped', 'Delivered') AND o.CreatedDate >= '2023-01-01' AND c.Province = 'Beijing' AND c.City <> 'Beijing'; -- 排除重复(City='Beijing' 已在上支覆盖)Step 2:为Customers表建复合索引,覆盖City和Province
-- 覆盖查询所需字段,避免回表 CREATE NONCLUSTERED INDEX IX_Customers_City_Province ON Customers(City, Province) INCLUDE (CustomerName, Email, CustomerID);Step 3:更新统计信息,强制优化器重新编译
UPDATE STATISTICS Customers WITH FULLSCAN; -- 全表扫描更新,最准 UPDATE STATISTICS Orders WITH FULLSCAN; -- 清空计划缓存(生产环境慎用,可针对单个查询用 OPTION(RECOMPILE)) DBCC FREEPROCCACHE;效果:执行时间从 12,345 ms → 823 ms,Orders表读取行数从 823,456 → 1,842,Customers表从 89,432 → 2,300。关键不是索引,而是让优化器看到真实的行数分布。
我的习惯是:遇到慢查询,第一反应不是建索引,而是执行
SET STATISTICS XML ON,把执行计划 XML 拷进 SQL Server Execution Plan Viewer (免费在线工具),盯着EstimateRows和ActualRows的比值。如果差 10 倍以上,90% 是统计信息或查询写法问题,索引只是补救。希望帮到你。
本文还有配套的精品资源,点击获取