Сгруппировать с выбором макс. Для части группы - PullRequest
1 голос
/ 17 января 2020

Это запрос ClickHouse, но я думаю, что мне не хватает базового sql решения. Я хочу посчитать максимальное количество строк в минуту в течение каждого дня:

SELECT date_date,
       formatDateTime(date_time,'%F %H:%M:00', 'Asia/Dubai') AS Max_TPS_Time,
       count(1) AS Max_TPS
FROM TEST
GROUP BY Max_TPS_Time,
         date_date

Что приводит к

date_date   Max_TPS_Time    Max_TPS
19/11/19    2019-11-19 11:37:00 4
19/11/19    2019-11-19 11:15:00 2
19/11/19    2019-11-19 11:40:00 5
19/11/18    2019-11-18 12:37:00 4
19/11/18    2019-11-18 12:15:00 2
19/11/18    2019-11-18 12:40:00 6

И в конце я хочу увидеть максимальное значение в день с соответствующей датой и временем ,

date_date   Max_TPS_Time    Max_TPS
19/11/19    2019-11-19 11:40:00 5
19/11/18    2019-11-18 12:40:00 6

Ответы [ 3 ]

1 голос
/ 17 января 2020

Я бы упростил / оптимизировал ваши запросы следующим образом:

SELECT
    toDate(max_tps_time) AS date_date,
    toTimeZone(toStartOfMinute(utc_date_time), 'Asia/Dubai') AS max_tps_time,
    count() AS max_tps
FROM
( /* test data */
    SELECT arrayJoin([
      toDateTime('2019-11-19 07:37:01', 'UTC'), toDateTime('2019-11-19 07:37:11', 'UTC'), toDateTime('2019-11-19 07:37:21', 'UTC'), toDateTime('2019-11-19 07:37:31', 'UTC'), 
      toDateTime('2019-11-18 08:15:21', 'UTC'), toDateTime('2019-11-18 08:15:42', 'UTC'),
      toDateTime('2019-11-19 07:40:01', 'UTC'), toDateTime('2019-11-19 07:40:11', 'UTC'), toDateTime('2019-11-19 07:40:21', 'UTC'), toDateTime('2019-11-19 07:40:31', 'UTC'), toDateTime('2019-11-19 07:40:41', 'UTC'), 
      toDateTime('2019-11-18 08:40:18', 'UTC'), toDateTime('2019-11-18 08:40:20', 'UTC'), toDateTime('2019-11-18 08:40:22', 'UTC'), toDateTime('2019-11-18 08:40:24', 'UTC'), toDateTime('2019-11-18 08:40:26', 'UTC'), toDateTime('2019-11-18 08:40:28', 'UTC')]) AS utc_date_time
)
GROUP BY max_tps_time
ORDER BY max_tps_time;

/* result
┌──date_date─┬────────max_tps_time─┬─max_tps─┐
│ 2019-11-18 │ 2019-11-18 12:15:00 │       2 │
│ 2019-11-18 │ 2019-11-18 12:40:00 │       6 │
│ 2019-11-19 │ 2019-11-19 11:37:00 │       4 │
│ 2019-11-19 │ 2019-11-19 11:40:00 │       5 │
└────────────┴─────────────────────┴─────────┘
*/
SELECT 
  toDate(max_tps_time) AS date_date, 
  argMax(max_tps_time, max_tps) max_tps_time_by_day,
  max(max_tps) max_tps_by_day
FROM (
  SELECT
      toTimeZone(toStartOfMinute(utc_date_time), 'Asia/Dubai') AS max_tps_time,
      count() AS max_tps
  FROM
  ( /* test data */
      SELECT arrayJoin([
        toDateTime('2019-11-19 07:37:01', 'UTC'), toDateTime('2019-11-19 07:37:11', 'UTC'), toDateTime('2019-11-19 07:37:21', 'UTC'), toDateTime('2019-11-19 07:37:31', 'UTC'), 
        toDateTime('2019-11-18 08:15:21', 'UTC'), toDateTime('2019-11-18 08:15:42', 'UTC'),
        toDateTime('2019-11-19 07:40:01', 'UTC'), toDateTime('2019-11-19 07:40:11', 'UTC'), toDateTime('2019-11-19 07:40:21', 'UTC'), toDateTime('2019-11-19 07:40:31', 'UTC'), toDateTime('2019-11-19 07:40:41', 'UTC'), 
        toDateTime('2019-11-18 08:40:18', 'UTC'), toDateTime('2019-11-18 08:40:20', 'UTC'), toDateTime('2019-11-18 08:40:22', 'UTC'), toDateTime('2019-11-18 08:40:24', 'UTC'), toDateTime('2019-11-18 08:40:26', 'UTC'), toDateTime('2019-11-18 08:40:28', 'UTC')]) AS utc_date_time
  )
  GROUP BY max_tps_time)
GROUP BY date_date  
ORDER BY date_date;

/* result
┌──date_date─┬─max_tps_time_by_day─┬─max_tps_by_day─┐
│ 2019-11-18 │ 2019-11-18 12:40:00 │              6 │
│ 2019-11-19 │ 2019-11-19 11:40:00 │              5 │
└────────────┴─────────────────────┴────────────────┘
*/

Эти запросы можно объединить в один запрос с помощью ROLLUP . Результат будет содержать два промежуточных результата для «ежедневных» и «минутных» агрегатов:

SELECT 
  toDate(max_tps_time) AS date_date, 
  argMax(max_tps_time, max_tps) max_tps_time_by_day,
  max(max_tps) max_tps_by_day,
  toInt32(max_tps_time) = 0 ? 1 : 0 is_daily_aggregate 
