3

以下のテーブル構造を参照してください。

CREATE TABLE Person (id int not null, PID INT NOT NULL, Name VARCHAR(50))
CREATE TABLE [Order] (OID INT NOT NULL, PID INT NOT NULL)

INSERT INTO Person VALUES (1,1,'Ian')
INSERT INTO Person VALUES (2,2,'Maria')
INSERT INTO [Order] values (1,1)

次のクエリが 2 つの結果を返すのはなぜですか。

select * from Person WHERE id IN (SELECT ID FROM [Order])

ID がオーダーに存在しません。上記のクエリで結果が生成されるのはなぜですか? 私は順番に存在しないので、エラーになると思います。

4

3 に答える 3

11

この動作は、直感的ではありませんが、Microsoft のナレッジ ベースで非常に明確に定義されています。

KB #298674 : PRB: サブクエリが列の名前を外部テーブルに解決する

その記事から:

動作を説明するために、次の 2 つのテーブル構造とクエリを使用します。

CREATE TABLE X1 (ColA INT, ColB INT)
CREATE TABLE X2 (ColC INT, ColD INT)
SELECT ColA FROM X1 WHERE ColA IN (Select ColB FROM X2)

クエリは、列 ColB がテーブル X1 から考慮される結果を返します。

列名を修飾することにより、次のクエリに示すようなエラー メッセージが表示されます。

SELECT ColA FROM X1 WHERE ColA in (Select X2.ColB FROM X2)

サーバー: メッセージ 207、レベル 16、状態 3、行 1
列名 'ColB' が無効です。

人々は何年もの間、この問題について不平を言ってきましたが、マイクロソフトはそれを修正するつもりはありません. 結局のところ、それは基本的に次のように述べている標準に準拠しています。

現在のスコープで列 x が見つからない場合は、参照が見つかるまで、次の外側のスコープにトラバースします。

次の接続の「バグ」の詳細と、この動作が設計によるものであり、変更されないという複数の公式の確認 (したがって、自分の動作を変更する必要があります。つまり、常にエイリアスを使用します)。

Connect #338468 : サブクエリでの CTE 列名の解決が検証されていない
Connect #735178 : IN 演算子を使用すると、T-SQL サブクエリが機能しない場合がある
Connect #302281 : 存在しない列によりサブクエリが無視される
Connect #772612 : エイリアス エラーIN 演算子
Connect 内では報告されない #265772 : sub select を使用するバグ

あなたの場合、ID、OID、および PID よりも意味のある名前を使用すると、この「エラー」が発生する可能性がはるかに低くなります。Order.PIDまたはを指していますPerson.idか?Person.PID人々があなたに尋ねなくてもリレーションシップを把握できるように、テーブルを設計します。Aは、スキーマ内のどこにあるかに関係なく、PersonID常にa である必要があります。PersonIDと同じOrderIDです。完全にあいまいなスキーマに対して数文字の入力を節約することは、良い代償ではありません。

EXISTS代わりに節を書くことができます:

... FROM dbo.Person AS p WHERE EXISTS 
(
  SELECT 1 FROM dbo.[Order] AS o
  WHERE o.PID = p.id -- or is it PID? See why it pays to be explicit?
);
于 2013-09-03T02:13:28.533 に答える