【问题标题】:How can I optimise this COUNT DISTINCT on joined InnoDB tables?如何在连接的 InnoDB 表上优化此 COUNT DISTINCT?
【发布时间】:2015-01-22 16:15:36
【问题描述】:
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

解释给我:

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

我能做些什么来更快地得到这个总数?

【问题讨论】:

  • 我不明白 r.id 如何代表打开的电子邮件,也不明白为什么需要DISTINCT。考虑添加一些示例数据来说明问题。
  • 对不起,我的问题的措辞不清楚:“打开唯一电子邮件”不是打开电子邮件的计数,而是打开电子邮件的唯一收件人的计数。因此,查询需要计算收件人 ID——尽管正如 John Bollinger 在下面指出的那样,我可以通过计算 emails.recipient_id 来节省自己的连接。我需要DISTINCT,因为收件人可能会多次打开一封电子邮件,我想打折。
  • 收件人记录何时创建?我想你知道你在用 INNER JOIN 做什么。如果您依靠 email.recipient_id 代替,您可能会记录没有匹配的收件人记录。您通常应该为这类问题提供示例数据和所需的输出,否则,我们必须假设您知道您在使用查询做什么。

标签: mysql count distinct innodb


【解决方案1】:

您需要加入recipients 如果您没有在recipients.idemails.recipent_id 之间强制执行外键约束,并且您希望排除以下收件人不再(不再)列入recipients 表中。否则,立即从连接中省略该表;您可以使用emails.recipient_id 而不是recipients.id。省略加入应该是一个巨大的胜利。

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

除此之外,您似乎已经很好地利用了索引,尽管我承认我发现 EXPLAIN PLAN 输出难以阅读,尤其是没有标题的情况下。尽管如此,您的查询似乎根本没有读取基表,因此添加新索引不太可能有帮助。

您可以尝试在查询中涉及的表上执行OPTIMIZE TABLE,尽管这听起来可能比它应该的更有希望。

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

如果您仍然需要更好的性能,那么有 other possibilities(包括迁移到更快的硬件),但它们太多了,无法在此处讨论。

【讨论】:

    【解决方案2】:

    您希望 MySQL 能够利用 WHERE 子句立即限制结果集。为此,您需要适当的索引来连接从活动到电子邮件,然后从电子邮件到收件人和视图。

    在campaigns.centre_id 上放置一个索引来帮助搜索(满足WHERE 子句)。我假设campaigns.id 是该表的主键。

    在 emails.campaign_id 上放置一个索引,以帮助加入来自营销活动的电子邮件。将接收者 ID 和电子邮件 ID 添加到该索引以提供覆盖索引。

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

    【讨论】:

      猜你喜欢
      • 2013-10-16
      • 1970-01-01
      • 2016-08-14
      • 1970-01-01
      • 2020-11-12
      • 2012-10-20
      • 2011-02-18
      相关资源
      最近更新 更多