2014-02-12 3 views
1

Я пишу аналитический запрос в таблице журнала действий пользователя в Postgres 9.3. Он имеет дату регистрации, поле данных (которое можно суммировать) и тип пользователя. Я построил некоторые примеры данных/sql для этой проблемы, и я надеюсь получить помощь в выяснении последней части. SQL, необходимый для тестирования, ниже - он будет падать/создавать таблицу, называемую фактами, поэтому обязательно работайте в изолированной программной среде.Как получить все даты в последовательности для группы по запросу?

Я суммирую данные по неделям и типу пользователя - так что вы получаете подсчет поля данных для каждого типа пользователя каждую неделю. Проблема в том, что я получаю результаты, которые не хватает на неделю для пользователя type = 'x'. Поскольку на странице 9-9-13 нет пользовательских данных для типа пользователя «x», не появляется строка (см. Примеры результатов ниже). Я бы хотел, чтобы там была строка для этого типа пользователя и недели. Я хотел бы выполнить это, если возможно, с помощью одного оператора select, без таблиц temp или измерений (это связано с тем, что я передам этот sql-менеджер бизнес-менеджеру, и один автономный SQL-запрос выбора, скорее всего, станет более глупым (.! критика этого подхода приветствуется, но не ответ) Спасибо всем за любую помощь

Вот результаты, которые я получаю:

 
Sum  test_week  user_type 
4 "2013-09-02" "x" 
5 "2013-09-02" "y" 
10 "2013-09-09" "y" 
2 "2013-09-16" "x" 
1 "2013-09-16" "y" 

Вот результаты, которые я хочу:

 
Sum  test_week  user_type 
4 "2013-09-02" "x" 
5 "2013-09-02" "y" 
0 "2013-09-09" "x" 
10 "2013-09-09" "y" 
2 "2013-09-16" "x" 
1 "2013-09-16" "y" 

Здесь это тестовые данные и оператор выбора SQL:

drop table if exists facts; 
create temp table facts (signup_date date, data integer, record_type varchar, alt varchar); 
insert into facts (signup_date, data, record_type) values 
('9/3/2013',1,'x'), 
('9/4/2013',1,'y'), 
('9/5/2013',2,'x'), 
('9/6/2013',3,'y'), 
('9/7/2013',1,'x'), 
('9/8/2013',1,'y'), 
-- note the week of 9/9 to 9/16 has no 'x' records 
('9/9/2013',2,'y'), 
('9/10/2013', 3, 'y'), 
('9/11/2013', 4, 'y'), 
('9/12/2013', 1, 'y'), 
('9/17/2013', 2, 'x'), 
('9/18/2013', 1, 'y'); 

select coalesce(data, 0), test_week, record_type 
    from 
    (select sum(data) as data, record_type, to_timestamp(EXTRACT(YEAR FROM signup_date) || ' ' || EXTRACT(WEEK FROM signup_date),'IYYY IW')::date as test_week 
    from facts 
    group by record_type, test_week 
    ) as facts 
    order by test_week, record_type 

ответ

1
select 
    coalesce(sum(data), 0) as "Sum", 
    to_char(date_trunc('week', c.signup_date), 'YYYY-MM-DD') as test_week, 
    c.record_type as user_type 
from 
    facts f 
    right join 
    (
     (
      select distinct record_type 
      from facts 
     ) f1 
     cross join 
     (
      select distinct signup_date 
      from facts 
     ) f2 
    ) c on f.record_type = c.record_type and f.signup_date = c.signup_date 
group by 2, 3 
order by 2, 3 
; 
Sum | test_week | user_type 
-----+------------+----------- 
    4 | 2013-09-02 | x 
    5 | 2013-09-02 | y 
    0 | 2013-09-09 | x 
    10 | 2013-09-09 | y 
    2 | 2013-09-16 | x 
    1 | 2013-09-16 | y 
+0

Спасибо! Ваш ответ имеет смысл использовать правое внешнее соединение с внутренним перекрестным объединением в качестве «виртуальной» таблицы размеров, чтобы принудительно использовать все комбинации строк * record_type независимо от исходных данных. Я принимаю ваш ответ в качестве правильного ответа. B/c SQL Gordon Linoff немного сложнее читать с использованием левого внешнего соединения, где таблица измерений является основной таблицей, а таблица фактов - объединенной таблицей. Я думаю, что ваша система с правильным внешним соединением легче понять. Думаю, оба ответа эквивалентны. –

+0

@Steve Да, они эквивалентны. В моем ответе есть еще одна деталь, которая заключается в использовании 'date_trunc', который проще в использовании, чем конкатенация' extract'. Также применяется только к окончательному набору. –

+0

Да, я заметил эти улучшения. Они - хорошая причина, почему ваше решение лучше, чем то, которое я разработал. Положив сумму на самый внешний запрос, вы избегаете уродства моего внутреннего креста (ответ ниже). Хороший SQL и еще раз спасибо! –

1

Для решения этой проблемы создайте список всех комбинаций всех record_type и всех недель тестов. Левые объединяются с этими комбинациями в таблицу фактических фактов. Это даст все записи, так что вы должны быть в состоянии получить строки, в которых нет никаких данных:

select coalesce(sum(f.data), 0) as data, rt.record_type, w.test_week 
from (select distinct record_type from facts) rt cross join 
    (select distinct to_timestamp(EXTRACT(YEAR FROM signup_date) || ' ' || EXTRACT(WEEK FROM signup_date),'IYYY IW')::date as test_week 
     from facts 
    ) w left outer join 
    facts f 
    on f.record_type = rt.record_type and 
     w.test_week = to_timestamp(EXTRACT(YEAR FROM f.signup_date) || ' ' || EXTRACT(WEEK FROM f.signup_date),'IYYY IW')::date 
group by rt.record_type, w.test_week 
order by w.test_week, rt.record_type; 
0

После игры вокруг с некоторыми SQL сам, я есть другое решение, которое также работает. Я уверен, что этот запрос является менее производительным, чем Clodoaldo А. Нет или Гордона Линофф, но я думал, что я хотел бы поделиться еще одной формой SQL, которая решает эту проблему, а также:

select coalesce(data, 0), rt as record_type, weeks 
    from 
     (select sum(data) as data, record_type, to_timestamp(EXTRACT(YEAR FROM signup_date) || ' ' || EXTRACT(WEEK FROM signup_date),'IYYY IW')::date as test_week 
     from facts 
     group by record_type, test_week 
     order by record_type, test_week) as facts 
    right join 
     (select distinct to_timestamp(EXTRACT(YEAR FROM signup_date) || ' ' || EXTRACT(WEEK FROM signup_date),'IYYY IW')::date as weeks, rts.rt as rt 
     from facts 
     cross join (select distinct record_type from facts) as rts (rt) 
     cross join (select distinct alt from facts) as alts (at)) as dates 
    on dates.weeks = facts.test_week 
    and dates.rt = facts.record_type 
Смежные вопросы