web-dev-qa-db-ja.com

サブクエリにインデックスを付けることはできますか?

以下のようなテーブルとクエリがあります。実用的な例については、これを参照してください SQL Fiddle

SELECT o.property_B, SUM(o.score1), w.score
FROM o
INNER JOIN 
(
    SELECT o.property_B, SUM(o.score2) AS score FROM o GROUP BY property_B
) w ON w.property_B = o.property_B
WHERE o.property_A = 'specific_A'
GROUP BY property_B;

私の実際のデータでは、このクエリには27秒かかります。ただし、最初にwを一時テーブルおよびインデックスproperty_Bとして作成した場合、すべてを合わせて約1秒かかります。

CREATE TEMPORARY TABLE w AS
SELECT o.property_B, SUM(o.score2) AS score FROM o GROUP BY property_B;

ALTER TABLE w ADD INDEX `property_B_idx` (property_B);

SELECT o.property_B, SUM(o.score1), w.score
FROM o
INNER JOIN w ON w.property_B = o.property_B
WHERE o.property_A = 'specific_A'
GROUP BY property_B;

DROP TABLE IF EXISTS w;

これら2つのクエリの最良のものを組み合わせる方法はありますか?つまりサブクエリのインデックス作成の速度の利点を備えた単一のクエリ?

編集

以下のMehranの回答の後、私はこの説明を MySQLドキュメント で読みました。

MySQL 5.6.3以降、オプティマイザはFROM句(つまり、派生テーブル)のサブクエリをより効率的に処理します。

.。

FROM句のサブクエリにマテリアライズが必要な場合、オプティマイザはマテリアライズされたテーブルにインデックスを追加することにより、結果へのアクセスを高速化できます。そのようなインデックスがテーブルへのrefアクセスを許可する場合、クエリの実行中に読み取る必要のあるデータの量を大幅に減らすことができます。次のクエリについて考えてみます。

SELECT * FROM t1
  JOIN (SELECT * FROM t2) AS derived_t2 ON t1.f1=derived_t2.f1;

オプティマイザは、派生した_t2から列f1にインデックスを作成します。そうすることで、最低コストの実行プランでrefアクセスを使用できるようになります。インデックスを追加した後、オプティマイザはマテリアライズされた派生テーブルをインデックス付きの通常のテーブルと同じように扱うことができ、生成されたインデックスから同様に恩恵を受けます。インデックス作成のオーバーヘッドは、インデックスなしのクエリ実行のコストと比較してごくわずかです。 refアクセスによって他のアクセス方法よりもコストが高くなる場合、インデックスは作成されず、オプティマイザは何も失いません。

12

まず、一時テーブルの作成が絶対に実行可能なソリューションであることを知っておく必要があります。しかし、他の選択肢が適用できない場合は、ここでは当てはまりません。

あなたの場合、サブクエリとメインクエリの両方が同じフィールドでグループ化されているため、FrankPlが指摘しているように、クエリを簡単にブーストできます。したがって、サブクエリは必要ありません。完全を期すために、FrankPlのソリューションをコピーして貼り付けます。

SELECT o.property_B, SUM(o.score1), SUM(o.score2)
FROM o
GROUP BY property_B;

ただし、サブクエリにインデックスを付けることができるシナリオに出くわすことが不可能であるという意味ではありません。この場合、2つの選択肢があります。最初は、自分で指摘した一時テーブルを使用して、サブクエリの結果を保持することです。このソリューションは、MySQLで長期間サポートされているため、有利です。大量のデータが関係している場合、それは実行不可能です。

2番目の解決策は MySQLバージョン5.6以降 を使用することです。 MySQLの最近のバージョンでは、新しいアルゴリズムが組み込まれているため、サブクエリ内で使用されるテーブルで定義されたインデックスをサブクエリの外部でも使用できます。

[更新]

質問の編集バージョンについては、次の解決策をお勧めします。

SELECT o.property_B, SUM(IF(o.property_A = 'specific_A', o.score1, 0)), SUM(o.score2)
FROM o
GROUP BY property_B
HAVING SUM(IF(o.property_A = 'specific_A', o.score1, 0)) > 0;

ただし、HAVINGの部分で作業する必要があります。実際の問題に応じて変更する必要があるかもしれません。

4
Mehran

私はMySqlにあまり精通しておらず、主にOracleを使用していました。 SUMにwhere句が必要な場合は、decodeまたはcaseを使用できます。それはそのように見えるでしょう

SELECT o.property_B, , SUM(decode(property_A, 'specific_A', o.score1, 0), SUM(o.score2)
FROM o
GROUP BY property_B;

またはケース付き

SELECT o.property_B, , SUM(CASE
                            WHEN property_A = 'specific_A' THEN o.score1 
                            ELSE 0 
                            END ), 
SUM(o.score2)
FROM o
GROUP BY property_B;
2
a.j. tawleed

なぜ参加が必要なのか全くわかりません。私はそれを仮定します

SELECT o.property_B, SUM(o.score1), SUM(o.score2)
FROM o
GROUP BY property_B;

あなたが望むものを与えるべきですが、はるかに単純で、したがってステートメントを最適化する方が良いです。

1
FrankPl

クエリを最適化するのはMySQLの義務であり、その場でインデックスを作成する方法はないと思います。ただし、property_oのインデックスの使用を強制することはできます(ある場合)。 http://dev.mysql.com/doc/refman/5.1/en/index-hints.html を参照してください

また、必要に応じて、createステートメントとalterステートメントをマージすることもできます。

1
koriander