2009-03-10 8 views
4

概要:左の結合は私が望むものですが、それらは非常に遅いですか?

Iは、1)加入者、BIOS、およびshirtsizes 3つのテーブルを持っていると私はバイオのない加入者を見つける必要があるかのテーブルが

加入者ようなレイアウトされて

shirtsizes

| season_id | user_id | 

バイオ

| bio_id | user_id | 

なしバイオた場合(、シャツは

| bio_id | shirtsize | 

のサイズと私はバイオやshirtsizeを持っていないすべてのユーザーを見つける必要があります。特定のシーズンの関係を介してshirtsizeしないでください)。

私はもともとのようなクエリを書いた:

SELECT * 
    FROM subscribers s 
    LEFT JOIN bio b ON b.user_id = subscribers.user_id 
    LEFT JOIN shirtsizes ON shirtsize.bio_id = bio.bio_id 
WHERE s.season_id = 185181 AND (bio.bio_id IS NULL OR shirtsize.size IS NULL); 

今完了するまでに10秒を取っています。

私は、それが合理的にプリフォームされるように、クエリ(または場合によっては問題)をどのように再構築できるか疑問に思っています。ここで

は、mysqlは説明している:(OGUの加入者を=、B =バイオ、TN = shirtshize)

| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+-------------+--------+-------------+  
| 1 | SIMPLE  | ogu | ref | PRIMARY  | PRIMARY | 4  | const  | 133 | Using where | 
| 1 | SIMPLE  | b  | index | NULL   | PRIMARY | 8  | NULL  | 187644 | Using index | 
| 1 | SIMPLE  | tn | ref | nid   | nid  | 4  | waka2.b.nid |  1 | Using where | 

上記かなりサニタイズされ、ここでrealz情報です:

mysql> DESCRIBE subscribers 
+-----------+---------+------+-----+---------+-------+ 
| Field  | Type | Null | Key | Default | Extra | 
+-----------+---------+------+-----+---------+-------+ 
| subscribers | int(11) | NO | PRI |   |  | 
| uid  | int(11) | NO | PRI |   |  | 


mysql> DESCRIBE bio; 
+-------+------------------+------+-----+---------+-------+ 
| Field | Type    | Null | Key | Default | Extra | 
+-------+------------------+------+-----+---------+-------+ 
| bio_id | int(10) unsigned | NO | PRI | 0  |  | 
| uid | int(10) unsigned | NO | PRI | 0  |  | 


mysql> DESCRIBE shirtsize; 
+-------+------------------+------+-----+---------+-------+ 
| Field | Type    | Null | Key | Default | Extra | 
+-------+------------------+------+-----+---------+-------+ 
| bio_id | int(10) unsigned | NO | PRI | 0  |  | 
| shirtsize | int(10) unsigned | NO | PRI | 0  |  | 

と実際のクエリは次のようになります。

SELECT ogu.nid, ogu.is_active, ogu.uid, b.nid AS bio_node, tn.nid AS size 
        FROM og_uid ogu 
        LEFT JOIN bio b ON b.uid = ogu.uid 
        LEFT JOIN term_node tn ON tn.nid = b.nid 
        WHERE ogu.nid = 185033 AND ogu.is_admin = 0 
        AND (b.nid IS NULL OR tn.tid IS NULL) 

nidはseason_idまたはbio_id(型付き)です。 term_nodeはshirtsizeになります

+0

これらのテーブルにはインデックスがありますか? –

+0

@jskulksi:各テーブルに「SHOW CREATE TABLE」の出力を含めることができますか? –

答えて

11

クエリはOKです。私はそれをクエリアナライザで実行し、テーブルのインデックスを絞り込みます。

+0

これは、私が貢献したモジュールが正しいインデックスを持つことを信頼するために得たものだと思います。ありがとう! – jskulski

1

