【问题标题】:Is it possible to use two COUNT and two JOIN in a SQL query from 3 tables?是否可以在来自 3 个表的 SQL 查询中使用两个 COUNT 和两个 JOIN?
【发布时间】:2019-03-01 20:36:37
【问题描述】:

所以我在这里要做的是获取一份关于不同用户发送了多少电子邮件(使用类似 MailChimp 的应用程序)的报告,但我希望一个查询中有两个不同的指标。我想知道每个用户发送了多少封单独的电子邮件。这意味着如果他们每人向 100 个联系人发送 3 封电子邮件,则显示为 300。但我还想知道发送了多少独特的电子邮件,这意味着将显示 3。

我想要一些类似的东西:

-------------------------------------------------------------
| Full Name   | Username        | Total Sent | Unique Mails |
|-------------|-----------------|------------|--------------|
| John Doe    | jdoe@mail.com   | 12000      | 4            |
| James Smith | jsmith@mail.com | 6000       | 12           |
| Jane Jones  | jjones@mail.com | 4000       | 2            |
| ...         | ...             | ...        | ...          |
-------------------------------------------------------------

所以我可以知道 John 向很多联系人发送了几封电子邮件,而 James 向更少的联系人发送了更多电子邮件。

这是我的查询的样子。我已经更改了表名和列名,但这是对它的准确表示。

SELECT
    CONCAT(Usernames.FirstName, ' ', Usernames.LastName) AS 'Full Name',
    Usernames.Username,
    COUNT(Sent_Mail_Contacts.IDContact) AS `Total Sent`,
    COUNT(Mass_Mail.IDMass_Mail) AS `Individual E-Mails`
FROM Usernames
LEFT JOIN Sent_Mail_Contacts ON Usernames.Username = Sent_Mail_Contacts.Username
LEFT JOIN Mass_Mail ON Usernames.Username = Mass_Mail.Username
GROUP BY Usernames.Username
ORDER BY `Total Sent`

我有一个包含用户名的表格,一个包含电子邮件的个人联系人的表格,以及一个包含唯一电子邮件的表格。

那么我的查询是否有意义?这甚至可能吗?因为现在当我运行它时,它给了我这样的东西:

-------------------------------------------------------------
| Full Name   | Username        | Total Sent | Unique Mails |
|-------------|-----------------|------------|--------------|
| John Doe    | jdoe@mail.com   | 12000      | 12000        |
| James Smith | jsmith@mail.com | 6000       | 6000         |
| Jane Jones  | jjones@mail.com | 4000       | 4000         |
| ...         | ...             | ...        | ...          |
-------------------------------------------------------------

我只是在两列中给了我相同的数字,并且需要 7 分钟来处理。

如果有帮助,下面是 3 个表格分别显示的示例:

Usernames
------------------------------------------------
| Username        | FirstName | LastName | ... |
|-----------------|-----------|----------|-----|
| jdoe@mail.com   | John      | Doe      | ... |
| jsmith@mail.com | James     | Smith    | ... |
| jjones@mail.com | Jane      | Jones    | ... |
| ...             | ...       | ...      | ... |
------------------------------------------------

Mass_Mail
----------------------------------------------------
| ID_Mass_Mail | Username       | Date       | ... |
|--------------|----------------|------------|-----|
|            1 | jdoe@mail.com  | 2019-01-16 | ... |
|            2 | jdoe@mail.com  | 2019-01-29 | ... |
|            3 | jjones@mail.com| 2019-02-14 | ... |
|          ... | ...            | ...        | ... |
----------------------------------------------------

Sent_Mail_Contacts
---------------------------------------------------------------------
| ID_Mass_Mail | Username       | Contact_ID | Contact_Email  | ... |
|--------------|----------------|------------|----------------|------
|            1 | jdoe@mail.com  |          1 | bob@mail.com   | ... |
|            1 | jdoe@mail.com  |          2 | jim@mail.com   | ... |
|            1 | jdoe@mail.com  |          3 | cindy@mail.com | ... |
|          ... | ...            |        ... | ...            | ... |
|            2 | jdoe@mail.com  |          4 | mike@mail.com  | ... |
|            2 | jdoe@mail.com  |          2 | jim@mail.com   | ... |
|            2 | jdoe@mail.com  |          3 | cindy@mail.com | ... |
|          ... | ...            |        ... | ...            | ... |
---------------------------------------------------------------------

