2つのテーブルtable_a(id、name)とtable_b(id)があります。Oracle12cについて考えてみましょう。
このクエリが例外を返さないのはなぜですか?
select * from table_a where name in (select name from table_b);
私が理解していることから、オラクルはこれを
select * from table_a where name = name;
しかし、私が得られないのはなぜですか?
table_b
にname
列がない場合でも、クエリは構文的に正しいSQLです。その理由は、スコープの解決です。
クエリが解析されると、最初にtable_b
にname
列があるかどうかがチェックされます。ないので、table_a
がチェックされます。どちらのテーブルにもname
列がない場合にのみ、エラーがスローされます。
最後に、クエリは次のように実行されます。
select a.*
from table_a a
where a.name in (select a.name
from table_b b
);
クエリが返す結果については、table_a
のすべての行について、サブクエリ(select name from table_b)
-または(select a.name from table_b b)
-は、同じa.name
値を持つ単一の列を持つテーブルですtable_b
と同じ行数。したがって、table_b
に1つ以上の行がある場合、クエリは次のように実行されます。
select a.*
from table_a a
where a.name in (a.name, a.name, ..., a.name) ;
または:
select a.*
from table_a a
where a.name = a.name ;
または:
select a.*
from table_a a
where a.name is not null ;
table_b
が空の場合、クエリは行を返しません(その可能性を示すために@ughaiにthnx)。
これ(エラーが発生しないという事実)は、すべての列参照の前にテーブル名/エイリアスを付ける必要がある最も良い理由です。クエリが次の場合:
select a.* from table_a where a.name in (select b.name from table_b);
すぐにエラーが発生します。テーブルプレフィックスが省略されている場合、特により複雑なクエリでそのようなミスが発生することは難しくありません。
Oracle docs:Static SQL Statementsでの名前の解決Inner captureの同様の例B-6と推奨事項も読んでくださいSELECTおよびDMLステートメント段落での内部キャプチャの回避:
ステートメントの各列参照を適切なテーブルエイリアスで修飾します。
なぜなら
ネストされたサブクエリが、サブクエリの1レベル上の親ステートメントを参照するテーブルの列を参照する場合、Oracleは相関サブクエリを実行します。 http://docs.Oracle.com/cd/E11882_01/server.112/e41084/queries007.htm#SQLRF52357
これは、サブクエリが相関しているかどうかを判断するために、Oraclemustが外部ステートメントコンテキストを含むサブクエリの名前も解決しようとすることを意味します。そして、接頭辞のないname
の場合、それが可能な唯一の解決策です。
table_b
にはname
フィールドがないため、Oracleはtable_a
からフィールドを取得します。私はEXPLAIN PLAN
を試しましたが、TABLE ACCESS
FULL
があることだけがわかりました。これにより、両方のテーブル間にある種のデカルト積が生成され、その結果、table_a
内のすべての名前のリストがサブクエリによって返されると思います。