2016-03-19 15 views
0

私は2つのテーブルps_productps_categoryを持っています。製品テーブルは、ローカルデータベースに約146690行、リモートデータベースに約196000のインデックスを持ち、id_productおよびid_default_categorybtreeインデックスがあります。カテゴリテーブルは、いくつかの851行と、とid_parentカラムのbtreeインデックスを持っています。mysql再帰結合クエリを最適化する

カテゴリは6レベルまで階層化されており、すべてのカテゴリのすべての製品が必要です。したがって、製品53がカテゴリ67であり、それが43のサブカテゴリである50のサブカテゴリである場合、ルートカテゴリである1まで最大で - > 67 →50→43→20→1-> null。

私は、データをフェッチするために自己ps_category 6回に参加しますmysqlのクエリを考え出した、それがローカルDBに約0.8秒を取り、 - 実行するために、ネットワーク経由で5 を。私はそれを最適化する方法はありますか?クエリ:

SELECT 
    p.id_product, c.id_category, c1.id_category, c2.id_category, c3.id_category, c4.id_category, c5.id_category, c6.id_category 
FROM `ps_category` c 
    left join ps_product p on p.id_category_default = c.id_category 
    left join ps_category c1 on c1.id_category = c.id_parent 
    left join ps_category c2 on c2.id_category = c1.id_parent 
    left join ps_category c3 on c3.id_category = c2.id_parent 
    left join ps_category c4 on c4.id_category = c3.id_parent 
    left join ps_category c5 on c5.id_category = c4.id_parent 
    left join ps_category c6 on c6.id_category = c5.id_parent 
+0

0.8マイクロ秒は非常に高速です。なぜ、より最適化されたソリューションを探しているのですか? – Shadow

+0

@ Shadow、統計情報を変更しました –

+0

あなたのお望みのものはうまくいきますし、最適化はほとんどできません。アプリケーションに応じて(新製品がデータベースに追加される頻度)、MATERIALIZED VIEWを設定することができます。このコンセプトはMySQLには存在しませんが、あなたはそれを模倣することができます:http://www.fromdual.com/mysql-materialized-views –

答えて

0

1つのオプションは、クロージャーテーブルの使用です。

CREATE TABLE category_closure (
    `a_catagory_id` SMALLINT UNSIGNED NOT NULL, 
    `d_category_id` SMALLINT UNSIGNED NOT NULL, 
    PRIMARY KEY (`a_category_id`,`d_category_id`) 
) ENGINE=InnoDB 

この表には、階層内のさまざまなレベルのカテゴリ間の関係が記録されています。 a_category_idは関係の祖先を表し、は子孫を表します。

表が正しく機能するためには、各カテゴリを祖先と子孫として表に入力する必要があります。

INSERT INTO category_closure 
(a_category_id, d_category_id) 
SELECT 
id_category, 
id_category 
FROM ps_category 

その後、各カテゴリにid_parent欄からご既知の関係を入力することができます。

INSERT INTO category_closure 
(a_category_id, d_category_id) 
SELECT 
id_parent, 
id_category 
FROM ps_category 
WHERE id_parent IS NOT NULL 

最後に、ドットを接続する必要があります。 do..whileループで次のSELECTを実行します。行が返される限り、これらの行をクロージャーテーブルに挿入してループを続行します。このクエリは何

SELECT 
cc1.a_category_id, 
cc2.d_category_id 
FROM category_closure cc1 
INNER JOIN category_closure cc2 
ON cc2.a_category_id = cc1.d_category_id 
LEFT OUTER JOIN category_closure missing_cc 
ON missing_cc.a_category_id = cc1.a_category_id 
AND missing_cc.d_category_id = cc2.d_category_id 
WHERE missing_cc.a_category_id IS NULL 

は、それがすべての既存の関係をとりであり、また、存在しなければならないものを見つけました。たとえば、次のようなチェーンを持っている:

53> 67あなたが最初の2つの挿入から、次のレコードを持っています意味> 50

: (50,50) (67,67) (53,53) (50,67) (67,53) (およびその他)。

53が50の子孫であるため、ここでは(50,53)が必要です。上記のSELECTクエリでは、cc1は(50,67)レコードと一致します。 cc2は(67,53)レコードと一致します。これは、missing_cccc1a_category_id)から53と、cc2d_category_id)の53と一致するように試みていることを意味します。

最初にこのようなレコードが存在しないため、SELECTステートメントはこれらの2つの行を戻し、挿入して繰り返すことができます。今度は、チェーンの上に行く。あなた(またはあなたのプログラム)がそこにいくつの層があるかを知る必要はなく、SELECTがそれ以上の結果を見つけなくなるまで続行します。

あなたの閉鎖テーブルが構築されると最後に、あなたは、関連する情報を選択することができます。

SELECT 
p.id_product, 
c.id_category, 
GROUP_CONCAT(cc.a_category_id) AS parent_category_ids 
FROM ps_category c 
LEFT OUTER JOIN ps_product p 
ON p.id_category_default = c.id_category 
LEFT OUTER JOIN category_closure cc 
ON cc.d_category_id = c.id_category 
AND cc.a_category_id != c.id_category 
GROUP BY c.id_category, p.id_product 

これは、すべてのカテゴリを選択し、各カテゴリ内のすべての製品、およびその後、先祖カテゴリのカンマ区切りのリストを提供しますそれらの組み合わせのそれぞれについて。

これは、カテゴリとその祖先を製品とは別に実行するだけなので、実際はかなりの情報が繰り返されていますが、このデータをどのように使いたいか、クエリーかどうか。

注:カテゴリを追加および削除する場合は、現在の閉鎖エントリをすべて削除した後で、カテゴリごとにこの手順を繰り返す必要があります。ここに示すよりも良い方法があり、そのような方法でも(特に階層が非周期的な場合は)id_parent列の使用を中止することさえできますが、そのようなことはこの質問の範囲外です。

この回答は、アプリケーションや既存のデータを変更することなく、何か試してみる必要があります。