0

日付範囲検索を使用して複数のテーブルを結合するクエリを実行していますが、それをさらに最適化する方法を見つけようとして立ち往生しています。

SELECT ACC.name AS account_name, CAMP.account_id AS account_id,CAMP.name AS campaign_name,CAMP.id AS campaign_id,ADG.id AS adgroup_id,ADG.name AS adgroup_name,KW.text AS keyword_name,
SUM(SPENT.billed_clicks) AS billed_clicks,KW.id AS keyword_id,KW.status_id AS status_id FROM account ACC, campaign CAMP,adgroup ADG,adgroup_keyword KW INNER JOIN keyword_spent SPENT
ON KW.id = SPENT.keyword_id WHERE     summary_date >= '2012-03-01' AND summary_date <= '2012-03-04' AND KW.adgroup_id = ADG.id AND ADG.campaign_id = CAMP.id AND CAMP.account_id = ACC.id
GROUP BY keyword_id

これに対する EXPLAIN は、次の結果をもたらします -

+----+-------------+-------+--------+----------------------------+--------------+---------+---------------------------------+--------+----------------------------------------------+
| id | select_type | table | type   | possible_keys              | key          | key_len | ref                             | rows   | Extra                                        |
+----+-------------+-------+--------+----------------------------+--------------+---------+---------------------------------+--------+----------------------------------------------+
|  1 | SIMPLE      | SPENT | range  | summary_date               | summary_date | 3       | NULL                            | 752191 | Using where; Using temporary; Using filesort | 
|  1 | SIMPLE      | KW    | eq_ref | PRIMARY,FK1948D0E6ED3A5544 | PRIMARY      | 8       | clicksummarydb.SPENT.keyword_id |      1 |                                              | 
|  1 | SIMPLE      | ADG   | eq_ref | PRIMARY,FKBBC2083C29112FD0 | PRIMARY      | 8       | advertisedb.KW.adgroup_id       |      1 |                                              | 
|  1 | SIMPLE      | CAMP  | eq_ref | PRIMARY,FKF7A90110246F33C4 | PRIMARY      | 8       | advertisedb.ADG.campaign_id     |      1 |                                              | 
|  1 | SIMPLE      | ACC   | eq_ref | PRIMARY                    | PRIMARY      | 8       | advertisedb.CAMP.account_id     |      1 |                                              | 
+----+-------------+-------+--------+----------------------------+--------------+---------+---------------------------------+--------+----------------------------------------------+

