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 を動的に生成しているため、コードを変更する必要はありません。

もう 1 つの修正方法は、データベースにダム行を追加することですが、これは面倒です。

PS メイン テーブルは、クライアントが検索するアセットを記録するアセット管理システムのセッション テーブルです。ルックアップにはさまざまな種類があり、種類ごとに個別の子テーブルが取得されます。さらに、検索可能なセッションのキーと値のペアの属性子テーブルがあります。

4

3 に答える 3

4

child2に行がない場合、child2テーブルへの内部結合のため、クエリは行を返しません。行のないテーブルに内部結合した場合、結果は得られません。child2が空のときに結果を取得したい場合は、代わりにchild2に外部結合する必要があります。

child2に行がある場合、クエリが結果を返す理由は、where句が原因です。

どこ
  child1.name ='child1' OR
  child2.name='存在しません';

内部結合は、一致するIDを持つ何かがchild2に存在する必要があることを示していますが、where句にはORが含まれているため、child1.name='child1'であるという理由だけで結果が得られます。その後、データベースはchild2テーブルをわざわざ見る必要はありません。

それを修正するには:

何らかの条件が満たされた場合にのみ子行を返したいという予感があります。次のように、両方に外部結合し、追加の条件をwhere句からjoin句に移動する必要があります。

選択する
  主要。*
から
  主要
左アウタージョイン
  child1
オン
  main.id = child1.main_id
  AND child1.name ='child1'
左アウタージョイン
  child2
オン
  main.id = child2.main_id
  AND child2.name='何でも'
  • 外部結合は、1つのテーブルが空であっても結果を得る可能性があることを意味します。

  • 追加の条件(child1.name = ...)をWHERE句から外部結合に移動すると、条件がtrueの場合にのみテーブル情報を取得できます。(これはあなたがやろうとしていることかもしれないと思いますが、そうではないかもしれません。その場合、最初に条件を持っていたWHERE句に条件を残してください。)

于 2009-05-09T08:15:19.550 に答える
2

内部結合を使用しているため、何も返されません。

内部結合を左結合に変更します

于 2009-05-09T08:18:00.370 に答える
2

INNER JOINと言うときは、結合の両側に結果がある行を返すようにクエリに要求しています。これは、一致する子行がない行が削除されることを意味します。

探しているのはLEFTJOINのようです。これには、右側(child1、child2)に一致するエントリがない場合でも、結合(main)の左側にあるすべての行が含まれます。

これは標準的な動作であり、SQLに精通していない人にとっては非常に一般的な問題です。 ウィキペディアにはすべての詳細があります。そうでない場合、グーグルですばやく検索するとたくさんの結果が表示されます。

于 2009-05-09T08:18:00.853 に答える