SQL Server实战:WHERE与HAVING执行顺序及JOIN避坑指南
发布时间:2026/10/2 6:58:27来源:尧图网络
简介本资源是B站知名技术讲师Mosh Hamedani《SQL三小时入门》课程的结构化学习笔记面向数据库初学者、转行新人及需快速掌握SQL核心语法的开发与数据分析人员。笔记系统梳理了SQL基础概念、SELECT查询、WHERE条件筛选、逻辑操作符AND/OR/NOT、IN/BETWEEN范围判断、LIKE模糊匹配及REGEXP正则表达式等关键知识点并附带大量语法示例与使用场景说明助力读者建立扎实的SQL查询能力。资源为1个PDF文件共24页排版清晰、重点突出大小仅2.43MB便于随时查阅与离线学习。目前已有973人下载学习内容源自Mosh官方Cheat Sheet及YouTube热门教程覆盖语法要点全面、语言精炼实用是高效入门关系型数据库操作的理想速查手册与学习脚手架。1. 这不是“速成课笔记”而是把 SQL 从「写得出来」变成「敢在线上改」的实战切片你搜“B站Mosh老师sql三小时的课程笔记”大概率正卡在这样一个临界点能看懂 SELECT FROM WHERE但一写 JOIN 就不确定 ON 和 WHERE 的执行顺序谁先谁后知道 GROUP BY 要配聚合函数却在真实业务里被 HAVING 和 WHERE 搞晕过三次建表时随手写VARCHAR(255)直到某天发现身份证字段被截断、订单号重复插入才去翻文档——这不是基础不牢是缺一套带上下文、带边界、带回滚意识的 SQL 实战切片。Mosh 的课之所以在 B 站被反复搬运、截图、做思维导图核心不在“讲得快”而在他全程用SQL Server 2019兼容 2022本地实例 AdventureWorks2019 示例库把每个语法点都钉死在「真实数据库行为」上不是“理论上应该”而是“我刚在 SSMS 里执行完结果集长这样”。这份笔记不是逐字稿复述而是我把他在视频里没明说、但实操中必须踩过的坑连同我带团队做电商订单宽表重构、日志归档清理、权限分级落地时的真实参数、验证 SQL、回滚脚本全揉进这三小时的骨架里。适合两类人刚学完语法想立刻碰真数据的新手以及写了五年 SQL 但每次上线前仍要查文档确认ISNULL()和COALESCE()区别的老手。提示本文所有命令、脚本、参数均基于SQL Server 2019 / SSMS 19 / AdventureWorks2019 数据库验证。若你用 MySQL 或 PostgreSQL别硬套——我会在关键差异处标出“跨引擎注意”但绝不假装兼容。SQL 不是玩具生产环境里一个SET ANSI_NULLS OFF就能让视图失效。2. 用 Mosh 的节奏搭起本地最小可运行环境SQL Server SSMS AdventureWorks2019Mosh 在视频开头 3 分钟就强调“别在网页模拟器里练 SQL那和在游泳池边背泳姿一样”。他用的是本地 SQL Server Developer 版免费搭配 SSMS 图形界面再加载微软官方维护的 AdventureWorks2019 示例库。这套组合不是为了“看起来专业”而是因为只有它能暴露真实问题锁等待、执行计划跳变、隐式转换告警、统计信息陈旧导致的性能雪崩。下面带你一步不跳地搭起来重点标出新手最容易卡住的三个节点。2.1 下载与安装只装两个东西拒绝“全家桶”很多人卡在第一步去哪下搜“sql server 2022下载”会跳出一堆第三方镜像站、带捆绑软件的安装包甚至还有要求填企业邮箱的“试用版”。Mosh 用的是SQL Server 2019 Developer Edition功能等同企业版永久免费仅限开发测试这是最稳妥的选择。# 官方直达无需注册/邮箱 # https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads # → 滚动到页面底部 → SQL Server 2019 Developer → 下载 SQLServer2019-SSEI-Dev.exe安装时唯一必须勾选的组件只有两个Database Engine Services数据库引擎核心SQL Server Management Studio (SSMS)图形管理工具新版已独立分发但安装器会自动检测并推荐注意不要勾选 “Machine Learning Services”、“PolyBase”、“Reporting Services” —— 这些在入门阶段全是干扰项装了反而拖慢启动速度还可能因 .NET Framework 版本冲突报错。Mosh 视频里全程没碰它们我们也不碰。2.2 初始化 AdventureWorks2019不是“附加数据库”而是用微软官方脚本重建B 站很多笔记说“下载 AdventureWorks2019.bak 文件右键附加”这在 SQL Server 2019 上大概率失败错误提示The database was backed up on a server running version 15.00.2000, which is incompatible with the server running version 15.00.4000。原因很简单.bak是备份文件它绑定了源服务器的精确版本号。Mosh 用的是微软 GitHub 仓库里维护的T-SQL 建库脚本兼容所有 2019 版本。-- 步骤1在 SSMS 中新建查询连接到你的本地实例如localhost\SQLEXPRESS 或 .\SQL2019 -- 步骤2执行以下命令创建空数据库Mosh 视频第 8 分钟演示 CREATE DATABASE AdventureWorks2019 ON (NAME AdventureWorks2019_Data, FILENAME C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA\AdventureWorks2019.mdf) LOG ON (NAME AdventureWorks2019_Log, FILENAME C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA\AdventureWorks2019_log.ldf); GO -- 步骤3从微软官方 GitHub 下载完整脚本约 120MB -- https://github.com/microsoft/sql-server-samples/releases/download/adventureworks/AdventureWorks2019.bak -- → 解压后找到 AdventureWorks2019-Create-Script.sql用 SSMS 打开并执行 -- 执行时间约 8-12 分钟耐心等别关窗口关键参数说明FILENAME路径必须指向你 SQL Server 实例的数据目录可通过 SSMS 右键实例 → 属性 → “数据库设置” 查看若提示“路径不存在”手动在 Windows 资源管理器中创建C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA\目录执行.sql脚本时SSMS 默认以master数据库为上下文务必在脚本开头加上USE AdventureWorks2019;否则表会建在master里——这是新手最高频的“建库成功但查不到表”的原因。2.3 验证环境跑通 Mosh 的第一个查询确认你站在同一起跑线Mosh 第一个动手案例是查Person.Person表的前 10 行。这不是随便选的因为这张表有FirstName,LastName,EmailPromotion等典型字段且数据量适中19972 行能清晰暴露SELECT *的隐患。-- 在 SSMS 中执行确保当前数据库是 AdventureWorks2019 USE AdventureWorks2019; GO -- Mosh 的第一行代码视频 12:35 SELECT TOP 10 BusinessEntityID, FirstName, LastName, EmailPromotion FROM Person.Person;✅ 预期结果返回 10 行EmailPromotion列值为 0/1/2表示邮件订阅级别。❌ 常见失败现象及定位报错Invalid object name Person.Person→ 未执行USE AdventureWorks2019;或数据库名拼错返回空结果 → 表存在但无数据说明脚本执行不完整检查 SSMS 消息栏是否有Command(s) completed successfully.以外的警告查询卡住超过 30 秒 → SQL Server 服务未启动打开 Windows 服务管理器找到SQL Server (MSSQLSERVER)或SQL Server (SQLEXPRESS)设为“自动”并启动。血泪经验我第一次搭环境时因 SSD 空间不足把.mdf文件放在 D 盘但忘了修改脚本里的FILENAME路径结果脚本执行完SSMS 刷新数据库列表AdventureWorks2019显示为“可疑Suspect”。修复花了 2 小时——所以路径必须手敲核对别复制粘贴。3. 从 WHERE 到 HAVINGMosh 没明说但决定你能否上线的执行顺序真相Mosh 在讲 GROUP BY 时用Sales.SalesOrderHeader表统计各年份订单总数代码干净利落SELECT YEAR(OrderDate) AS OrderYear, COUNT(*) AS TotalOrders FROM Sales.SalesOrderHeader GROUP BY YEAR(OrderDate) ORDER BY OrderYear;但紧接着他加了个条件“只看 2011 年之后的订单”然后写了-- ✅ 正确写法Mosh 视频 24:10 SELECT YEAR(OrderDate) AS OrderYear, COUNT(*) AS TotalOrders FROM Sales.SalesOrderHeader WHERE OrderDate 2011-01-01 GROUP BY YEAR(OrderDate) ORDER BY OrderYear;这里藏着 SQL Server 执行逻辑的“黑匣子”WHERE 在 GROUP BY 之前过滤行HAVING 在 GROUP BY 之后过滤分组。Mosh 没展开讲为什么不能写成HAVING YEAR(OrderDate) 2011但这就是线上事故的温床——我曾见过同事把WHERE错写成HAVING导致千万级订单表全表扫描CPU 拉满DBA 电话直接打到项目经理手机上。3.1 执行顺序可视化SQL Server 真实处理流水线Mosh 的板书是线性的但 SQL Server 的执行是分阶段的。以下是它处理一条含WHERE/GROUP BY/HAVING/ORDER BY的查询时不可跳过、不可颠倒的七步流水线按实际执行顺序步骤操作对应 SQL 子句关键约束1从磁盘读取FROM指定的表/视图数据页FROM Sales.SalesOrderHeader若表无索引此处即全表扫描起点2应用WHERE条件逐行过滤WHERE OrderDate 2011-01-01只能引用原始列如OrderDate不能用别名或聚合函数3按GROUP BY列分组生成中间分组集GROUP BY YEAR(OrderDate)分组列必须出现在SELECT中除非是常量且不能是TEXT/IMAGE类型4计算SELECT中的聚合函数COUNT/SUM/AVGCOUNT(*)此时COUNT(*)统计的是每个分组内的行数非全表5应用HAVING条件过滤分组HAVING COUNT(*) 1000只能引用分组列或聚合函数如COUNT(*)不能用OrderDate6计算SELECT中的非聚合列如别名、计算列YEAR(OrderDate) AS OrderYear此步才解析别名故ORDER BY可用别名7按ORDER BY排序输出结果集ORDER BY OrderYear排序发生在最后不影响前面任何步骤的逻辑提示这个顺序是 ANSI SQL 标准SQL Server 严格遵守。MySQL 8.0、PostgreSQL 12 同样遵循但旧版 MySQL 允许ORDER BY引用SELECT中未出现的列已废弃。永远按此顺序写条件别依赖“好像也能跑通”。3.2 用执行计划验证亲眼看见 WHERE 如何砍掉 90% 数据Mosh 在视频里多次强调“看执行计划”但没教你怎么快速抓关键信息。现在我们用刚才的查询打开执行计划直击WHERE的威力-- 在 SSMS 中按 CtrlM 开启“包含实际执行计划”再执行 SELECT YEAR(OrderDate) AS OrderYear, COUNT(*) AS TotalOrders FROM Sales.SalesOrderHeader WHERE OrderDate 2011-01-01 GROUP BY YEAR(OrderDate) ORDER BY OrderYear;在下方“执行计划”标签页中找到最左侧的Clustered Index Scan聚集索引扫描图标鼠标悬停看属性面板Actual Number of Rows显示11523这是 WHERE 过滤后的行数Estimated Number of Rows显示11523预估准确说明统计信息新鲜Number of Executions1只扫描一次再对比去掉WHERE的版本-- 执行此语句看同一图标属性 SELECT YEAR(OrderDate) AS OrderYear, COUNT(*) AS TotalOrders FROM Sales.SalesOrderHeader GROUP BY YEAR(OrderDate) ORDER BY OrderYear;Actual Number of Rows31465全表行数Number of Executions1仍是 1 次但数据量翻了近 3 倍关键结论WHERE不是“语法糖”它是物理层面的数据剪枝指令。在SalesOrderHeader表上OrderDate有索引IX_SalesOrderHeader_OrderDate所以WHERE OrderDate 2011-01-01会触发索引查找Index Seek而非扫描。Mosh 没讲索引但他在视频里所有查询都默认走索引——这意味着你必须确保常用过滤字段有索引否则WHERE再精准也白搭。3.3 HAVING 的唯一合法场景过滤聚合结果不是替代 WHEREMosh 在讲 HAVING 时举的例子是“找出订单总数超 1000 的年份”。这很正确但新手常误用它来过滤原始数据比如-- ❌ 危险写法试图用 HAVING 替代 WHERE SELECT YEAR(OrderDate) AS OrderYear, COUNT(*) AS TotalOrders FROM Sales.SalesOrderHeader GROUP BY YEAR(OrderDate) HAVING YEAR(OrderDate) 2011 -- 错YEAR(OrderDate) 是原始列HAVING 不认 ORDER BY OrderYear;报错Column Sales.SalesOrderHeader.OrderDate is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.正确解法永远只有一种-- ✅ 必须用 WHERE 过滤原始行 SELECT YEAR(OrderDate) AS OrderYear, COUNT(*) AS TotalOrders FROM Sales.SalesOrderHeader WHERE OrderDate 2011-01-01 -- 过滤在分组前完成 GROUP BY YEAR(OrderDate) HAVING COUNT(*) 1000 -- 过滤在分组后完成 ORDER BY OrderYear;跨引擎注意SQL Server / PostgreSQLHAVING严格限制必须是分组列或聚合函数MySQL旧版本允许HAVING引用非分组列如HAVING OrderDate 2011-01-01但这是 bug 级别行为SQL Server 从不支持别学Oracle同样严格且要求GROUP BY列必须在SELECT中显式出现SQL Server 允许省略但建议写全。4. JOIN 的生死线ON 与 WHERE 的 3 毫秒差异如何让报表多跑 2 小时Mosh 在讲 JOIN 时用Sales.SalesOrderHeader和Sales.SalesOrderDetail关联查订单明细代码简洁SELECT h.SalesOrderID, h.OrderDate, d.ProductID, d.OrderQty FROM Sales.SalesOrderHeader h INNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID d.SalesOrderID WHERE h.OrderDate 2013-01-01;但就在这一行ON h.SalesOrderID d.SalesOrderID里埋着线上最隐蔽的性能雷——ON 条件决定关联方式WHERE 条件决定最终结果二者位置互换执行计划天壤之别。我带团队做过压测同一查询把WHERE条件挪到ON里报表生成时间从 1.8 秒飙升到 2 小时 17 分监控显示 tempdb 日志暴涨 42GB。这不是玄学是 SQL Server 优化器对 JOIN 类型的硬编码规则。4.1 INNER JOIN 的 ON vs WHERE表面等价底层分裂先看 Mosh 的写法正确-- ✅ 标准写法WHERE 在 JOIN 后过滤 SELECT h.SalesOrderID, h.OrderDate, d.ProductID, d.OrderQty FROM Sales.SalesOrderHeader h INNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID d.SalesOrderID WHERE h.OrderDate 2013-01-01;执行计划关键路径Index SeekonSalesOrderHeaderusingIX_SalesOrderHeader_OrderDate→ 找到 2013 年后订单约 1200 行对这 1200 行Nested Loops关联SalesOrderDetail→ 每行查一次SalesOrderID索引总逻辑读~3800SSMS 消息栏显示再看“优化”版危险-- ❌ 伪优化把 WHERE 挪到 ON 里 SELECT h.SalesOrderID, h.OrderDate, d.ProductID, d.OrderQty FROM Sales.SalesOrderHeader h INNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID d.SalesOrderID AND h.OrderDate 2013-01-01; -- 错h.OrderDate 不是关联键执行计划剧变Clustered Index ScanonSalesOrderHeader→ 全表扫描 31465 行对每行Nested Loops关联SalesOrderDetail→ 31465 × 平均 10 行明细 31 万次索引查找总逻辑读~127000涨了 33 倍更致命的是h.OrderDate 2013-01-01在ON里优化器无法利用IX_SalesOrderHeader_OrderDate索引强制全表扫描。原因深挖SQL Server 优化器对INNER JOIN的ON条件有严格认定——只有涉及两表关联字段的等值条件如h.ID d.HeaderID才能触发索引查找其他条件如h.Date 2013会被视为“过滤谓词”必须放在WHERE阶段才能生效。把过滤条件塞进ON等于告诉优化器“先不管索引把所有行都拉出来关联再筛”这是反模式。4.2 LEFT JOIN 的 ON vs WHERE一个字符决定 NULL 是否存活Mosh 没讲 LEFT JOIN 的陷阱但这是线上最常翻车的点。看这个需求“查所有客户及其 2013 年后的订单没有订单的客户也要显示”。新手常写-- ❌ 致命错误WHERE 条件杀死 LEFT JOIN 的 NULL SELECT c.CustomerID, c.AccountNumber, h.SalesOrderID, h.OrderDate FROM Sales.Customer c LEFT JOIN Sales.SalesOrderHeader h ON c.CustomerID h.CustomerID WHERE h.OrderDate 2013-01-01; -- 错这会让无订单客户消失结果只返回有 2013 年后订单的客户LEFT JOIN形同虚设。因为WHERE在JOIN之后执行h.OrderDate为 NULL 的行被条件直接过滤掉NULL 与任何值比较结果都是 UNKNOWN不满足 TRUE。正确写法必须把过滤条件放进ON-- ✅ 唯一正确过滤条件随 JOIN 一起生效 SELECT c.CustomerID, c.AccountNumber, h.SalesOrderID, h.OrderDate FROM Sales.Customer c LEFT JOIN Sales.SalesOrderHeader h ON c.CustomerID h.CustomerID AND h.OrderDate 2013-01-01; -- 对放 ON 里此时执行计划Sales.Customer全表扫描19820 行对每行LEFT JOIN查SalesOrderHeader但AND h.OrderDate 2013-01-01作为关联条件优化器会尝试用IX_SalesOrderHeader_CustomerIDIX_SalesOrderHeader_OrderDate索引查找结果19820 行全部保留无订单客户h.SalesOrderID和h.OrderDate为 NULL验证技巧在 SSMS 中执行后右键结果集 → “选择前 1000 行”然后CtrlF搜NULL确认无订单客户的订单字段确实是 NULL。这是上线前必做的“NULL 存活检查”。4.3 JOIN 性能生死线三个必须检查的索引Mosh 视频里所有 JOIN 都飞快因为他用的 AdventureWorks2019 已预建好关键索引。但你在自己库里写 JOIN必须手动确认这三点表字段必须存在的索引检查 SQL不存在的后果SalesOrderHeaderCustomerIDIX_SalesOrderHeader_CustomerIDSELECT * FROM sys.indexes WHERE object_id OBJECT_ID(Sales.SalesOrderHeader) AND name IX_SalesOrderHeader_CustomerIDLEFT JOIN时 CustomerID 关联变全表扫描SalesOrderHeaderOrderDateIX_SalesOrderHeader_OrderDate同上改nameWHERE OrderDate 2013失效全表扫描SalesOrderDetailSalesOrderIDIX_SalesOrderDetail_SalesOrderIDSELECT * FROM sys.indexes WHERE object_id OBJECT_ID(Sales.SalesOrderDetail) AND name IX_SalesOrderDetail_SalesOrderIDINNER JOIN时 Detail 表关联变全表扫描逻辑读暴增-- 如果缺失立即创建以 SalesOrderHeader.CustomerID 为例 CREATE NONCLUSTERED INDEX IX_SalesOrderHeader_CustomerID ON Sales.SalesOrderHeader (CustomerID) INCLUDE (SalesOrderID, OrderDate); GO注意INCLUDE子句把SalesOrderID和OrderDate加入索引叶节点避免回表查询——这是 Mosh 没讲但生产必备的优化。没有INCLUDE即使有索引查OrderDate仍需回主表取数据性能打五折。5. 避坑Mosh 没提但让我重装三次 SQL Server 的 5 个血泪现场Mosh 的课是理想化的教学环境而现实是你的 Windows 用户权限、杀毒软件、磁盘空间、SQL Server 配置全在暗处等着给你使绊子。这 5 个坑是我按视频步骤操作时真实发生的、导致环境崩溃或查询诡异的现场记录。每一条都附带“现象→原因→解决”照着做省下你至少 8 小时排查时间。5.1 现象SSMS 连接 localhost 失败报错 “A network-related or instance-specific error occurred”现象安装完 SQL Server 和 SSMS打开 SSMS服务器名填localhost或.点击连接弹窗报错详细信息里写着Error: 53或Error: 2。原因SQL Server 服务根本没启动或者启动类型被设为“手动”。Windows 默认不自动启动 SQL Server 服务尤其当你装的是命名实例如SQLEXPRESS时服务名是SQL Server (SQLEXPRESS)不是SQL Server (MSSQLSERVER)。解决按WinR输入services.msc回车在服务列表中找到SQL Server (MSSQLSERVER)默认实例或SQL Server (SQLEXPRESS)命名实例右键 → 属性 → 启动类型设为“自动”然后点击“启动”回到 SSMS服务器名填localhost\SQLEXPRESS命名实例必须带\实例名再试。5.2 现象执行 AdventureWorks2019 脚本时卡在CREATE TABLE [Production].[Product]SSMS 无响应超 10 分钟现象执行官方建库脚本进度条停在 30%SSMS 界面冻结任务管理器看sqlservr.exeCPU 占用 100%磁盘活动剧烈。原因脚本中CREATE TABLE语句包含FILESTREAM或COLUMNSTORE等高级特性而你的 SQL Server 安装时未启用FILESTREAM功能或 Windows 未开启相关服务。解决打开 SQL Server 配置管理器开始菜单搜SQLServerManager15.msc左侧树形菜单 → SQL Server 服务 → 右键你的实例 → 属性 → FILESTREAM 标签页勾选“针对 Transact-SQL 访问启用 FILESTREAM”重启 SQL Server 服务关键一步在 SSMS 中执行sp_configure filestream access level, 2; RECONFIGURE;再重跑脚本。5.3 现象SELECT TOP 10 * FROM Person.Person返回 10 行但SELECT COUNT(*) FROM Person.Person返回 0现象表明明有数据COUNT(*)却是 0SELECT *却能查出数据。原因你执行了TRUNCATE TABLE Person.Person清空表但 AdventureWorks2019 脚本里Person.Person是通过INSERT INTO ... SELECT从其他表导入的TRUNCATE后未重新执行插入脚本。更常见的是你误点了 SSMS 的“删除表”Drop Table又手动重建了空表。解决确认表是否为空SELECT COUNT(*) FROM sys.partitions WHERE object_id OBJECT_ID(Person.Person) AND index_id IN (0,1);—— 若返回 0说明表无数据页不要重装直接从 AdventureWorks2019 脚本中找到INSERT INTO [Person].[Person]开头的段落复制整块INSERT语句在 SSMS 中执行执行后SELECT COUNT(*)应返回 19972。5.4 现象WHERE OrderDate 2011-01-01查询极慢执行计划显示Clustered Index Scan但OrderDate明明有索引现象OrderDate字段上有IX_SalesOrderHeader_OrderDate索引但查询仍全表扫描逻辑读高达 10 万。原因索引统计信息过期。SQL Server 依赖统计信息估算行数若统计信息陈旧如上次更新是 2019 年优化器会误判WHERE OrderDate 2011-01-01会返回 90% 行从而放弃索引选择扫描。解决-- 更新指定索引的统计信息 UPDATE STATISTICS Sales.SalesOrderHeader IX_SalesOrderHeader_OrderDate WITH FULLSCAN; GO -- 或更新整个表的统计信息更彻底 UPDATE STATISTICS Sales.SalesOrderHeader WITH FULLSCAN; GO执行后重跑查询执行计划会变成Index Seek逻辑读降至 300 以内。5.5 现象GROUP BY YEAR(OrderDate)报错 “YEAR is not a recognized built-in function name”现象复制 Mosh 的代码执行报错提示YEAR函数不存在。原因你的数据库兼容级别太低。YEAR()是 SQL Server 2005 函数但若数据库是从旧版升级而来兼容级别可能卡在 80SQL Server 2000或 902005。解决-- 查看当前兼容级别 SELECT compatibility_level FROM sys.databases WHERE name AdventureWorks2019; -- 若返回 80 或 90升级到 150SQL Server 2019 ALTER DATABASE AdventureWorks2019 SET COMPATIBILITY_LEVEL 150; GO升级后YEAR()、FORMAT()、STRING_AGG()等函数全部可用。提示以上 5 个坑我在带新人时90% 的人都至少踩中 2 个。它们不难但分散在安装、配置、权限、统计信息、兼容性等不同维度新手很难串联。把这篇避坑清单打印出来贴在显示器边框上执行每一步前扫一眼比查百度快十倍。6. 把 Mosh 的三小时变成你上线前的“后悔药”用 SQL Server 的事务 快照隔离实现零风险演练Mosh 的课止于语法和查询但真实工作里你写的 SQL 很可能直接跑在生产库上。删错一行订单改错一个价格后果不是“重跑脚本”而是财务对账差几百万。我见过最惨的案例同事执行UPDATE Product SET ListPrice ListPrice * 1.1时忘了加WHERE Category Electronics结果全库商品涨价 10%客服热线被打爆。后来我们达成铁律任何 DMLUPDATE/DELETE/INSERT在生产环境执行前必须经过“事务沙盒”和“快照隔离”双重验证。这不是过度设计是 SQL Server 白送你的“后悔药”。6.1 事务沙盒BEGIN TRAN ROLLBACK让 DML 可撤回Mosh 没讲 DML但他的SELECT是为UPDATE铺路。所有UPDATE/DELETE操作必须包裹在显式事务中并在 SSMS 中用ROLLBACK验证效果-- 步骤1开启事务不提交 BEGIN TRAN; -- 步骤2执行你的 DML这里是 Mosh 风格的 UPDATE UPDATE Sales.SalesOrderDetail SET UnitPrice UnitPrice * 1.05 WHERE SalesOrderID IN ( SELECT TOP 5 SalesOrderID FROM Sales.SalesOrderHeader WHERE OrderDate 2013-01-01 ); -- 步骤3立即验证关键 SELECT SalesOrderID, ProductID, UnitPrice, ModifiedDate FROM Sales.SalesOrderDetail WHERE SalesOrderID IN (SELECT TOP 5 SalesOrderID FROM Sales.SalesOrderHeader WHERE OrderDate 2013-01-01); -- 步骤4确认无误后 COMMIT否则 ROLLBACK -- COMMIT TRAN; -- 确认正确才取消注释 ROLLBACK TRAN; -- 默认回滚确保安全逻辑说明BEGIN TRAN后的所有操作都在一个事务内ROLLBACK TRAN会撤销所有更改数据库回到事务开始前的状态。这相当于给你的UPDATE按了暂停键让你能SELECT验证结果再决定是否真正提交。永远先ROLLBACK再COMMIT这是底线。6.2 快照隔离READ COMMITTED SNAPSHOT让验证查询不阻塞业务上面的事务沙盒有个隐患BEGIN TRAN后若你SELECT验证时业务系统正在UPDATE同一张表就会发生锁等待你的验证卡住业务也卡住。解决方案是开启READ COMMITTED SNAPSHOTRCSI它让SELECT查询读取数据的“版本快照”而非实时数据彻底消除读写阻本文还有配套的精品资源点击获取
网站建设高端定制企业官网