MYSQL:计算值在结果中出现的次数,而无需递归运行查询

时间:2019-02-27 00:20:37

标签: mysql sql join group-by count

我一直在搜索和查看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在03-03上有1个服务到期,在03-10上有143的服务到期。

我需要计算每个用户出现在列表中的次数,因为我正在寻找截止日期超过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      |

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

对于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