2014-02-12 2 views
1

Postgres 9.3의 사용자 활동 로그 테이블에 분석 쿼리를 작성했습니다. 가입 날짜, 데이터 필드 (합계 가능) 및 사용자 유형이 있습니다. 이 문제에 대한 몇 가지 샘플 데이터/sql을 구축했으며 마지막 부분을 파악하는 데 도움이되기를 바랍니다. 테스트해야하는 SQL은 아래에 있습니다. facts라는 테이블을 삭제/작성하므로 샌드 박스에서 작업하십시오.쿼리별로 그룹의 모든 날짜를 순서대로 가져 오는 방법은 무엇입니까?

나는 주와 사용자 유형별로 데이터를 집계하므로 매주 각 사용자 유형에 대한 데이터 필드의 수를 얻을 수 있습니다. 내가 가진 문제는 사용자 유형 = 'x'에 대해 1 주일 누락 된 결과가 나오는 것입니다. 사용자 유형 'x'에 대해 9-9-13 주에 사용자 데이터가 없으므로 행이 표시되지 않습니다 (아래 샘플 결과 참조). 해당 사용자 유형과 주에 대한 행이 있어야합니다. 가능하면 단일 SELECT 문을 사용하여 임시 또는 차원 테이블을 사용하여이 작업을 수행하고 싶습니다. (비즈니스 관리자에게이 sql을 전달하고 단일 자체 SQL SELECT 문을 전달할 것이기 때문에 희망이 있습니다. 결과는 내가 얻을이 방법에 대한 비판은) 환영하지만 대답 어떤 도움을 여러분 모두 감사합니다 여기에

은 다음과 같습니다.!

여기
 
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 select 문입니다 :

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

감사합니다! 내부 데이터 조인을 "가상"차원 테이블로 사용하여 외부 조인을 사용하면 소스 데이터에 관계없이 모든 날짜 * 레코드 유형 행 조합을 강제로 사용하는 것이 좋습니다. 정답으로 동의합니다. b/c Gordon Linoff의 SQL은 차원 테이블이 기본 테이블이고 팩트 테이블이 조인 된 테이블 인 왼쪽 외부 조인을 사용하여 읽는 것이 약간 더 어렵습니다. 오른쪽 바깥 쪽 조인을 사용하는 시스템이 이해하기 쉽다고 생각합니다. 두 대답은 모두 같다고 생각합니다. –

+0

@Steve 예 그들은 동일합니다. 내 답변에는 'extract'를 연결하는 것보다 사용하기 쉬운'date_trunc'를 사용하는 것이 하나 더 자세히 나와 있습니다. 또한 최종 세트에만 적용됩니다. –

+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 주위에 자신을 연주 한 후, 나는 또한 작동 다른 해결책이있다. 나는이 쿼리 클로도 알도 네토 나 고든 리노 프의보다 성능이 좋은 것입니다 확신 해요,하지만 난뿐만 아니라 문제를 해결 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 
관련 문제