☰
SQL Server 实战入门:从连不上到慢查询优化
2026/10/2 9:46:52 网站建设 项目流程

简介:这是一份面向数据库初学者与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 协议并手动设置:

  1. 打开SQL Server Configuration Manager→ 左侧展开SQL Server Network Configuration→ 点击Protocols for MSSQL2022;
  2. 右键TCP/IP→ “启用”;
  3. 右键TCP/IP→ “属性” → 切换到IP Addresses选项卡;
  4. 拉到底部IPAll区域,清空TCP Dynamic Ports,在TCP Port输入1433;
  5. 重启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:用户已锁定(多次输错触发账户锁定)。

解决:

  1. 查 Windows 事件查看器 → Windows 日志 → 应用程序 → 筛选来源MSSQLSERVER,找到对应时间戳的Error 18456事件,末尾有State: X;
  2. 若State=8,重置密码:ALTER LOGIN sa WITH PASSWORD = 'NewPass123!';
  3. 若State=11/12,解锁:ALTER LOGIN sa WITH PASSWORD = 'NewPass123!' UNLOCK;
  4. 永远不要用记事本存密码——它可能插入不可见 Unicode 字符(如U+200E左向控制符),导致粘贴后密码无效。

4.2 现象:A network-related or instance-specific error occurred while establishing a connection...

原因:TCP/IP 协议未启用,或 SQL Server Browser 服务未启动(命名实例必需),或防火墙拦截 1433 端口。

解决:

  1. 确认SQL Server (MSSQL2022)和SQL Server Browser两个服务均Running;
  2. 在SQL Server Configuration Manager中启用TCP/IP并设固定端口(见 2.2 节);
  3. Windows 防火墙放行:New-NetFirewallRule -DisplayName "SQL Server Port 1433" -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow;
  4. 测试端口连通性: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 绑定的是机器全名。

解决:

  1. 查当前 SPN:setspn -L MSSQLSvc/DESKTOP-ABC123:1433(替换为你机器名);
  2. 若无输出,注册 SPN:setspn -S MSSQLSvc/DESKTOP-ABC123:1433 DOMAIN\SQLServiceAccount(SQLServiceAccount是 SQL Server 服务账户,如NT Service\MSSQL$MSSQL2022);
  3. 简单绕过法:SSMS 连接 → “选项” → “连接属性” → 勾选“连接到数据库引擎” → 在“数据库名称”填master,可强制走 NTLM 而非 Kerberos。

4.4 现象:Driver cannot establish a secure SSL connection

原因:JDBC/ODBC 驱动强制要求加密,但 SQL Server 未配置证书,或客户端未信任服务器证书。

解决:

  1. 临时关闭加密(仅测试用):连接字符串加encrypt=false;trustServerCertificate=true;
  2. 生产环境正解:在 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,而实例是纯本地部署。

解决:

  1. 删除连接字符串中所有Authentication=参数;
  2. 确认 SQL Server 配置中未启用 Azure AD(SELECT * FROM sys.dm_exec_connections WHERE auth_scheme = 'KERBEROS' OR auth_scheme = 'NTLM',不应出现AzureAD);
  3. 若真需 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"—— 全表扫描是索引缺失的铁证。

优化闭环:

  1. 复制statement_text中的WHERE条件字段(如WHERE Status = 'Pending' AND CreatedDate > '2023-01-01');
  2. 在对应表上建覆盖索引:CREATE NONCLUSTERED INDEX IX_Orders_Status_CreatedDate ON Orders(Status, CreatedDate) INCLUDE (OrderID, TotalAmount);;
  3. 清空缓存(仅测试):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% 是统计信息或查询写法问题,索引只是补救。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询