web-dev-qa-db-ja.com

多くのJOINを使用してクエリのパフォーマンスを向上させる方法

いくつかの結合を使用して各列を取得する(ビューを作成するための)クエリがあります。追加された結合のセットごとに、パフォーマンスが急速に(指数関数的に)低下します。

このクエリを高速化するには、どのようなアプローチが適切でしょうか?クエリ内のコメントを参照してください。

それが役立つ場合は、WordPress DBスキーマを使用しています。

EXPLAINのスクリーンショット enter image description here

製品表

+--+----+
|id|name|
+--+----+
|1 |test|
+--+----+

メタデータテーブル

+----------+--------+-----+
|product_id|meta_key|value|
+----------+--------+-----+
|1         |price   |9.99 |
+----------+--------+-----+
|1         |sku     |ABC  |
+----------+--------+-----+

TERM_RELATIONSHIPS TABLE

+---------+----------------+
|object_id|term_taxonomy_id|
+---------+----------------+
|1        |1               |
+---------+----------------+
|1        |2               |
+---------+----------------+

TERM_TAXONOMY TABLE

+----------------+-------+--------+
|term_taxonomy_id|term_id|taxonomy|
+----------------+-------+--------+
|1               |1      |size    |
+----------------+-------+--------+
|2               |2      |stock   |
+----------------+-------+--------+

用語表

+-------+-----+
|term_id|name |
+-------+-----+
|1      |500mg|
+-------+-----+
|2      |10   |
+-------+-----+

クエリ

SELECT 
  products.id,
  products.name,
  price.value AS price,
  sku.value AS sku,
  size.name AS size
FROM products

/* These joins are performing quickly */

INNER JOIN `metadata` AS price ON products.id = price.product_id AND price.meta_key = 'price'
INNER JOIN `metadata` AS sku ON products.id = sku.product_id AND sku.meta_key = 'sku'

/* Here's the part that is really slowing it down - I run this chunk about 5 times with different strings to match */

INNER JOIN `term_relationships` AS tr ON products.id = tr.object_id
  INNER JOIN `term_taxonomy` AS tt
  ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'
    INNER JOIN `terms` AS size
    ON tt.term_id = size.term_id
20
dloewen

パフォーマンスの問題は、おそらく 'term_taxonomy'テーブルとの結合が原因です。
他のすべての結合は、主キーを使用しているようです(おそらく作業インデックスがある)。

だから私の提案は、複合インデックスをterm_taxonomy_idおよびterm_idに追加することです(または必要な場合はtaxonomy)。このような:

CREATE UNIQUE INDEX idx_term_taxonomy_id_taxonomy
ON term_taxonomy( term_taxonomy_id, taxonomy);

これがお役に立てば幸いです。

12
carleson

"ON"条件ステートメントが存在するすべての列にインデックスを付ける必要があることを確認してください。これにより、速度が大幅に向上します。

2
akkig

私はそれらを提案します:

  • ビジネスレベルからこれらの結合を減らすことを検討してください。
  • 「トップ」(ビジネスレベル)から行うことができず、データがリアルタイムでない場合は、メモリテーブルを準備することをお勧めします(ソリューションが理想的でないことはわかっています)。そして、直接メモリテーブルからデータを選択します。

私の経験では:

  • "joins"はパフォーマンスのキラーであり、データが大きいほど、より多くの痛みを感じるでしょう。
  • 必要がない限り結合を維持することでクエリのパフォーマンスを向上させようとせず、結合を取り除いてください。通常、これらの問題を「上」から「下」に修正しようとします
  • 最後の提案は、上記のすべてが機能しない場合です。やりたいことがあれば、「map/reduce +全文検索」を検討します。

(私にあなたのクエリのパフォーマンスを改善するためのソリューションを提供しなかったことを許してください。)

0
Joshua

METADATA_TABLEおよびTERM_RELATIONSHIP_TABLEには、proimaryキーはありません。これらのテーブルに巨大なレコードがある場合、クエリのパフォーマンスが低下します。

