2011-07-19 14 views
8

テーブルAがテーブルBと一対多の関係にあるMySQLデータベースを持っていて、テーブル内に子を持たないすべての行を選択したいA.私は、これらの両方が遅いように見える子供がいない場合のみ親行を選択

SELECT id FROM A WHERE NOT EXISTS (SELECT * FROM B WHERE B.id=A.id) 

SELECT id FROM A LEFT JOIN B ON A.id=B.id WHERE B.id IS NULL 

を使用して試してみました。同じことを達成するためのより高速なクエリがありますか?

この場合、データベーステーブルAには約500,000行あり、テーブルBには約300万〜400万行あります。

編集:私のデータベース内の実際の表の、説明は私に与える:

select number from frontend_form471 where not exists (select * from SchoolData where SchoolData.`f471 Application Number`=frontend_form471.number) 

+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+ 
| id | select_type | table   | type | possible_keys | key      | key_len | ref | rows | Extra           | 
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+ 
| 1 | SIMPLE  | frontend_form471 | index | NULL   | frontend_form471_61a633e8 | 32  | NULL | 671927 | Using index; Using temporary     | 
| 1 | SIMPLE  | SchoolData  | index | PRIMARY  | PRIMARY     | 49  | NULL | 3121110 | Using where; Using index; Not exists; Distinct | 
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+ 

ため

+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+ 
| id | select_type  | table   | type | possible_keys | key      | key_len | ref | rows | Extra     | 
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+ 
| 1 | PRIMARY   | frontend_form471 | index | NULL   | frontend_form471_61a633e8 | 32  | NULL | 671927 | Using where; Using index | 
| 2 | DEPENDENT SUBQUERY | SchoolData  | index | PRIMARY  | PRIMARY     | 49  | NULL | 3121110 | Using where; Using index | 
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+ 

私の場合frontend_form471テーブルAとSchoolDataは

EDIT2テーブルBである場合:私のデータベース内のテーブルB(SchoolData)で、IDが2つの部分、主キーの最初の部分であるので、それは同じidを持つBには複数のエントリがまだ存在します。

+0

'EXPLAIN SELECT id from LEFT JOIN B ON A.id = B.id WHERE B.id IS NULL '両方のクエリに対してEXPLAINの結果を投稿できますか? – Igor

+0

インデックスは役に立ちませんか? – Londeren

+0

'COUNT(*)= 0'より速く選択していますか? –

答えて

8
SELECT id FROM A LEFT OUTER JOIN B ON A.id=B.id WHERE B.id IS NULL 

これは可能です。外部結合は少しのパフォーマンスをもたらすはずですが、それほど多くはありません。

新しいデータベースシステムではおそらくクエリが最適化されるため、違いはありません。

ここで正しい方法はキャッシングです!可能であれば、クエリcacherとアプリケーションレベルのキャッシングを試してください。

もちろん、適切なインデックスが必要です。

、それは本当にこのダウンを遅らせるかを確認するには、クエリの前に説明入れてみてください対数

を持つすべての木に比較ですにおける静的ルックアップ時間を持つことになりますよう、適切で私はテーブルと、好ましくは、ハッシュインデックスの両方に意味。

本当にこれが高速である必要がある場合は、データ構造を再構築することができます。

テーブルAにフラグをマークするトリガーを作成して、テーブルbeに対応するエントリがあるかどうかを判断できます。もちろん、このIDデータの冗長性が、時にはその価値があります。それをキャッシングと考えるだけです。

最後に考えたこと:試してみることができますSELECT id FROM A WHERE id NOT IN (SELECT id FROM B)実際の結合が必要ないため少し速くなる可能性がありますが、beのセットの検索がフルスキャンになるため遅くなることもあります。どのように処理されるのかは分かりませんが、試してみる価値があります。

+0

これは最良の解決策です。一致するかどうかは分かりませんが、存在しない場合はレコードを返すだけです。親テーブルを1サイクル...過去にも同様にアプローチしています。 – DRapp

+2

MySQLだけがこれを持っています:他のエンジンは存在しない方が良いですhttp://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/ – gbn

+0

あなたが作った最も重要なポイントは、ハッシュインデックスです。できればそれを使用しますが、InnoDBはそれらをサポートしていないので、エンジンを切り替えてこのクエリを動作させる準備はできていません。 – murgatroid99

1

どのように見ても、それは遅くなるでしょう。最悪の場合のパフォーマンスは、2兆の潜在的なマッチ(4ミル* 500k)を生み出すフルクロス・ジョインになります。

2つ目のクエリは、単一のクエリであるため、パフォーマンスが向上します。

1

あなたは

SELECT id FROM A WHERE A.id NOT IN (SELECT id FROM B) 

を試みることができるが、これはどの速くなるかどうかはわかりません。私は最初に左の結合を試みただろう。私はあなたの問題がインデックスともっと関係していると思います。両方のidフィールドにインデックスがありますか?

0

インデックスはA.idに、もう1つはB.idに設定してください。

A.idとB.idを結びつけるのはちょっと変わったようです。 B.idはAの外部キーですか、それともBの主キーですか?

+0

B.idは、Aの外部キーと2列の主キーの半分です。 – murgatroid99

+0

それは問題ですか?もちろんmabyのデータ構造を利用することができます。 –

+0

結合がOKであることを確認したかっただけです。 – phlogratos

1

インデックス作成に問題があります。すべての形態のために

(IN、LEFT JOINを、EXISTS)スキーマは、このようなものである場合は、両方テーブル

+0

id-sはPKのように見えますので、クエリは高速でなければなりません。 – Igor

+0

@Igor:子テーブルがそれ自身のサロゲート(ここでは使用されず、IDはFK列n)またはIDが複合キーの一部です。 1:1の関係でない限り、両側で正しいインデックスを仮定することはできません。 – gbn

+0

B.に同じIDを持つ行が多数あるため、B.idは間違いなくPKではありません。 – phlogratos

0

にIDにインデックスを持つ必要があります:あなたはすべての場合

CREATE TABLE b(
    id int, 
    value varchar(255) 
) 

CREATE TABLE a(
    id int, 
    father_id int, 
    value varchar(255) 
) 

テーブルAの子を持たないテーブルAの行。

SELECT * FROM B WHERE id NOT IN (SELECT father_id FROM A GROUP BY father_id) 

私はテストしていませんが、のフェスター。これが空の値の代わりにNULLをしようとしないのはなぜ

0

を役に立てば幸い

IDの上にインデックスを置くことを忘れないでください。 SQLでは、NULL値は他の値(NULLも含む)と比較して決して真ではありません。 NULLを含む式は、式に含まれる演算子および関数のドキュメントで特に明記されていないかぎり、常にNULL値を生成します。

関連する問題