Вы хотите использовать агрегат count
как функцию окна, например count(id) over (partition by event_date rows 3 preceeding)
... но это сильно осложняется характером ваших данных. Вы сохраняете временные метки, а не только даты, и хотите группировать по день не по количеству предыдущих событий. В довершение всего, вы хотите перекрестно перечислить результаты.
Если PostgreSQL поддерживает RANGE
в функциях окна, это будет значительно проще, чем есть. Как бы то ни было, вы должны сделать это с трудом.
Вы можете фильтровать, что через окно, чтобы получить за событие за день отставал отсчеты ... кроме того, что ваши дни события не являются смежными и, к сожалению, функция PostgreSQL окон поддерживают только ROWS
, не RANGE
, поэтому у вас есть сначала присоединиться к сгенерированной серии дат.
WITH
/* First, get a listing of event counts by day */
event_days(event_name, event_day, event_day_count) AS (
SELECT event_name, date_trunc('day', event_date), count(id)
FROM Table1
GROUP BY event_name, date_trunc('day', event_date)
ORDER BY date_trunc('day', event_date), event_name
),
/*
* Then fill in zeros for any days within the range that didn't have any events.
* If PostgreSQL supported RANGE windows, not just ROWS, we could get rid of this/
*/
event_days_contiguous(event_name, event_day, event_day_count) AS (
SELECT event_names.event_name, gen_day, COALESCE(event_days.event_day_count,0)
FROM generate_series((SELECT min(event_day)::date FROM event_days), (SELECT max(event_day)::date FROM event_days), INTERVAL '1' DAY) gen_day
CROSS JOIN (SELECT DISTINCT event_name FROM event_days) event_names(event_name)
LEFT OUTER JOIN event_days ON (gen_day = event_days.event_day AND event_names.event_name = event_days.event_name)
),
/*
* Get the lagged counts by using the sum() function over a row window...
*/
lagged_days(event_name, event_day_first, event_day_last, event_days_count) AS (
SELECT event_name, event_day, first_value(event_day) OVER w, sum(event_day_count) OVER w
FROM event_days_contiguous
WINDOW w AS (PARTITION BY event_name ORDER BY event_day ROWS 1 PRECEDING)
)
/* Now do a manual pivot. For arbitrary column counts use an external tool
* or check out the 'crosstab' function in the 'tablefunc' contrib module
*/
SELECT d1.event_day_first, d1.event_days_count AS "Event_A", d2.event_days_count AS "Event_B"
FROM lagged_days d1
INNER JOIN lagged_days d2 ON (d1.event_day_first = d2.event_day_first AND d1.event_name = 'event_A' AND d2.event_name = 'event_B')
ORDER BY d1.event_day_first;
Выход с данными выборки:
event_day_first | Event_A | Event_B
------------------------+---------+---------
2013-04-24 00:00:00+08 | 2 | 1
2013-04-25 00:00:00+08 | 4 | 1
2013-04-26 00:00:00+08 | 3 | 0
2013-04-27 00:00:00+08 | 2 | 1
(4 rows)
Вы можете потенциально сделать запрос быстрее, но гораздо уродливее путем объединения трех КТР положения в вложенный запрос с использованием FROM (SELECT...)
и оборачивать их в представлении вместо CTE для использования из внешнего запроса. Это позволит Pg «превзойти» предикаты в запросах, значительно сокращая данные, с которыми вы должны работать при запросе подмножеств данных. кажется, не работает в данный момент
SQLFiddle, но вот демо настройки я использовал:
CREATE TABLE Table1
(id integer primary key, "event_date" timestamp not null, "event_name" text);
INSERT INTO Table1
("id", "event_date", "event_name")
VALUES
(101, '2013-04-24 18:33:37', 'event_A'),
(102, '2013-04-24 20:34:37', 'event_B'),
(103, '2013-04-24 20:40:37', 'event_A'),
(104, '2013-04-25 01:00:00', 'event_A'),
(105, '2013-04-25 12:00:15', 'event_A'),
(106, '2013-04-26 00:56:10', 'event_A'),
(107, '2013-04-27 12:00:15', 'event_A'),
(108, '2013-04-27 12:00:15', 'event_B');
Я изменил идентификатор последней записи от 107 до 108, как я подозреваю, что это просто ошибка в вашем ручном редактировании.
Вот как выразить это как вид вместо:
CREATE VIEW lagged_days AS
SELECT event_name, event_day AS event_day_first, sum(event_day_count) OVER w AS event_days_count
FROM (
SELECT event_names.event_name, gen_day, COALESCE(event_days.event_day_count,0)
FROM generate_series((SELECT min(event_date)::date FROM Table1), (SELECT max(event_date)::date FROM Table1), INTERVAL '1' DAY) gen_day
CROSS JOIN (SELECT DISTINCT event_name FROM Table1) event_names(event_name)
LEFT OUTER JOIN (
SELECT event_name, date_trunc('day', event_date), count(id)
FROM Table1
GROUP BY event_name, date_trunc('day', event_date)
ORDER BY date_trunc('day', event_date), event_name
) event_days(event_name, event_day, event_day_count)
ON (gen_day = event_days.event_day AND event_names.event_name = event_days.event_name)
) event_days_contiguous(event_name, event_day, event_day_count)
WINDOW w AS (PARTITION BY event_name ORDER BY event_day ROWS 1 PRECEDING);
Вы можете использовать представление в любой перекрестный запросы вы хотите написать. Он будет работать с предварительным запросом вручную перекрестном:
SELECT d1.event_day_first, d1.event_days_count AS "Event_A", d2.event_days_count AS "Event_B"
FROM lagged_days d1
INNER JOIN lagged_days d2 ON (d1.event_day_first = d2.event_day_first AND d1.event_name = 'event_A' AND d2.event_name = 'event_B')
ORDER BY d1.event_day_first;
... или с помощью crosstab
из tablefunc
расширения, которое я дам вам учиться на.
Для смеха, вот explain
на приведенном выше запросе вид на основе: http://explain.depesz.com/s/nvUq
Ваши ожидаемые результаты немного неправильно. Событие A для 25-го должно получиться 4, а не 3. –
@CraigRinger .. спасибо за указание. Я только что сделал исправление. – Anant