перекрестное соединение, чтобы получить все даты и часы и избежать дублирования - PullRequest
0 голосов
/ 27 сентября 2018

У нас есть 2 таблицы:

  • продажа
  • час (только 1 поле (час) чисел: от 0 до 23)

Цельэто перечислить все даты и все 24 часа для каждого дня и групповых часов, которые имеют продажи.Для часов, которые не имеют продаж, будет показан ноль.

Этот перекрестный запрос соединяет таблицу продаж с таблицей часов и содержит список всех дат и 24 часов.Тем не менее, есть также много повторяющихся строк.Как мы можем избежать дубликатов?

Мы используем Amazon Redshift (на основе Postgres 8.0).

with h as (
SELECT
    a.purchase_date,
    CAST(DATE_PART("HOUR", AT_TIME_ZONE(AT_TIME_ZONE(CAST(a.purchase_date AS 
    DATETIME), "0:00"), "PST")) as INTEGER) AS Hour,
    COUNT(a.quantity) AS QtyCount,
    SUM(a.quantity) AS QtyTotal,
    SUM((a.price) AS Price
FROM sales a    
GROUP BY CAST(DATE_PART("HOUR", 
AT_TIME_ZONE(AT_TIME_ZONE(CAST(a.purchase_date AS DATETIME), "0:00"), 
"PST")) as INTEGER), 
DATE_FORMAT(AT_TIME_ZONE(AT_TIME_ZONE(CAST(a.purchase_date AS DATETIME), 
"0:00"), "PST"), "yyyy-MM-dd")
ORDER by a.purchase_date
),
hr as (
     SELECT
          CAST(hourt AS INTEGER) AS hourt
     FROM hourt
),
joined as (
     SELECT
          purchase_date,
          hourt,
          QtyCount,
          QtyTotal,
          Price
     FROM h
     cross JOIN hr
)
SELECT *
     FROM joined
Order by purchase_date,hourt

Образцы таблиц:

Перед перекрестным объединением запроситевернули правильные продажи и сгруппированные часы, как показано в таблице ниже.

enter image description here

Таблица желаемых результатов:

enter image description here

Ответы [ 2 ]

0 голосов
/ 30 сентября 2018

Я достиг желаемых результатов, используя левое соединение (таблица A с таблицей B) вместо перекрестного соединения этих двух таблиц:

  • В таблице A есть все даты и часы
  • Таблица B является первой частью исходного запроса
0 голосов
/ 27 сентября 2018

Необходимо создать серию из всех значений часов и оставить присоединенные данные к этому.Встроенные комментарии объясняют логику.

WITH data AS (-- Do the basic aggregation first
    SELECT DATE_TRUNC('hour',a.purchase_date) purchase_hour --Truncate timestamp to the hour is simpler
        ,COUNT(a.quantity) AS QtyCount
        ,SUM(a.quantity)   AS QtyTotal
        ,SUM((a.price)     AS Price
    FROM sales a
    GROUP BY DATE_TRUNC('hour',a.purchase_date)
    ORDER BY DATE_TRUNC('hour',a.purchase_date)
    --           SELECT '2017-01-13 12:00:00'::TIMESTAMP purchase_hour, 1 qty_count, 1 qty_total, 119 price
    -- UNION ALL SELECT '2017-01-13 15:00:00'::TIMESTAMP purchase_hour, 1 qty_count, 1 qty_total, 119 price
    -- UNION ALL SELECT '2017-01-14 21:00:00'::TIMESTAMP purchase_hour, 1 qty_count, 1 qty_total, 119 price
    )
,time_range AS (--Calculate the start and end **date** values
    SELECT DATE_TRUNC('day',MIN(purchase_hour))   start_date
         , DATE_TRUNC('day',MAX(purchase_hour))+1 end_date
     FROM data
    )
,hr AS (--Generate all hours between start and end
    SELECT (SELECT start_date
            FROM time_range
            LIMIT 1) --Limit 1 so the optimizer knows it's not a correlated subquery
           + ((n-1) --Make the series start at zero so we don't miss the starting value
             * INTERVAL '1 hour') AS "hour"
    FROM (SELECT ROW_NUMBER() OVER () n
         FROM stl_query --Can use any table here as long as it enough rows
         LIMIT 100) series
    WHERE "hour" < (SELECT end_date FROM time_range LIMIT 1)
    )
--Use NVL to replace missing values with zeroes
SELECT hr.hour                AS purchase_hour --Timestamp like `2017-01-13 12:00:00`
     , NVL(data.qty_count, 0) AS qty_count
     , NVL(data.qty_total, 0) AS qty_total
     , NVL(data.price, 0)     AS price
FROM hr
LEFT JOIN data
ON hr.hour = data.purchase_hour
ORDER BY hr.hour
;
...