【问题讨论】:

  • 附带说明:数据模型似乎有点缺陷。为什么Sent_Mail_Contacts 既有Contact_ID 又有Contact_Email?如果联系人有 ID,则应该有一个联系人表,并且该表应该每个联系人 ID 有一行,并包含相关的电子邮件地址。然后你在Sent_Mail_Contacts 中有Username。为什么? ID_Mass_Mail 是否可以为空,所以有些行您无法从那里获取用户?否则,Mass_MailSent_Mail_Contacts 中不同的用户名会表示什么?带有虚假帐户的电子邮件?让您的数据库摆脱冗余。标准化。

标签: mysql sql count left-join heidisql


【解决方案1】:

使用COUNT(DISTINCT ...)

SELECT
    CONCAT(Usernames.FirstName, ' ', Usernames.LastName) AS 'Full Name',
    Usernames.Username,
    COUNT(Sent_Mail_Contacts.IDContact) AS `Total Sent`,
    COUNT(DISTINCT Mass_Mail.IDMass_Mail) AS `Individual E-Mails`
FROM Usernames
LEFT JOIN Sent_Mail_Contacts ON Usernames.Username = Sent_Mail_Contacts.Username
LEFT JOIN Mass_Mail ON Usernames.Username = Mass_Mail.Username
GROUP BY Usernames.Username
ORDER BY `Total Sent`

注意:这不会使查询变得更快。首先,您至少应该确保在 JOINs 中使用主键/外键关系:Usernames(Username)Sent_Mail_Contacts(Username)Mass_Mail(Username)

【讨论】:

  • 也许我不够清楚。对不起。我的意思是每发送一封电子邮件只有一个 IDMass_Mail。电子邮件号码 1457 可以发送给 100 个联系人,而在“Sent_Mail_Contacts”表中,它会为每个联系人显示一行,他们都将具有相同的 IDMass_Mail,即 1457,而在 Mass_Mail 表中,IDMass_Mail 1457 将仅在那里一次。我不知道我是否清楚抱歉,我对这一切有点陌生。
  • @G.Langlois :嗯,好吧,我想我误读了你的问题。如果您可以编辑您的问题以添加 3 输入表的示例数据以及相应的预期结果,那就太好了。见this link。暂时我会删除我的答案。
  • @G.Langlois :根据您的示例数据,看起来DISTINCT 确实是您需要的......所以我取消了我的答案。
  • 这确实是我需要的。我已经发布了一个答案来展示我做了什么,并且效果很好。非常感谢!
【解决方案2】:

假设IDMass_Mail 中的值表示一个唯一的电子邮件,那么您只需编辑最后一个COUNT 以使用DISTINCT 关键字。

COUNT(DISTINCT Mass_Mail.IDMass_Mail) AS `Individual E-Mails`

这将返回由Username 分组的唯一值的数量。

如果您能够为Sent_Mail_ContactsMass_Mail 表中的Username 列添加索引,您还应该获得性能提升。

【讨论】:

  • 嗨,埃里克。感谢您的回答。你之前的某个人回答了同样的事情,我不确定它会起作用。也许我没有正确理解您的答案,但我看到的问题是 IDMass_Mail 确实是独一无二的,没有必要计算不同的 IDMass_Mail 因为在 Mass_Mail 表中,它们只出现一次。我想知道每个用户名有多少 IDMass_Mail。我在我的问题中添加了更多关于这 3 个表格的详细信息。也许您可以检查一下,看看是否有帮助。
  • 从您的示例数据中,在Sent_Mail_Contacts 表中,如果您按Username 分组,将有两个不同的值ID_Mass_Mail,1 和2。所以这是两个唯一的邮件和一个一个用户总共发送了六个。这就是您要查找的结果集。获取数据子集并尝试一下,这样您就不会永远等待它。
  • 我尝试使用 DISTINCT 函数,它确实有效……有点,因为出于某种原因,我为大多数用户获得了准确的数据,但其中一些甚至显示了 0 封独特的电子邮件尽管他们向 10,000 个联系人发送电子邮件……但在大多数情况下,它确实按预期工作。我也尝试过相同的查询,但仅限于最近几天,而且速度要快得多。我认为您的回答是准确的,但我遗漏了一些信息......在我们的数据库中我可能没有考虑其他变量。我会在星期一回到这个问题。非常感谢您的帮助