FROM (
  SELECT
      toTimeZone(toStartOfMinute(utc_date_time), 'Asia/Dubai') AS max_tps_time,
      count() AS max_tps
  FROM
  ( /* test data */
      SELECT arrayJoin([
        toDateTime('2019-11-19 07:37:01', 'UTC'), toDateTime('2019-11-19 07:37:11', 'UTC'), toDateTime('2019-11-19 07:37:21', 'UTC'), toDateTime('2019-11-19 07:37:31', 'UTC'), 
        toDateTime('2019-11-18 08:15:21', 'UTC'), toDateTime('2019-11-18 08:15:42', 'UTC'),
        toDateTime('2019-11-19 07:40:01', 'UTC'), toDateTime('2019-11-19 07:40:11', 'UTC'), toDateTime('2019-11-19 07:40:21', 'UTC'), toDateTime('2019-11-19 07:40:31', 'UTC'), toDateTime('2019-11-19 07:40:41', 'UTC'), 
        toDateTime('2019-11-18 08:40:18', 'UTC'), toDateTime('2019-11-18 08:40:20', 'UTC'), toDateTime('2019-11-18 08:40:22', 'UTC'), toDateTime('2019-11-18 08:40:24', 'UTC'), toDateTime('2019-11-18 08:40:26', 'UTC'), toDateTime('2019-11-18 08:40:28', 'UTC')]) AS utc_date_time
  )
  GROUP BY max_tps_time)
GROUP BY date_date, max_tps_time WITH ROLLUP
HAVING toInt32(date_date) != 0
ORDER BY max_tps_time, max_tps_time_by_day;

/* result
┌──date_date─┬─max_tps_time_by_day─┬─max_tps_by_day─┬─is_daily_aggregate─┐
│ 2019-11-18 │ 2019-11-18 12:40:00 │              6 │                  1 │<-- daily aggregate
│ 2019-11-19 │ 2019-11-19 11:40:00 │              5 │                  1 │<-- daily aggregate
│ 2019-11-18 │ 2019-11-18 12:15:00 │              2 │                  0 │<-- minute aggregate
│ 2019-11-18 │ 2019-11-18 12:40:00 │              6 │                  0 │<-- minute aggregate
│ 2019-11-19 │ 2019-11-19 11:37:00 │              4 │                  0 │<-- minute aggregate
│ 2019-11-19 │ 2019-11-19 11:40:00 │              5 │                  0 │<-- minute aggregate
└────────────┴─────────────────────┴────────────────┴────────────────────┘
*/
0 голосов
/ 17 января 2020
SELECT date_date,
       formatDateTime(date_time,'%F %H:%M:00', 'Asia/Dubai') AS Max_TPS_Time,
       count() AS Max_TPS
FROM TEST GROUP BY Max_TPS_Time, date_date
order by Max_TPS_Time, date_date, Max_TPS desc 
limit 1 by Max_TPS_Time, date_date


select argMax(date_date, Max_TPS) date_date,  
argMax(Max_TPS_Time, Max_TPS) Max_TPS_Time, 
Max_TPS
from (
   SELECT date_date,
       formatDateTime(date_time,'%F %H:%M:00', 'Asia/Dubai') AS Max_TPS_Time,
       count() AS Max_TPS
   FROM TEST GROUP BY Max_TPS_Time, date_date)
0 голосов
/ 17 января 2020

Вы ищете первую 1 запись в группе из результата вашего совокупного запроса.

Насколько я знаю, clickhouse не поддерживает оконные функции (что облегчило бы эту задачу).

Но он поддерживает общие табличные выражения: так что вы можете превратить существующий запрос в CTE, а затем отфильтровать с помощью самокоррелированного подзапроса:

WITH cte as (
    SELECT 
        date_date,
        formatDateTime(date_time,'%F %H:%M:00', 'Asia/Dubai') AS Max_TPS_Time,
        count(*) AS Max_TPS
    FROM TEST
    GROUP BY Max_TPS_Time, date_date
)
SELECT c.*
FROM cte c
WHERE c.Max_TPS = (SELECT MAX(c1.Max_TPS) FROM cte c1 WHERE c1.date_date = c.date_date)

Обратите внимание, что если вы просто хотите Max_TPS в день, но вам не нужно соответствующее Max_TPS_Time, тогда это проще, вы можете просто добавить еще один уровень агрегации к вашему текущему запросу:

SELECT date_date, MAX(Max_TPS) Max_TPS
FROM (
    SELECT 
        date_date,
        formatDateTime(date_time,'%F %H:%M:00', 'Asia/Dubai') AS Max_TPS_Time,
        count(*) AS Max_TPS
    FROM TEST
    GROUP BY Max_TPS_Time, date_date
) t
GROUP BY date_date

РЕДАКТИРОВАТЬ

Если вышеуказанное решение CTE не работает в clickhouse, вы можете повторить агрегированный запрос, например, так:

SELECT t.*
FROM (
    SELECT 
        date_date,
        formatDateTime(date_time,'%F %H:%M:00', 'Asia/Dubai') AS Max_TPS_Time,
        count(*) AS Max_TPS
    FROM TEST
    GROUP BY Max_TPS_Time, date_date
) t
WHERE t.Max_TPS = (
    SELECT count(*)
    FROM TEST t
    GROUP BY formatDateTime(date_time,'%F %H:%M:00', 'Asia/Dubai')
    WHERE t.date_date = c.date_date
)
...