keyword_spent テーブルには 150 万行を超える行が含まれており、これは show create table です。

 | keyword_spent | CREATE TABLE `keyword_spent` (
   `id` bigint(20) NOT NULL auto_increment,
   `summary_date` date NOT NULL,
   `adgroup_id` bigint(20) NOT NULL,
   `keyword_id` bigint(20) NOT NULL,
   `billed_clicks` int(11) default NULL,
   `un_billed_clicks` int(11) default NULL,
   `spent` decimal(20,5) default NULL,
   `last_click_recno` bigint(20) default NULL,
   `campaign_id` bigint(20) NOT NULL,
   `account_id` bigint(20) NOT NULL,
   `total_convs` bigint(20) unsigned default '0',
    PRIMARY KEY  (`id`),
   UNIQUE KEY `keyword_spent_uniq` (`summary_date`,`adgroup_id`,`keyword_id`),
   KEY `idx_account_id` (`account_id`),
   KEY `idx_kw_id` (`keyword_id`),
   KEY `adgroup_id` (`adgroup_id`),
   KEY `campaign_id` (`campaign_id`),
   KEY `summary_date` (`summary_date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 | 

その日付範囲に 100,000 件以下のレコードがあるのに、750,000 行近くがスキャンされている理由がわかりません。

また、インデックスを使用する代わりにファイルソートを行うのはなぜですか。?

4

3 に答える 3

2

結合述語で参照されているすべての列でインデックスを試してください。

CREATE INDEX keyword_spent_IX2 ON keyword_spent (keyword_id, summary_date)

-また-

CREATE INDEX keyword_spent_IX3 ON keyword_spent (summary_date, keyword_id)

- または - クエリで参照されるすべての列を含むカバリング インデックスを作成することもできます。

CREATE INDEX keyword_spent_IX4 ON keyword_spent (keyword_id, summary_date,
    billed_clicks, un_billed_clicks, spent, total_convs)

ファイルソート操作は、GROUP BY が原因である可能性があります。

私の好みはJOIN ... ON、古い学校のコンマではなく構文を使用し、WHERE 句で結合述語を混合することです。

  FROM account ACC
  JOIN campaign CAMP ON CAMP.account_id = ACC.id
  JOIN adgroup ADG ON ADG.campaign_id = CAMP.id
  JOIN adgroup_keyword KW ON KW.adgroup_id = ADG.id
  JOIN keyword_spent SPENT ON SPENT.keyword_id = KW.id
 WHERE SPENT.summary_date >= '2012-03-01'
   AND SPENT.summary_date <= '2012-03-04'
 GROUP BY SPENT.id

SELECT リスト内の非集計のサブセットのみによってグループ化しています。他のほとんどの RDBMS はこれに対して例外をスローします。MySQL はよりリベラルです。

于 2012-07-06T17:47:16.707 に答える
1

ファイルの並べ替えは必ずしも悪いわけではありません。Baron Schwartzのブログ投稿に示されているように、ファイルの並べ替えは必ずしもファイルに関するものではありません。これは、使用可能な有効なインデックスがない場合に使用されるクイックソートです。

最適化する方法のアイデアとして:おそらく、すべての集合体データを独自のサブクエリに入れ、そのデータを結合しますか?私はこのようなことを考えています(必要に応じて調整してください):

SELECT ACC.name AS account_name,
CAMP.account_id AS account_id,
CAMP.name AS campaign_name,
CAMP.id AS campaign_id,
ADG.id AS adgroup_id,
ADG.name AS adgroup_name,
KW.text AS keyword_name,
KW.id AS keyword_id,
JOINED.billed_clicks AS billed_clicks,
JOINED.un_billed_clicks AS un_billed_clicks,
JOINED.total_clicks AS total_clicks,
JOINED.spent AS spent,
JOINED.total_convs AS total_convs
FROM account ACC
INNER JOIN campaign CAMP ON ACC.id = CAMP.account_id
INNER JOIN adgroup ADG ON CAMP.id = ADG.campaign_id
INNER JOIN adgroup_keyword KW ON ADG.id = KW.adgroup_id
INNER JOIN (SELECT
    SUM(billed_clicks) AS billed_clicks,
    SUM(un_billed_clicks) AS un_billed_clicks,
    SUM(billed_clicks) + SUM(un_billed_clicks) AS total_clicks,
    SUM(spent) AS spent,
    SUM(total_convs) AS total_convs,
    id AS keyword_id
    FROM keyword_spent
    GROUP BY keyword_id
) JOINED ON JOINED.keyword_id = KW.id

うまくいけば、私はこの権利を理解しています。このソリューションには1つの利点があります。groupby/aggregatesが別々に保持され、元の例では実行しなかった他の列のgroupbyについて心配する必要がありません。

于 2012-07-06T17:52:32.397 に答える
1

最初に summary_date のインデックスを試して (where で使用されます)、次に keyword_id; JOIN内で日付範囲を明示的に移動します。

ON (SPENT.id = KW.id AND SPENT.summary_date BETWEEN ... AND ...)

また、SPENT の集計フィールドを提供する VIEW を作成してみてください。理想的には、オプティマイザーがこれをよりよく理解し、時間を節約できるようにする必要があります。

CREATE VIEW SPENT AS SELECT
    keyword_id,
    SUM(SPENT.billed_clicks) AS billed_clicks,
    SUM(SPENT.un_billed_clicks) AS un_billed_clicks,
    SUM(SPENT.spent) AS spent,
    SUM(SPENT.total_convs) AS total_convs
FROM keyword_spent GROUP BY keyword_id;

これには、最初に keyword_id、2 番目に summary_date のインデックスが必要であり、VIEW を使用した JOIN は 100,000 行の SELECT に相当する必要があります。

于 2012-07-06T17:58:48.773 に答える