2009-05-22 11 views
10

です。本当に怪しいMySQLテーブルがあります各グループの平均。計算は〜15時間実行され、私は強い気分がある私はそれが間違っているだよ。SQLマジック - クエリは15時間かかるべきではありませんが、

まず、巨大なテーブルのレイアウト:

  • category
  • element_id
  • date_updated
  • value
  • weight
  • source_prefix
  • ここ
  • source_name

だけキーがelement_id(BTREE、〜8kのユニークな要素)です。

と計算プロセス:

各グループおよびサブグループのためのハッシュを作成します。

CREATE TEMPORARY TABLE `temp1` (INDEX (`ds_hash`)) 
       SELECT `category`, 
       `element_id`, 
       `source_prefix`, 
       `source_name`, 
       `date_updated`, 
       `value`, 
       `weight`, 
       MD5(CONCAT(`category`, `element_id`, `source_prefix`, `source_name`)) AS `subcat_hash`, 
       MD5(CONCAT(`category`, `element_id`, `date_updated`)) AS `cat_hash` 
       FROM `bigbigtable` WHERE `date_updated` <= '2009-04-28' 

私は本当にハッシュで、この騒ぎを理解していないが、それはより速く、このように働きました。暗い魔法、私は推測する。

各サブグループ

CREATE TEMPORARY TABLE `temp2` (INDEX (`subcat_hash`)) 

       SELECT MAX(`date_updated`) AS `maxdate` , `subcat_hash` 
       FROM `temp1` 
       GROUP BY `subcat_hash`; 

の最大の日付がカテゴリ

CREATE TEMPORARY TABLE `valuebycats` (INDEX (`category`)) 
      SELECT `temp1`.`element_id`, 
        `temp1`.`category`, 
        `temp1`.`source_prefix`, 
        `temp1`.`source_name`, 
        `temp1`.`date_updated`, 
        AVG(`temp1`.`value`) AS `avg_value`, 
      SUM(`temp1`.`value` * `temp1`.`weight`)/SUM(`weight`) AS `rating` 

      FROM `temp1` LEFT JOIN `temp2` ON `temp1`.`subcat_hash` = `temp2`.`subcat_hash` 
      WHERE `temp2`.`subcat_hash` = `temp1`.`subcat_hash` 
      AND `temp1`.`date_updated` = `temp2`.`maxdate` 

      GROUP BY `temp1`.`cat_hash`; 