パフォーマンスを向上させるチェックポイント。

  1. すべてのテーブルには主キーが必要です。これは、テーブルの行が物理的に並べ替えられるためです。
  2. テーブルに主キーを保持する少数のテーブルを含む小規模なクエリでは十分です。それでもパフォーマンスを向上させたい場合は、* term_relationshipsテーブルのobject_Idフィールド*などの列の非クラスター化インデックスを作成します。非クラスター化インデックスは、結合操作に参加しているテーブル内の列に対して作成する必要があります。

ただし、複数の挿入と更新が行われているテーブルでは、非クラスター化インデックスが非常に少なくなることに注意してください。これは単純な質問ではなく、実行時間だけに基づいて回答することはできません。特に、ストアドプロシージャが実行されている環境のトランザクションが多すぎる場合は、他にも回答に影響する要素があります。

あなたはもっと見つけることができます ここ

0
gokul
    Declare @query as NVARCHAR(MAX)
    set @query = ('SELECT 
    products.id,
    products.name,
    price.value AS price,
    sku.value AS sku,
    size.name AS size
    FROM products
    INNER JOIN metadata AS price ON products.id = price.product_id AND price.meta_key = price
    INNER JOIN metadata AS sku ON products.id = sku.product_id AND sku.meta_key = sku
    INNER JOIN term_relationships AS tr ON products.id = tr.object_id
    INNER JOIN term_taxonomy AS tt
    ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = size
    INNER JOIN terms AS size
    ON tt.term_id = size.term_id
    into #t')

    exec(@query);
    select * from #t

上記の方法で時間の使用率が減るか、または選択したすべてのフィールドを持つ一時テーブルを作成し、一時テーブルを他のすべてのテーブルに結合して一時テーブルを更新することも効果的であると思いますが、よくわかりませんしかし、あなたの質問が興味をそそられるように私はあなたの結果を待っています

0
JB9

以下のスクリプトは、SQL Serverルールに従ってフォーマットされています-これをMySQLルールに従って変更して、試してみることができます-

SELECT 
  P.id,
  P.name,
  PIVOT_METADATA.price,
  PIVOT_METADATA.sku,
  size.name AS size
FROM products P (NOLOCK)

INNER JOIN term_relationships AS tr (NOLOCK)
    ON P.id = tr.object_id

INNER JOIN term_taxonomy AS tt (NOLOCK)
    ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'

INNER JOIN terms AS size (NOLOCK)
    ON tt.term_id = size.term_id

INNER JOIN METADATA (NOLOCK)
    PIVOT
    (
        MAX(value)
        FOR [meta_key] IN (price,sku)
    )AS PIVOT_METADATA
    ON P.id = PIVOT_METADATA.product_id

クエリでボトルネックになっていると思われるもの-メタデータに2回参加しています。テーブルには1対多の関係があるため、メタデータ2結合は害を及ぼしませんが、その後、より多くのテーブルを結合すると、1対多の関係が原因で行数が増加するため、パフォーマンスが低下します。

私が達成したこと-1対1の関係が可能な限り満たされるようにしています。これを行うために、メタデータのピボットを実行し、列として価格とSKUを作成しました。これで、私の製品IDのメタデータピボットに1行のみが含まれるようになります。 alos、私は最後にこのピコに参加することを確認しました。

試してみる。期待されるパフォーマンス、保有しているレコードの数、および私のパフォーマンスで得られるパフォーマンスを教えてください。

0
Suyash Khandwe

これを試して:

SELECT p.id, p.name, MAX(CASE m.meta_key WHEN 'price' THEN m.value ELSE '' END) AS price, 
       MAX(CASE m.meta_key WHEN 'sku' THEN m.value ELSE '' END) AS sku, s.name AS size
FROM products p 
INNER JOIN `metadata` AS m ON p.id = m.product_id  
INNER JOIN `term_relationships` AS tr ON p.id = tr.object_id 
INNER JOIN `term_taxonomy` AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'
INNER JOIN `terms` AS s ON tt.term_id = s.term_id
GROUP BY p.id;

それでもクエリが遅い場合は、クエリのEXPLAINプランを追加して、どの列にINDEXが必要かを見つけられるようにします。

0
Saharsh Shah