SQL Server开发里那些老是遇到但又说不清楚的问题到底都有哪些呢?
- 问答
- 2026-01-12 18:49:50
- 3
很多人在用SQL Server做开发的时候,经常会碰到一些感觉“模模糊糊”的问题,这些问题吧,你说它完全不懂,又不是,大概知道个所以然;但真要你清晰、有条理地跟别人解释清楚,又觉得卡壳,说不明白,这些问题就像鞋子里的小石子,不大,但老是硌脚,下面我就把这些常见又“说不清”的问题捋一捋。
第一个大问题,就是NULL值带来的各种“意想不到”。 这个问题几乎每个开发者都踩过坑,来源自数据库理论中“未知”或“不适用”的表示,你写个查询条件 WHERE 列名 = NULL,结果什么都查不出来,你得写成 WHERE 列名 IS NULL,为什么?因为NULL代表未知,两个未知(NULL = NULL)进行比较,结果还是未知(不是True),所以不会被查询条件选中,这还引申出聚合函数的问题,COUNT(*) 和 COUNT(列名) 的区别。COUNT(*) 统计所有行数,包括列值为NULL的行;而 COUNT(列名) 会忽略该列为NULL的行,如果你没搞清楚,统计出来的数字对不上,还得花半天时间找原因。
第二个常说不清的是“锁”和“阻塞”。 经常有这种情况:一个查询在测试环境跑得飞快,一到生产环境就卡住,或者直接把系统搞慢了,背后很可能就是锁在作怪,你有一个大表,业务高峰期时,一个用户正在更新(UPDATE)某一行,这个更新操作会在这行数据上加一个“锁”,防止别人同时修改,保证数据不会错乱,但如果这个更新事务很长时间没提交(比如忘了写COMMIT,或者逻辑复杂运行慢),另一个用户恰好也要查询或更新这行数据,他就得“等着”(阻塞),等久了,就可能超时报错,大家通常只知道“卡住了”,但具体是哪种锁(共享锁、排他锁?)、锁在了哪个对象上(行、页、表?)、是谁堵住了谁,往往说不清楚,排查起来很头疼。
第三点是关于索引的“为什么建了索引还不走?” 这可能是最让人困惑的问题之一,来源自SQL Server查询优化器的工作原理,我们都知道索引能加快查询,但有时候明明建了索引,查询速度却依然很慢,用执行计划一看,发现它居然没用你的索引,而是选择了全表扫描(Table Scan),这时候就会很纳闷,原因可能有很多种,但常常说不全:你的查询条件没有使用索引的“最左前缀”,就像电话簿按(姓,名)排序,你直接查“名”是没法快速查找的;又或者,你要查询的数据量太大了,优化器觉得全表扫描比在索引里跳来跳去再回表查数据更划算;还有可能是索引的统计信息过时了,优化器错误地估计了数据分布,做出了糟糕的选择。
第四点是事务隔离级别和“脏读”、“幻读”这些概念。 这些词经常听,但具体场景下是什么意思,区别在哪,很多人只能意会,默认的隔离级别“已提交读”(READ COMMITTED)是什么意思?就是说一个事务只能读到其他事务已经提交的数据,这样就不会读到“脏数据”(别人修改了但还没提交的、可能被回滚的数据),那“可重复读”(REPEATABLE READ)又是什么?是保证在同一个事务里,多次读取同一批数据,值不会变(防止别人修改),而“幻读”(Phantom Read)是指同一个事务中,两次查询同样条件的数据,第二次查出了第一次没有的“新”行(像幻觉一样),这是“可重复读”级别可能无法解决的,需要更高的“可序列化”(SERIALIZABLE)级别,光是把这几个名词和对应的现象准确区分开,就不太容易。
第五个是临时表(#temp)和表变量(@table)的选择。 什么时候该用哪个?它们有什么区别?这也是个老生常谈但又细节模糊的问题,大家都知道临时表是存在tempdb里的真实表,而表变量更像是在内存中的变量,但具体差异呢?临时表可以创建索引,表变量不能(除了主键和唯一约束带来的索引);临时表会参与事务,回滚事务时对临时表的修改也会回滚,而表变量不受事务回滚影响;当数据量很大时,临时表因为会有统计信息,查询优化器能做出更好的执行计划,可能比表变量性能更好,这些细微的差别,决定了在不同场景下该用谁。
第六点,字符串拼接和GROUP BY聚合时的显示问题。 有一个订单表,一个订单对应多个商品,你想用一个查询,把同一个订单的所有商品名称用逗号拼接成一个字段显示出来,早期常用FOR XML PATH(‘’)这种“黑魔法”一样的写法,现在有了STRING_AGG函数好了很多,但为什么以前要那么写?原理是什么?很多人用了却说不清,同样,在GROUP BY时,SELECT后面出现的列,要么在GROUP BY里,要么在聚合函数里,这个规则都知道,但为什么要有这个规则?它的设计初衷是为了保证分组后,非聚合列的值的唯一性,避免出现语义不清的结果,理解了这个“为什么”,才能更好地写查询。
还有一些像“视图(View)到底会不会提升性能?”这样的误解。 很多人觉得把复杂查询做成视图,下次查视图就会快,其实不然,视图本质上是一个保存好的查询语句,它本身不存储数据(物化视图除外),查询视图时,仍然是执行底层那个复杂的SQL,它主要作用是简化代码、增强安全性和逻辑抽象,而不是直接提升性能,有时候复杂的多表视图反而可能因为优化器无法完美优化而成为性能瓶颈。
这些问题之所以“说不清楚”,往往是因为它们背后牵扯到数据库的核心机制,比如事务的ACID特性、查询优化器的成本计算、锁的并发控制等,日常开发中我们更关注功能的实现,对这些底层原理一知半解,等到问题出现时,就只能用“好像”、“可能”来猜测,而无法精准地定位和解释了。

本文由瞿欣合于2026-01-12发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://www.haoid.cn/wenda/79476.html
