尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SQL Server 实战入门:从连不上到慢查询优化

SQL Server 实战入门:从连不上到慢查询优化 简介这是一份面向数据库初学者与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 StudioSSMS输完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_statssys.dm_exec_sql_text定位真实拖垮系统的语句再用CREATE INDEXINCLUDEWHERE三步闭环落地。适合刚转岗 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与ARM64Windows 11 ARM 设备需单独选型。安装时必须以管理员身份运行 setup.exe否则服务账户注册失败。推荐使用静默安装避免 UI 向导跳过关键配置命令如下setup.exe /Q /ACTIONInstall /INSTANCENAMEMSSQL2022 /FEATURESSQLEngine,Replication,FullText /UPDATEENABLEDFALSE /SQLSVCACCOUNTNT Service\MSSQL$MSSQL2022 /SQLSVCPASSWORD /SQLSYSADMINACCOUNTSBUILTIN\Administrators /AGTSVCACCOUNTNT Service\SQLAgent$MSSQL2022 /IACCEPTSQLSERVERLICENSETERMS说明/INSTANCENAMEMSSQL2022显式指定命名实例名避免默认实例冲突/SQLSVCACCOUNTNT Service\MSSQL$MSSQL2022使用内置虚拟账户比 LocalSystem 更安全且无需手动配置磁盘权限/SQLSYSADMINACCOUNTSBUILTIN\Administrators将本机管理员组设为 sysadmin省去后续手动授权/UPDATEENABLEDFALSE关闭自动更新防止安装中途弹窗中断流程静默安装日志默认存于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 AppPass2022!, 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 IDAppUser;PasswordAppPass2022!即可访问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用户不存在拼错saState 5用户存在但密码错误大小写敏感或复制粘贴带空格State 8密码错误最常见但需结合日志确认State 9密码已过期CHECK_EXPIRATION ON且未改密State 11 or 12用户已锁定多次输错触发账户锁定。解决查 Windows 事件查看器 → Windows 日志 → 应用程序 → 筛选来源MSSQLSERVER找到对应时间戳的Error 18456事件末尾有State: X若State8重置密码ALTER LOGIN sa WITH PASSWORD NewPass123!若State11/12解锁ALTER LOGIN sa WITH PASSWORD NewPass123! UNLOCK永远不要用记事本存密码——它可能插入不可见 Unicode 字符如U200E左向控制符导致粘贴后密码无效。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 1433PowerShell返回TcpTestSucceeded : True即通。4.3 现象The target principal name is incorrect原因客户端尝试 Kerberos 认证但 SQL Server 实例的 SPNService Principal Name未注册或注册错误。常见于域环境或用localhost连接但 SPN 绑定的是机器全名。解决查当前 SPNsetspn -L MSSQLSvc/DESKTOP-ABC123:1433替换为你机器名若无输出注册 SPNsetspn -S MSSQLSvc/DESKTOP-ABC123:1433 DOMAIN\SQLServiceAccountSQLServiceAccount是 SQL Server 服务账户如NT Service\MSSQL$MSSQL2022简单绕过法SSMS 连接 → “选项” → “连接属性” → 勾选“连接到数据库引擎” → 在“数据库名称”填master可强制走 NTLM 而非 Kerberos。4.4 现象Driver cannot establish a secure SSL connection原因JDBC/ODBC 驱动强制要求加密但 SQL Server 未配置证书或客户端未信任服务器证书。解决临时关闭加密仅测试用连接字符串加encryptfalse;trustServerCertificatetrue生产环境正解在 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 集成。用户误在连接字符串中加了AuthenticationActive Directory Password而实例是纯本地部署。解决删除连接字符串中所有Authentication参数确认 SQL Server 配置中未启用 Azure ADSELECT * 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 NSalesDB_Data, FILENAME ND:\SQLData\SalesDB.mdf, -- 建议 SSD 盘勿放 C:\Program Files\ SIZE 100MB, -- 初始大小避免频繁自动增长 MAXSIZE UNLIMITED, -- 生产环境建议设上限如 500GB FILEGROWTH 50MB -- 每次增长 50MB而非默认 10%防碎片 ) LOG ON ( NAME NSalesDB_Log, FILENAME ND:\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 NodeId1 PhysicalOpClustered 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.0022,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; -- 排除重复CityBeijing 已在上支覆盖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 msOrders表读取行数从 823,456 → 1,842Customers表从 89,432 → 2,300。关键不是索引而是让优化器看到真实的行数分布。我的习惯是遇到慢查询第一反应不是建索引而是执行SET STATISTICS XML ON把执行计划 XML 拷进 SQL Server Execution Plan Viewer 免费在线工具盯着EstimateRows和ActualRows的比值。如果差 10 倍以上90% 是统计信息或查询写法问题索引只是补救。希望帮到你。本文还有配套的精品资源点击获取
返回列表