SELECT *ではなく、正確に何を探しているのかを定義すると、少し高速化する可能性があります。ORを使用せずに書き直すことができれば、ORは最速のクエリではありませんより速くなる。

また...あなたはおそらく結合を左にする代わりに結合を試みることができますか?

 
(SELECT s.user_id FROM subscribers s WHERE s.season_id = 185181) 
UNION 
(SELECT b.user_id, b.bio_id FROM bio b WHERE bio.bio_id IS NULL) 
UNION 
(SELECT shirtsizes.bio_id FROM shirtsizes WHERE shirtsizes.size is NULL) 

(私に右見ていないこと正直に言うと...しかし、私は、構文や労働組合に加入 参加するか を使用することはありません...:

 
SELECT s.user_id 
    FROM subscribers s 
    LEFT JOIN bio b ON b.user_id = s.user_id 
    LEFT JOIN shirtsizes ON shirtsize.bio_id = bio.bio_id 
WHERE s.season_id = 185181 AND (bio.bio_id IS NULL OR shirtsize.size IS NULL); 

は何かのようになります)

私はどうなる

 
SELECT * 
FROM subscribers s, bio b, shirtsizes sh 
WHERE s.season_id = 185181 
AND shirtsize.bio_id = bio.bio_id 
AND b.user_id = s.user_id 
AND (bio.bio_id IS NULL 
    OR 
    shirtsize.size IS NULL); 
+2

"私は決して結合または結合を使用しません"もちろん結合を使用します。加入者Sから 、バイオbは、s.season_id = 185181 AND shirtsize.bio_id = bio.bio_id AND b.user_id = s.user_id ジョインの集合であるSH をshirtsizes。これは古いスタイルの結合を使用していますが、依然として結合です。 – HLGEM

+0

「私は結合構文の使用を避け、私は組合を使わない」と書いた方が正しいでしょう。 – SeanJA

10

参加を使用すると、SQLクエリに対して実行できる最も高価な業務の一つです。クエリを自動的に最適化できるはずですが、おそらくそれを再構築してみてください。まず、SELECT *の代わりに、どの列から必要なリレーションを指定するのかを確認してください。これは物事をかなりスピードアップします。

あなただけ例えば、ユーザIDが必要な場合:

SELECT s.user_id 
    FROM subscribers s 
    LEFT JOIN bio b ON b.user_id = subscribers.user_id 
    LEFT JOIN shirtsizes ON shirtsize.bio_id = bio.bio_id 
WHERE s.season_id = 185181 AND (bio.bio_id IS NULL OR shirtsize.size IS NULL); 

SQLデータベースは、自分自身でもう少し効率的にクエリを再構築することができます。

+1

通常の結合は適度に高価です。外部結合はより高価です。典型的な相関サブクエリは悪魔的です。 –

+0

私は実際のクエリを掲示し、いくつかの列を取得するだけですが、ありがとう。 – jskulski

1

bio_idはバイオマスの主キーですか? b.user_id = subscribers.user_idとなっているが、b.bio_idがNULLのBIOS行があるのは本当ですか?

shirtsize.bio_idのシャツサイズの行はNULLですか?これらの行はNULLでないshirtsize.sizeを持っていますか?

4

私はこれをチェックしていませんが、あなたが望むのは、一致するバイオがないか、バイオとシャツの間の結合が失敗した加入者を選択することです。私はこの条件のためにNOT EXISTSを使用することを検討するでしょう。おそらく、bio.user_idとshirtsizes.bio_idのインデックスが必要になります。

select * 
from subscribers 
where s.season_id = 185181 
     and not exists (select * 
         from bio join shirtsizes on bio.bio_id = shirtsizes.bio_id 
         where bio.user_id = subscribers.user_id) 

EDIT:あなたの更新に基づいて

、あなたは複合主キーを有することに加えて、個別の各列のキーの代わりに/を作成することもできます。結合が複合主索引を最適に活用できない可能性があり、結合列自体の索引が高速化する可能性があります。

