【问题标题】:MYSQL: Count the number of times a value appears in results without running the query recursivelyMYSQL:计算值出现在结果中的次数而不递归运行查询
【发布时间】:2019-02-27 00:20:37
【问题描述】:

我一直在谷歌搜索和查看 SO 帖子,但仍不确定如何完成此操作。

我有一个按(用户、截止日期)分组的结果表, 计算每个用户每个到期日的项目数。

这里是查询:

SELECT 
    userid as user, 
    nextduedate as due_date, 
    count(th.id) as services 
FROM 
    `tblhosting` th 
    JOIN `tblcustomfieldsvalues` tcfv on th.userid = tcfv.relid
    JOIN `tblclients` tc on th.userid = tc.id
WHERE 
    th.domainstatus = 'Active' 
    AND (th.nextduedate > date(DATE_SUB(curdate(), INTERVAL 5 day)) AND th.nextduedate < date(DATE_ADD(curdate(), INTERVAL 1 month))) 
    AND th.packageid NOT IN (132, 130, 129)
    AND tcfv.fieldid = 55
    AND tcfv.value = "on"
    AND tc.separateinvoices = 0
GROUP BY userid, nextduedate
ORDER BY userid asc

结果:

| user | due_date   | services |
|------|------------|----------|
| 77   | 2019-03-10 | 4        |
| 81   | 2019-03-05 | 23       |
| 99   | 2019-03-10 | 97       |
| 455  | 2019-03-13 | 9        |
| 478  | 2019-03-10 | 18       |
| 491  | 2019-03-03 | 1        |
| 491  | 2019-03-10 | 143      |
| 541  | 2019-03-02 | 2        |
| 541  | 2019-03-10 | 68       |
| 575  | 2019-03-02 | 46       |

用户 491 有 1 项服务将于 03-03 到期,143 将于 03-10 到期。

我需要计算每个用户出现在列表中的次数,因为我专门寻找到期日超过 1 个的用户。

理论上这实际上很容易,因为我可以像这样进行外部选择:

SELECT userid, COUNT(*) 
FROM (inner select) a
GROUP BY a.userid

这会给我:

| user | count(userid)|
|------|--------------|
| 77   | 1            |
| 81   | 1            |
| 99   | 1            |
| 455  | 1            |
| 478  | 1            |
| 491  | 2            |
| 541  | 2            |
| 575  | 1            |

然后我可以将此结果加入原始结果,但它需要运行两次查询。像

Select * FROM 
(

  Inner Select a
    LEFT JOIN 
    (
    SELECT userid, COUNT(*) FROM 
    (inner select) a
    GROUP BY a.userid
    ) b ON a.userid = b.userid 
  where x and y
) c

有了这个,我必须运行原始选择(作为内部选择),对其进行分组和计数(以获取计数),然后将其加入到原始选择中,这非常低效并且成倍地增加了运行时间。

为了效率,我想通过引用结果集来统计每个用户在原始结果中出现的次数。我需要为每个用户保留不同的截止日期,所以我不能简单地按用户 ID 分组。

理想情况下应该是这样的:

| user | due_date   | services | counts |
|------|------------|----------|--------|
| 77   | 2019-03-10 | 4        | 1      |
| 81   | 2019-03-05 | 23       | 1      |
| 99   | 2019-03-10 | 97       | 1      |
| 455  | 2019-03-13 | 9        | 1      |
| 478  | 2019-03-10 | 18       | 1      |
| 491  | 2019-03-03 | 1        | 2      |
| 491  | 2019-03-10 | 143      | 2      |
| 541  | 2019-03-02 | 2        | 2      |
| 541  | 2019-03-10 | 68       | 2      |
| 575  | 2019-03-02 | 46       | 1      |

感谢您的帮助!

【问题讨论】:

    标签: mysql sql join group-by count


    【解决方案1】:

    在 MySQL 8.0 中,使用窗口函数:

    SELECT t.*, COUNT(*) OVER(PARTITION BY t.user) AS counts
    FROM (
        -- your query
    ) AS t
    

    对于旧版本的 MySQL,窗口函数和公用表表达式都不可用。我将在两个不同(尽管几乎相同)子查询中计算两个聚合级别的结果,然后 JOIN 他们的结果:

    SELECT t1.*, t2.counts
    FROM (
        SELECT userid as user, nextduedate as due_date, count(th.id) as services 
        FROM 
            `tblhosting` th 
            JOIN `tblcustomfieldsvalues` tcfv on th.userid = tcfv.relid
            JOIN `tblclients` tc on th.userid = tc.id
        WHERE 
            th.domainstatus = 'Active' 
            AND (th.nextduedate > date(DATE_SUB(curdate(), INTERVAL 5 day)) AND th.nextduedate < date(DATE_ADD(curdate(), INTERVAL 1 month))) 
            AND th.packageid NOT IN (132, 130, 129)
            AND tcfv.fieldid = 55 and tcfv.value = "on"
            AND tc.separateinvoices = 0
        GROUP BY userid, nextduedate
    ) t1 INNER JOIN (
        SELECT userid, count(th.id) as counts 
        FROM 
            `tblhosting` th 
            JOIN `tblcustomfieldsvalues` tcfv on th.userid = tcfv.relid
            JOIN `tblclients` tc on th.userid = tc.id
        WHERE 
            th.domainstatus = 'Active' 
            AND (th.nextduedate > date(DATE_SUB(curdate(), INTERVAL 5 day)) AND th.nextduedate < date(DATE_ADD(curdate(), INTERVAL 1 month))) 
            AND th.packageid NOT IN (132, 130, 129)
            AND tcfv.fieldid = 55 and tcfv.value = "on"
            AND tc.separateinvoices = 0
        GROUP BY userid
    ) t2 ON t1.userid = t2.userid
    ORDER BY t1.userid
    

    【讨论】:

    • 哇,这让我对 MySQL 8.0 所提供的东西感到非常兴奋,谢谢!我应该提到我们的环境需要 5.7,并且在我们的 3rd 方软件支持 8.0 之前无法升级:/
    • @Andrew : 太糟糕了......好吧,我用 MySQL
    • 这就是我所担心的,感谢您的更新。我非常感谢您提到 CTE 和 Window 函数作为更好解决方案的选项,这些对我来说都是新的,并为我指明了学习/研究的新地方。此外,使用内部连接的完整解决方案比我想出的(多个外部选择)要好,所以脱帽致敬,非常感谢您,先生!干杯
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-10
    • 2012-08-06
    • 2022-01-06
    • 2019-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多