2009-05-09 4 views
0

これは、実行中のクエリの簡略化されたバージョンで、子行が一致するメインの親テーブル内のすべての行を検索する必要があります。子テーブルの1つが空の場合、以下のクエリは結果を返しません。このクエリは、空ではない子テーブルの結果のみを返すのはなぜですか?

メインテーブルには2つのテーブルを持っています

CREATE TABLE main (id INT PRIMARY KEY, name VARCHAR(8)); 

CREATE TABLE child1(id INT PRIMARY KEY, main_id int, name VARCHAR(8)); 
ALTER TABLE child1 add constraint fk_child1_main foreign key (main_id) references main (id); 

CREATE TABLE child2(id INT PRIMARY KEY, main_id int, name VARCHAR(8)); 
ALTER TABLE child2 add constraint fk_child2_main foreign key (main_id) references main (id); 

INSERT INTO main (id, name) VALUES (1, 'main'); 
INSERT INTO child1 (id, main_id, name) VALUES (2, 1, 'child1'); 

ありchild2の中に行がありませんし、それが空である場合は、次のクエリは行が戻されない:行が追加され

SELECT 
    main.* 
FROM 
    main 
INNER JOIN 
    child1 
ON 
    main.id = child1.main_id 
INNER JOIN 
    child2 
ON 
    main.id = child2.main_id 
WHERE 
    child1.name = 'child1' OR 
    child2.name = 'DOES NOT EXIST'; 

場合child2は、WHERE句と一致しない場合でも、SELECTはメインテーブルの行を返します。

INSERT INTO child2 (id, main_id, name) VALUES (4, 1, 'child2'); 

私はこれをDerbyとSQLiteでテストしました。これはデータベースで一般的なものです。

これはなぜこのように動作しますか?

私はそれを修正するために何ができますか?

私は、UNION、別のSELECTに変えることができるが、それははるかに冗長だし、プラス、我々は動的SQLを生成していると私はむしろ私たちのコードを変更する必要はありませんと思います。

別の修正は、単にデータベースにダムの行を追加することですが、それは厄介です。

PSは、メインテーブルには、クライアントが見上げる資産を記録した資産管理システムにおけるセッションテーブルです。ルックアップにはさまざまなタイプがあり、それぞれのタイプには別々の子テーブルがあります。また、検索可能なセッションのキー/値ペアの属性子テーブルがあります。

答えて

4

child2のが行を持っていない場合は、クエリがあるため、内の行を戻さないがchild2のテーブルに参加します。行がないテーブルに内部結合すると、決して結果が得られません。つまり、child2が空の場合に結果を取得する場合は、child2に外部結合する必要があります。

child2の行を持っている場合は、クエリの結果が返された理由が原因WHERE句は次のとおりです。

 
WHERE 
    child1.name = 'child1' OR 
    child2.name = 'DOES NOT EXIST'; 

内部結合が一致したIDとchild2の中に何かがなければならないと言うが、どこ句にはORが含まれているので、child1.name = 'child1'なので結果が得られます。その後、データベースはchild2テーブルを調べる必要はありません。それを修正する

私はあなただけのいくつかの条件が満たされたときに子行を返したい勘を持っています。あなたはそれらの両方に外部結合、そしておそらくまた、このように、JOIN句にwhere句から、あなたの余分な条件を移動する必要があります:あなたが取得した結果のチャンスを持っているわけ外部結合

 
SELECT 
    main.* 
FROM 
    main 
LEFT OUTER JOIN 
    child1 
ON 
    main.id = child1.main_id 
    AND child1.name = 'child1' 
LEFT OUTER JOIN 
    child2 
ON 
    main.id = child2.main_id 
    AND child2.name = 'whatever' 
  • 1つのテーブルが空であっても

  • WHERE句から外部結合への余分な条件(child1.name = ...)の移動は、条件が真である場合にのみテーブル情報を取得することを意味します。(私はこれがあなたがやろうとしますが、そうでないかもしれない、その場合には、あなたがもともとそれらを持っていたWHERE句の条件を残しているものかもしれないと思う。)それは何も返さないだ

2

あなたは内部結合を使用しているので、 。

は、あなたの内を変更

左に加入しますがINNERあなたが参加の両側に結果を持っている行を返すために、クエリを求めているのJOIN言うとき

+0

次に、一致しない行をchild2に追加するとクエリの結果が変更されるのはなぜですか?このステートメントによれば、一致しない行をchild2に追加した後でも、クエリはまだ行を返さないはずです。 –

+1

これは、child2.nameが一致しなくても、クエリが結果を返さないようにするmain.id = child2.main_idだと分かります。私はこの質問の部分を無視していました。 –

2

に参加します。つまり、一致する子行を持たない行はすべて削除されます。

LEFT JOINは、右側(child1、child2)に一致するエントリがなくても、結合(メイン)の左側にあるすべての行を含むLEFT JOINです。 。

これは標準的な動作であり、SQLに精通していない人にとっては非常に一般的な問題です。 Wikipediaにはすべての詳細が含まれています。そうでなければ、迅速なGoogle searchが多くの結果をもたらします。

+0

次に、一致しない行をchild2に追加すると、クエリの結果が変更されるのはなぜですか?このステートメントによれば、一致しない行をchild2に追加した後でも、クエリはまだ行を返さないはずです。 –

+0

これは、child2.nameが一致しなくても、クエリが結果を返さないmain.id = child2.main_idだと分かります。私はこの質問の部分を無視していました。 –

関連する問題