(加重平均値を見つけるために、TEMP2とTEMP1を検索参加、今私はそれを見て、それをすべてを書いたこと私はその最後のクエリ(900k * 900kのテンポラリテーブルを避けるために)でINNER JOINを使うべきだと私には思われます。

まだ、普通の方法でがそうですか?

UPD:参照のためのいくつかの画像:

削除死んImageShackのリンク

UPD:提案されたソリューションのためにEXPLAIN:

+----+-------------+-------+------+---------------+------------+---------+--------------------------------------------------------------------------------------+--------+----------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref                     | rows | filtered | Extra          | 
+----+-------------+-------+------+---------------+------------+---------+--------------------------------------------------------------------------------------+--------+----------+----------------------------------------------+ 
| 1 | SIMPLE  | cur | ALL | NULL   | NULL  | NULL | NULL                     | 893085 | 100.00 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | next | ref | prefix  | prefix  | 1074 | bigbigtable.cur.source_prefix,bigbigtable.cur.source_name,bigbigtable.cur.element_id |  1 | 100.00 | Using where         | 
+----+-------------+-------+------+---------------+------------+---------+--------------------------------------------------------------------------------------+--------+----------+----------------------------------------------+  

答えて

5

hashsesを使用して、データベースエンジンが参加する実行可能な方法の一つです。独自のハッシュベースの結合を作成する必要があることは非常にまれです。これは確かに1つのようには見えません.900kの行テーブルといくつかの集計があります。

あなたのコメントに基づいて、このクエリは、あなたが探しているものを行う可能性があります:

SELECT cur.source_prefix, 
     cur.source_name, 
     cur.category, 
     cur.element_id, 
     MAX(cur.date_updated) AS DateUpdated, 
     AVG(cur.value) AS AvgValue, 
     SUM(cur.value * cur.weight)/SUM(cur.weight) AS Rating 
FROM eev0 cur 
LEFT JOIN eev0 next 
    ON next.date_updated < '2009-05-01' 
    AND next.source_prefix = cur.source_prefix 
    AND next.source_name = cur.source_name 
    AND next.element_id = cur.element_id 
    AND next.date_updated > cur.date_updated 
WHERE cur.date_updated < '2009-05-01' 
AND next.category IS NULL 
GROUP BY cur.source_prefix, cur.source_name, 
    cur.category, cur.element_id 

GROUP BYは、ソース+カテゴリ+要素ごとに計算を実行します。

JOINは、古いエントリを除外します。それ以降のエントリを検索し、WHERE文は後のエントリが存在する行をフィルタリングします。このような結合は、(source_prefix、source_name、element_id、date_updated)の索引から利益を得ます。

古いエントリをフィルタリングする方法はたくさんありますが、この方法では共振がよくなります。

+0

さて、私は説明しようとします。 この表には測定値があります。各測定には、ソース(接頭辞+名前で識別される)とカテゴリがあります。各要素は、すべてのカテゴリで測定値を持つことができます。 ソースからの要素の最新の測定値を見つけて、要素+カテゴリの加重平均を計算します。 私の英語には申し訳ありませんが、私の主な言語ではありません:\ –

+0

更新されました。最新のすべての測定でdate_updated *が*正確に等しくなっていますか?または、彼らは同じ日にちょうどですか? – Andomar

+0

彼らは同じソースと要素の最新です。彼らは変わるかもしれません。 –

3

オクラホマので、900Kの行は」にISN大規模なテーブルですが、それはかなり大きいですが、あなたの質問はそれほど長くはならないはずです。

最初のものが最初ですが、上記の3つのうちどれが時間がかかりますか?

私が見る最初の問題は、あなたの最初の質問です。WHERE句には索引付き列は含まれません。したがって、これは、テーブル全体に対して完全なテーブルスキャンを実行する必要があることを意味します。

「data_updated」列に索引を作成してから、再度問合せを実行して、それが何を行うかを確認してください。

ハッシュを必要とせず、暗い魔法を利用するためにそれらを使用している場合は、それらを完全に削除します。

編集:私よりもSQL-fuが多い人は、テンポラリテーブルを使用せずに、論理セット全体を1つのSQL文に縮小するでしょう。

編集:私のSQLは少し錆びますが、3番目のSQLステートメントで2回参加していますか?多分それは違いをすることはありませんが、それはすべきではない:

SELECT temp1.element_id, 
    temp1.category, 
    temp1.source_prefix, 
    temp1.source_name, 
    temp1.date_updated, 
    AVG(temp1.value) AS avg_value, 
    SUM(temp1.value * temp1.weight)/SUM(weight) AS rating 
FROM temp1 LEFT JOIN temp2 ON temp1.subcat_hash = temp2.subcat_hash 
WHERE temp1.date_updated = temp2.maxdate 
GROUP BY temp1.cat_hash; 

または

SELECT temp1.element_id, 
    temp1.category, 
    temp1.source_prefix, 
    temp1.source_name, 
    temp1.date_updated, 
    AVG(temp1.value) AS avg_value, 
    SUM(temp1.value * temp1.weight)/SUM(weight) AS rating 
FROM temp1 temp2 
WHERE temp2.subcat_hash = temp1.subcat_hash 
AND temp1.date_updated = temp2.maxdate 
GROUP BY temp1.cat_hash; 
+0

最後の1つ。最初は瞬時に近く、2番目は約23分です。 –

+0

ハッシュを削除できますが、クエリには無限の時間がかかります(大丈夫でしょうか、そうではありませんが、そのような忍耐もクライアントもありません)。何とかこれらのハッシュをインデックスに入れることができると思います。 –

+0

インデックスの提案が理にかなっているとは思わないでください。このような集約クエリは、常にテーブル全体のスキャンになります。 – Andomar

関連する問題