+1

かなりの人がこれを調査して、SELECT *が通常EXISTSクエリのSELECT 1/SELECT NULL /などよりも少し速いことが分かります。しかし、はい、EXISTSを使用すると参加するよりも速くなることがよくありますが、必ずしもそうではありません。それぞれのシナリオで試すのは武器です。 – MatBailie

+1

ポイントを獲得しました。私は答えを更新しました。 – tvanfosson

1

関連する季節の購読者のリストと、ビオスとシャツサイズの季節の購読者のリストとの間に違いはありますか?

SELECT * 
    FROM Subscribers 
    WHERE season_id = 185181 
    AND user_id NOT IN 
     (SELECT DISTINCT s.user_id 
      FROM subscribers s 
      JOIN bios b ON s.user_id = b.user_id 
      JOIN shirtsizes z ON b.bio_id = z.bio_id 
      WHERE s.season_id = 185181 
     ) 

これにより、内部結合ほど速くない外部結合が回避されるため、処理が高速になる可能性があります。他方では、2つの大きなリストを作成している可能性があります。サブクエリのDISTINCTがパフォーマンスを向上させるか、またはパフォーマンスを低下させるかは明確ではありません。これはソート操作(高価)を意味しますが、MySQLオプティマイザがそのようなことをサポートしている場合、マージ結合の道を開くことになります。

たとえば、MINUSまたはDIFFERENCEなどの他の表記が使用できる場合があります。

あなたは完全にする必要がありますことを意味し、(とにかく、それは選択ではない場合、それはかなり無意味であるインデックス化)私はあなたの「大きなテーブルは、」加入者であることを想定し、そのseason_idはおそらく選択もインデックス化もない
+0

サブクエリでDISTINCTを使用する理由はありません。 –

0
select * from subscribers where user_id not in (
    select user_id from bio where bio_id not in (
    select bio_id from shirt_sizes 
) 
) and season_id=185181 
0

とにかく、サブスクライバをスキャンします。 Partingでは、私は(内部結合で)2つの他のテーブルに参加します - shirt_sizeにbio_idがない場合、bioがない場合とまったく同じです。 まずビット:あなたはshirtsizesがbio_idにインデックス化されていることを確認したい

select uid 
from bio 
    inner join shirtsizes 
      on shirtsizes.bio_id = bio.bio_id 

た時点で。今 あなたが加入者にこのクエリの外部結合左ことができます。

select * 
from subscribers s 
    left outer join (select uid 
         from bio 
         inner join shirtsizes 
           on shirtsizes.bio_id = bio.bio_id) x 
        on x.uid = s.uid 
where s.season_id = 185181 
    and x.uid is null 

適度に高速でもないバイオもshirtsizesが巨大である場合に実行する可能性がある...

0

あなたのクエリ、それが今で書かれているとして、評価さすべてbioterm_nodeが存在する場合は、それらをフィルタリングして除外します。

しかし、あなただけの

(もterm_nodeを持っていない暗示bioを持っていない)■og_uid「のterm_nodeありませんS」を見つけるされたいので、あなただけのbio年代とterm_nodeを評価停止したいですさんとすぐに見つけるように第1のterm_node既存:

SELECT * 
FROM (
     SELECT ogu.nid, ogu.is_active, ogu.uid, 
       (
       SELECT 1 
       FROM bio b, term_node tn 
       WHERE b.uid = ogu.uid 
         AND tn.nid = b.nid 
       LIMIT 1 
       ) AS ex 
     FROM og_uid ogu 
     WHERE ogu.nid = 185033 
       AND ogu.is_admin = 0 
     ) ogu1 
WHERE ex IS NULL 

これは、最大で1 bio、各og_uidについて最大で1つのterm_nodeを評価し、既存のすべての千を評価し、それらをフィルタリングするのではなく、

はるかに速く動作するはずです。

関連する問題