如何在加入的InnoDB表上优化此COUNT DISTINCT?

时间:2015-01-22 16:15:36

标签: mysql count distinct innodb

SELECT COUNT(DISTINCT r.id)
FROM views v
INNER JOIN emails e ON v.email_id = e.id
INNER JOIN recipients r ON e.recipient_id = r.id
INNER JOIN campaigns c ON e.campaign_id = c.id
WHERE c.centre_id IS NULL;

...或者,“我们有多少个独特的电子邮件打开?(在一般广告系列中)”

目前在Amazon RDS实例上运行大约需要一分半钟。所涉及的表的总行数大致为:

  • 广告系列:250
  • 收件人:330,000
  • 观看次数:530,000
  • 电子邮件:1,380,000

EXPLAIN给了我:

1   SIMPLE  r   index   PRIMARY UNIQ_146632C4E7927C74   767 NULL    329196  Using index
1   SIMPLE  e   ref PRIMARY,IDX_4C81E852E92F8F78,IDX_4C81E852F639F774   IDX_4C81E852E92F8F78    111 ecomms.r.id 1   Using where
1   SIMPLE  v   ref IDX_11F09C87A832C1C9    IDX_11F09C87A832C1C9    111 ecomms.e.id 1   Using where; Using index
1   SIMPLE  c   eq_ref  PRIMARY,IDX_E3737470463CD7C3    PRIMARY 110 ecomms.e.campaign_id    1   Using where

如何更快地完成这项工作?

2 个答案:

答案 0 :(得分:0)

如果您未在recipientsrecipients.id之间强制执行外键约束,则需要加入emails.recipent_id 您想要排除那些(不再)参加recipients表中的收件人。否则,直接从联接中省略该表;您可以使用emails.recipient_id代替recipients.id。省略加入应该是一个巨大的胜利。

或者,从联接中省略recipients,因为它与提出的问题无关,即打开的唯一电子邮件,而不是关于唯一的收件人打开任何电子邮件。在这种情况下,您应该只能SELECT COUNT(*) FROM ...,因为每个emails行都是唯一的。

除此之外,看起来你已经很好地利用了你的索引,虽然我承认我发现EXPLAIN PLAN输出难以阅读,特别是没有标题。尽管如此,看起来您的查询根本不会读取基表,因此添加新索引不太可能有所帮助。

您可以尝试对查询中涉及的表执行OPTIMIZE TABLE,但这可能听起来比预期更有希望。

您应该定期对此查询中涉及的表运行ANALYZE TABLE,以使查询优化程序最有可能选择最佳计划。看起来优化器已经在选择一个合理的计划了,所以这可能没有多大帮助。

如果你仍然需要更好的性能,那么other possibilities(包括转向更快的硬件),但它们太多了,无法在这里讨论。

答案 1 :(得分:0)

您希望MySQL能够利用WHERE子句立即限制结果集。为此,您需要使用适当的索引从广告系列加入电子邮件,然后从电子邮件加入收件人和视图。

在campaigns.centre_id上​​添加索引以帮助搜索(满足WHERE子句)。我假设campaign.id是该表的主键。

在emails.campaign_id上添加一个索引,以帮助加入来自广告系列的电子邮件。将recipient_id和email_id添加到该索引以提供覆盖索引。

现在,EXPLAIN结果应按顺序显示表格,从广告系列开始,然后是电子邮件,然后是其他两个。 MySQL仍然需要一个内部临时表来应用DISTINCT。你确定需要吗? 我假设emails.id和recipients.id是主键。