【发布时间】:2020-03-16 19:07:43
【问题描述】:
我正在编写一些代码来查询一些 DMV。 DMV 中可能存在也可能不存在某些列,具体取决于 SQL 版本。我在网上找到了一个有趣的建议,如何使用 CROSS APPLY 跳过特定检查。
下面的查询是为可能丢失的列读取 DMV 的代码示例。代码为该列创建一个默认值,并使用CROSS APPLY 从 DMV 中提取实际列(如果存在)。
代码尝试提取的列 BogusColumn 不存在。我希望下面的查询会生成有关无效列名的错误......但事实并非如此。它返回 NULL 而不会出错。
为什么下面的 CROSS APPLY 子句不会导致“列名无效”错误?
declare @x int
select @x = b.BogusColumn
from
(
select cast(null as int) as BogusColumn
) a
cross apply
(
select BogusColumn from sys.dm_exec_sessions
) b;
select @x;
如果我在CROSS APPLY 中单独运行查询:
select BogusColumn from sys.dm_exec_sessions;
我收到关于无效列名的预期错误:
Msg 207, Level 16, State 1, Line 9
Invalid column name 'BogusColumn'.
如果我将 DMV 列名更改为 BogusColumn2 以使其唯一,则会收到预期的列名错误:
select a.BogusColumn1, b.BogusColumn2
from
(
select cast(null as int) as BogusColumn1
) a
cross apply
(
select BogusColumn2 from sys.dm_exec_sessions
) b
我已经在 SQL 2012 到 SQL 2017 的版本上测试了这种行为,并且所有版本的行为都是一致的。
【问题讨论】:
-
虽然这种行为是可以预见的,但它也是一种极其卑鄙的黑客行为。无论是谁想出它,都值得称赞,也值得一记耳光,因为他引入了这样的维护陷阱。它只是在系统视图中覆盖版本差异是可以接受的,而且几乎没有其他作用。
-
我同意@JeroenMostert。为避免由于列分辨率的意外更改而导致的错误,请始终在列的 from 中使用表别名。我看到生产下降,因为有人在表格中添加了一个新列,导致类似的效果。
-
好问题!感谢@Piotr 提到列别名。我经常使用 APPLY,通常是嵌套的并且没有别名,事情很快就会变得很混乱。
-
我同意这既聪明又丑陋。我不想在生产代码中使用它,但我确实想用它来避免 DMV 中的版本控制问题。使用这种方法来分析服务器活动的 DBA 类型查询要简单得多,而不是我必须做的所有版本检查。
IF @MajorVersion >= @SQL2016 AND @MinorVersion >= @SQL2016SP1 BEGIN /* write and execute dynamic SQL, etc. */ END
标签: sql-server tsql cross-apply