MYSQL: подсчитать, сколько раз значение появляется в результатах без рекурсивного выполнения запроса. - PullRequest
0 голосов
/ 27 февраля 2019

Я гуглял и просматривал 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.

Мне нужно посчитать, сколько раз каждый пользователь появляется в списке, поскольку я специально ищу пользователей с более чем одной датой исполнения.

Это на самом деле довольно легко в теории, так как я могу сделать внешний выбор, например, так:

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

При этом мне нужно запустить исходное выделение (как внутреннее выделение), сгруппировать и сосчитать его (чтобы получить счет), а затем соединить его с исходным выделением, что довольно неэффективно иувеличивает время выполнения в геометрической прогрессии.

Ради эффективности я хотел бы подсчитать, сколько раз каждый пользователь появляется в исходных результатах, ссылаясь на набор результатов.Мне нужно сохранить разные сроки выполнения для каждого пользователя, поэтому я не могу просто группировать по идентификатору пользователя.

В идеале это выглядело бы так:

| 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 голосов
/ 27 февраля 2019

В 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
...