优化含多UNION ALL的同表SQL查询:实现单次表扫描
解决多次全表扫描的列转行优化方案
嘿,这个问题我之前也踩过坑——用一堆UNION ALL拼子查询做列转行,结果百万行的表被反复扫了N次,性能直接崩了。既然不能改表、加索引也没用(毕竟要全表扫描),那咱们换个思路,用一次扫描完成列转行就搞定了,给你两个靠谱的方案:
方案1:用CROSS APPLY实现灵活的列转行
CROSS APPLY可以把每行的多个列转换成对应的多行记录,而且全程只需要扫描一次tbl1表。它的优势是灵活性拉满,比如你可以轻松过滤掉volume为空的行,或者处理不同数据类型的列(如果有的话)。
示例代码:
WITH cte AS ( SELECT ca.volume, ca.description FROM dbo.tbl1 CROSS APPLY ( VALUES (A, 'Column A'), (B, 'Column B'), (C, 'Column C') -- 在这里继续添加其他需要转换的列和对应的description ) AS ca(volume, description) -- 如果需要过滤空值,加个WHERE条件:WHERE ca.volume IS NOT NULL ) SELECT * FROM cte;
原理很直白:SQL Server会先一次性扫描tbl1拿到所有行,然后对每行执行CROSS APPLY里的VALUES列表,把每一列拆成一条新记录,全程只扫一次表。
方案2:用UNPIVOT实现简洁的列转行
如果你的列数据类型一致,而且不需要额外的过滤逻辑,UNPIVOT是更简洁的选择,同样只做一次全表扫描。
示例代码:
WITH cte AS ( SELECT volume, description FROM dbo.tbl1 UNPIVOT ( volume FOR description IN ( A AS 'Column A', B AS 'Column B', C AS 'Column C' -- 继续添加其他列和对应的描述 ) ) AS unpvt ) SELECT * FROM cte;
UNPIVOT是SQL Server专门为列转行设计的运算符,它会直接把指定的列转换成行,底层执行计划里只会有一次对tbl1的扫描,性能比一堆UNION ALL高太多。
为什么这俩方案能解决问题?
原来的UNION ALL写法里,每个子查询都是独立的,SQL Server会分别执行每个SELECT,也就是每次都扫一遍表;而上面的两种方案都是先一次性读取全表数据,再在内存里完成列到行的转换,从根本上避免了多次全表扫描的问题。
你可以自己对比下执行计划,就能明显看到扫描次数的差异了!
内容的提问来源于stack exchange,提问作者Yuhan Shao
相关产品推荐
相关产品推荐

