そのため、タイプミスのあるサブクエリを持つクエリがあります。 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'
このステートメントは合法です(つまり、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);
#bar
にfoo
がないため、これは間違いなくエラーになります。いいえ。 SQL Serverは、「ああ、ここでfoo
を見つけられませんでした。もう1つを意味しているに違いありません」と言います。
また、一般的にはNOT IN
は避けます。 NOT EXISTS
は一部のシナリオでより効率的になる可能性がありますが、より重要なのは、ターゲット列がNULL
である可能性がある場合でもその動作が変わらないことです。 詳細はこの投稿を参照してください 。
これを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が評価されているようです。
SQL ServerのSELECT構文では、FROMセクションは必要ありません。 FROMを省略した場合、selectステートメントは1行で1列の「ダミー」テーブルを使用します。そう
select 'x' as c where ...
式が真の場合は1行を返し、偽の場合は行を返しません。