FROM句がなく、エラーにならないのはなぜですか?


9

そのため、タイプミスのあるサブクエリを含むクエリがあります。FROM句がありません。しかし、実行してもエラーにはなりません!なぜ!?


SELECT

    1
   ,r.id
   ,'0D4133BE-C1B5-4141-AFAD-B171A2CCCE56'
   ,GETDATE()
   ,1
   ,'Y'
   ,'N'
   ,oldItem.can_view
   ,oldItem.can_update

FROM Role r

JOIN RoleObject oldReport
    ON r.customer_id = oldReport.customer_id

JOIN RoleItem oldItem
    ON oldReport.id = oldItem.role_object_id
        AND r.id = oldItem.role_id

WHERE r.id NOT IN (SELECT
        role_id
    WHERE role_object_id = '0D4133BE-C1B5-4141-AFAD-B171A2CCCE56')

AND oldReport.id = '169BA22F-1614-4EBA-AF45-18E333C54C6C'

回答:


21

このステートメントは合法です(つまり、FROM必要ありません)。

SELECT x = 1;
SELECT x = 1 WHERE 1 = 1; -- also try WHERE 1 = 0;

トリックは、明らかに存在できない列名を導入するときです。したがって、これらは失敗します:

SELECT name WHERE 1 = 1;

SELECT x = 1 WHERE id > 0;

メッセージ207、レベル16、状態1
無効な列名 'name'。
メッセージ207、レベル16、状態1
無効な列名 'id'。

ただし、無効な列がサブクエリのようなものに導入された場合、SQL Serverがサブクエリの内部スコープでその列を見つけられなかった場合のSQL Serverの動作は、外部スコープに移動し、サブクエリをその外部スコープに関連付けます。これにより、すべての行が返されます。次に例を示します。

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE 1 = 1);

それは本質的に言っているので:

SELECT * FROM sys.columns WHERE name IN (SELECT sys.columns.name WHERE 1 = 1); /*
              ^^^^^^^^^^^                       -----------
                   |                                 |
                   -----------------------------------    */

WHEREサブクエリに句も必要ありません。

SELECT * FROM sys.columns WHERE name IN (SELECT name);

次の理由により、実際は外側のスコープテーブルを参照していることがわかります。

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE name > N'x');

はるかに少ない行を返します(私のシステムでは11)。

これには、スコーピングに関する標準への準拠が含まれます。#tempテーブルが2つある場合、同様のことがわかります。

CREATE TABLE #foo(foo int);
CREATE TABLE #bar(bar int);

SELECT foo FROM #foo WHERE foo IN (SELECT foo FROM #bar);

何もありませんので、明らかに、これは、右、エラーすべきfooでは#bar?いいえ。何が起こるのかというと、SQL Serverは、「ああ、私はfooここを見つけられませんでした。もう一方を意味しているに違いありません」と言っています。

また、一般的には避けNOT INます。NOT EXISTS一部のシナリオではより効率的になる可能性がありますが、さらに重要なことに、ターゲット列が可能である可能性がある場合、その動作は変更されませんNULL詳細については、この投稿を参照してください


Stack Overflowに質問をしましたが、その答えは基本的にこれと同じです(ただし、あなたの答えはより完全です)。クエリされるテーブルの一部ではない列を(左側のオペランドとして)参照しているのは、EXISTS演算子のエラーではないのはなぜですか?
Marc.2377

2

これを2016年に簡略化した例で再現しました。

declare @t1 table (c1 int, c2 int, c3 int)
insert into @t1 values (1,2,3), (2,3,4), (3,4,5)

select * from @t1
where
    c1 not in 
    (select c2 where c3 = 3)

各行でc2とc3が評価されているようです。


1

SQL ServerのSELECT構文では、FROMセクションは必要ありません。FROMを省略した場合、selectステートメントは1行で1列の「ダミー」テーブルを使用します。そう

select 'x' as c where ...

式が真の場合は1行を返し、偽の場合は行を返しません。


しかし、あなたが言うだけで、いくつかの外部オブジェクトに存在しない場合、select cそれcは機能しません。これFROMは必須ではないことに同意ますが、外部スコープに存在する列に明示的に名前を付ける場合のここでの仕組みは、ダミーテーブルとは明らかに異なり、必要ない列に定数を指定しない場合存在するとランタイムエラーが発生するため、ダミーテーブルもありません。ダミーテーブルは他のシナリオでも機能しますが、参照がサブクエリ/派生テーブルにある場合は機能しません。
アーロンバートランド

あなたの例では、それは相関副選択であり、role_idとrole_object_idは外部選択のテーブルの1つに属しています。
Piotr

そうですが、言うことSELECT 'x' AS cは、先ほど言ったOP とはまったく異なるシナリオですSELECT cサブクエリ/派生テーブル内。
アーロンバートランド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.