CROSS APPLYがこのクエリで無効な列エラーを取得しないのはなぜですか?


8

一部のDMVをクエリするためのコードを書いています。SQLのバージョンによっては、一部の列がDMVに存在する場合と存在しない場合があります。を使用して特定のチェックをスキップする方法をオンラインで興味深い提案が見つかりました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のバージョンでこの動作をテストしましたが、動作はすべてのバージョンで一貫しています。


5
この動作は予測可能ですが、非常に巧妙なハックでもあります。それを思いついた人は両方の賞賛に値し、このようなメンテナンストラップを導入した手首に平手打ちをかけます。それはだだけで他には何のためのシステムビューのバージョンの違いを覆う程度許容し、ちょうど約。
Jeroen Mostert

3
@JeroenMostertに同意します。列の解決における予期しない変更によって引き起こされるエラーを回避するために、常に列のテーブルエイリアスを使用してください。誰かが新しい列をテーブルに追加して同様の効果を引き起こしたため、生産がダウンしているのがわかりました。
Piotr

1
素晴らしい質問です!そして、列のエイリアスの言及について@Piotrを称賛します。私はAPPLYを頻繁に使用します。多くの場合、入れ子になっており、エイリアスがないと、混乱が生じやすくなります。
アランバースタイン

これは賢いことであり、醜いハックでもあることに同意します。これを本番コードで使用したくありませんが、DMVのバージョン管理の問題を回避するために使用したいと思います。サーバーアクティビティを分析するためのDBAタイプのクエリは、この方法を使用すると、他の方法で行う必要があるすべてのバージョンチェックの代わりに、はるかに簡単になります。IF @MajorVersion >= @SQL2016 AND @MinorVersion >= @SQL2016SP1 BEGIN /* write and execute dynamic SQL, etc. */ END
ポールウィリアムズ

回答:


7

BogusColumnは、最初のクエリで有効な列として定義されています。

クロス適用を適用する場合、次のように列解決を使用します
。1. 2番目のクエリ(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;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.