【解决方案3】:

我设法使用一个查询来做到这一点(除了出于隐私考虑更改实际表和列名之外)看起来完全像这样。

SELECT
    Accounts.Account_Name AS `account`,
    Usernames.Username AS `username`,
    COUNT(Mass_Mail_Reached_Contacts.ID_Contact) AS `total_emails`,
    COUNT(Mass_Mail_Reached_Contacts.ID_Mass_Mail) / 
        (
            SELECT COUNT(*)
                FROM 
                    Mass_Mail_Reached_Contacts 
                WHERE
                    Mass_Mail_Reached_Contacts.DATE >= '2019-02-01'
                    AND
                    Mass_Mail_Reached_Contacts.DATE <= '2019-02-28'
        )
    * 100 AS `%`,
    COUNT(DISTINCT Mass_Mail.ID_Mass_Mail) AS `unique_emails`,
    COUNT(Mass_Mail_Reached_Contacts.ID_Mass_Mail) / 
        COUNT(DISTINCT mass_mail.ID_Mass_Mail) 
        AS `avg_contacts_per_email`

FROM
    Usernames

LEFT JOIN Mass_Mail_Reached_Contacts ON Mass_Mail_Reached_Contacts.Username = Usernames.Username
LEFT JOIN Account ON Account.ID_Account = Usernames.ID_Account
LEFT JOIN Mass_Mail ON Mass_Mail.ID_Mass_Mail = Mass_Mail_Reached_Contacts.ID_mass_mail

WHERE
    Mass_Mail_Reached_Contacts.DATE >= '2019-02-01'
    AND
    Mass_Mail_Reached_Contacts.DATE <= '2019-02-28'

GROUP BY
    Usernames.Username

HAVING COUNT(DISTINCT Mass_Mail.IDMass_Mail) > 0

ORDER BY
    `total_emails` DESC

我现在可以得到一个看起来像这样的表

Emails Stats
--------------------------------------------------------------------------------------
| account  | username     | total_emails |     % | unique_emails | avg_contact_email | 
|----------|--------------|--------------|-------|------------------------------------
| Bob inc. | bob@mail.com | 28,550       | 14.52 |            12 |           2379.17 |
| ...      | ...          | ...          |   ... |           ... |               ... |
--------------------------------------------------------------------------------------

【讨论】:

    【解决方案4】:

    首先:为什么Mass_MailSent_Mail_Contacts 都包含Username?这看起来是多余的。还是Sent_Mail_Contacts.ID_Mass_Mail 可以为空?

    至少对于这个查询,我想我们可以完全忽略Sent_Mail_Contacts 中的Username。真正链接这两个表的是ID_Mass_Mail,而您在查询中忘记了这个连接条件。

    select
      ws_concat(' ', u.firstname, u.lastname) as full_name,
      u.username,
      count(smc.idmass_mail) as total_sent,
      count(mm.idmass_mail) as individual_e_mails
    from usernames u
    left join mass_mail mm on mm.username = u.username
    left join sent_mail_contacts smc on smc.id_mass_mail = u.id_mass_mail
    group by u.username
    order by total_sent;
    

    【讨论】:

    • 谢谢。我设法通过使用 ID_Mass_Mail 而不是用户名来加入这两个表来实现我的目标,并且它起作用了。此外,我改进了查询并设法将处理时间缩短到 12 秒。
    猜你喜欢
    • 2012-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-29
    • 1970-01-01
    • 1970-01-01
    • 2015-12-02
    • 2015-06-10
    相关资源
    最近更新 更多