【问题标题】:Why does CROSS APPLY *not* get an invalid column error in this query?为什么 CROSS APPLY *not* 在此查询中出现无效列错误?
【发布时间】: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


【解决方案1】:

BogusColumn 被定义为第一个查询中的有效列。

当我们应用交叉应用时,它使用的列分辨率如下:
1. 它在第二个查询 (dmv) 中查找列“BogusColumn”
2.如果dmv中存在该列,则解析为dmv
3.如果dmv中不存在该列,它将在外部查询(顶部)中查找该列并使用那里提供的值。

换句话说,当视图中未定义虚假列时,最终查询将按以下方式工作:

select * from
(
    select cast(null as int) as BogusColumn
) a
cross apply
(
    select a.BogusColumn AS BogusColumn from sys.dm_exec_sessions
) b;

如果已定义,查询将解析为:

select * from
(
    select cast(null as int) as BogusColumn
) a
cross apply
(
    select s.BogusColumn AS BogusColumn from sys.dm_exec_sessions as s
) b;

【讨论】:

    猜你喜欢
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多