web-dev-qa-db-ja.com

SQLクエリが複数のテーブルを結合する-遅すぎる(8テーブル)

私は他のアプリケーションが使用するインデックスを作成するために8つのテーブルを1つに結合しようとしています、私のクエリは次のとおりです:(mysqlスキルは非常にアマチュアです)

SELECT t1_id, t2_name, t3_name, t4_name, t5_name, 
       t6_name, t7_name, t8_name, t9_name 
FROM t1 
  LEFT JOIN t2 ON (t1_id = t2_id) 
  LEFT JOIN t3 ON (t3_id = t1_id) 
  LEFT JOIN t4 ON (t4_id = t1_id)
  LEFT JOIN t5 ON (t5_id = t1_id)
  LEFT JOIN t6 ON (t6_id = t1_id) 
  LEFT JOIN t7 ON (t7_id = t1_id)
  LEFT JOIN t8 ON (t8_id = t1_id)
  LEFT JOIN t9 ON (t9_id = t1_id)

クエリを実行してもクエリ結果が表示されません。高速化する方法はありますか? :)あらゆる種類のヘルプを歓迎しますが、1つのクエリ(アプリケーションルール以外)のみにする方がよい

前もって感謝します

33
lordlinier

すべてのidフィールドがインデックス付けされた大きなテーブルに結合するいくつかのルックアップテーブルで同様の問題が発生しました。クエリの実行時の結合の影響を監視するために、クエリを数回(最初の100行に制限して)実行し、毎回追加のテーブルに結合を追加しました。 12個のテーブルを結合した後、クエリの実行時間に大きな変化はありませんでした。 13番目のテーブルに参加するまでに、実行時間は1秒にジャンプしました。 14番目の表4秒、15番目の表20秒、16番目の表90秒。

結合の代わりに相関サブクエリを使用するというKeijroの提案.

SELECT t1_id, 
        (select t2_name from t2 where t1_id = t2_id), 
        (select t3_name from t3 where t1_id = t3_id), 
        (select t4_name from t4 where t1_id = t4_id), 
        (select t5_name from t5 where t1_id = t5_id), 
        (select t6_name from t6 where t1_id = t6_id), 
        (select t7_name from t7 where t1_id = t7_id), 
        (select t8_name from t8 where t1_id = t8_id), 
        (select t9_name from t9 where t1_id = t9_id)  FROM t1

クエリのパフォーマンスが劇的に向上しました。実際、サブクエリはクエリを実行する時間を長くするようには見えませんでした(クエリはほとんど瞬時でした)。

相関サブクエリのパフォーマンスが結合よりも悪いと思ったので、少し驚きました。

53
Andrew

テーブル内のデータの量によっては、結合対象の列にインデックスを配置する必要がある場合があります。多くの場合、クエリの速度が遅いのは、適切な場所にインデックスがないためです。

また:

LEFT JOINはINNER JOINよりも低速です(ただし、これは正確に何を行っているかに依存します)-内部結合で探していることを達成できますか?

29
Nathan Ridley

クエリのEXPLAIN PLANを投稿できれば、少し役立ちます。

しかし、最初に、結合で使用されるすべてのフィールドにインデックスがありますか? CREATE INDEX ix_t2_id on t2 (t2_id, t2_name);のようなもの

結合の代わりに、次のようなことができます

SELECT t1_id, 
    (select t2_name from t2 where t1_id = t2_id), 
    (select t3_name from t3 where t1_id = t3_id), 
    (select t4_name from t4 where t1_id = t4_id), 
    (select t5_name from t5 where t1_id = t5_id), 
    (select t6_name from t6 where t1_id = t6_id), 
    (select t7_name from t7 where t1_id = t7_id), 
    (select t8_name from t8 where t1_id = t8_id), 
    (select t9_name from t9 where t1_id = t9_id) 
FROM t1 

しかし、優れたクエリプランナーを使用すれば、結合と違いはありません。

5
Jimmy Stenke

どれくらいのデータについて話しているのですか?大量のデータがあり、クエリプロセスの最後にwhere句が実行されているため、フィルタリングする前に大量のデータを結合している可能性があります。

その場合、できるだけ早くデータをフィルタリングして、最初の内部選択でT1からのデータを制限できる場合は、他のすべての結合がより制限されたデータセットに結合します。

Select <your fields> from
(
Select * from t1 where t1_id = t1_value
) t1

Inner join t2
on t1.ID = t2.ID
...

大量のデータではない場合。インデックスが正しいことを確認してから、サーバーの種類を確認してください。インデックスの断片化。ディスクキューなど.

5
u07ch

クエリプランから、sn、およびqとして参照されるテーブルには、結合されているフィールドにインデックスがないと結論付けることができます。

これらのテーブルには多くの行があるため(約400,000デカルト積の行)およびMySQLJOINの唯一の方法はNESTED LOOPS、それは本当に永遠にかかります。

これらのテーブルにインデックスを作成するか、結合フィールドをPRIMARY KEY

1
Quassnoi

T1のすべての行が必要で、他のテーブルの主キー(クラスター化インデックスでもあると思います)で結合を残した場合、クエリの速度を改善する方法はありません。

パフォーマンスを向上させるには、結果セットを減らすか、厄介なトリックを実行する必要があります(データの非正規化コピーを作成するなど)。

私が見ることができるように、t1テーブルはすべてのテーブルと結合されているものであり、非常に多くの結合を持つ単一のクエリに入れる代わりに、おそらくこのような異なるクエリのユニオンを試すことができます。

SELECT  t1_id, t2_name 
FROM    t1 LEFT JOIN t2 ON (t1_id = t2_id)
union 
SELECT  t1_id, t3_name 
FROM    t1 LEFT JOIN t3 ON (t1_id = t3_id)

ただし、その場合、取得する結果は8列ではなく1列になります。それがあなたと利用可能なオプションであるかどうかわからない。

実装するソリューションで必要なもう1つのことは、すべてのテーブルに適切なインデックスを作成することです。インデックス列のベストプラクティスは、結合またはwhere句で最も頻繁に使用される列に作成することです。

0
Vikram