2017-09-13 18 views
5

私は以下の表を持っている:PostgreSQLの別のテーブルから日付範囲+前の日付を生成するには?

links

created_at   active 
2017-08-12 15:46:01 false 
2017-08-13 15:46:01 true 
2017-08-14 15:46:01 true 
2017-08-15 15:46:01 false 

日付範囲を指定したとき、私はアクティブリンクが等しいか、現在よりも小さい日付で作成されたどのように多くの私に告げる時系列を抽出する必要があり(ローリング)日付。 (日付範囲2017年8月12日のために - 2017年8月17日)

出力:

day   count 
2017-08-12 0 (there are 0 active links created on 2017-08-12 and earlier) 
2017-08-13 1 (there is 1 active link created on 2017-08-13 and earlier) 
2017-08-14 2 (there are 2 active links created on 2017-08-14 and earlier) 
2017-08-15 2 ... 
2017-08-16 2 
2017-08-17 2 

私は日付を生成するための次のクエリを思い付いた:

SELECT date_trunc('day', dd):: date 
FROM generate_series 
    ('2017-08-12'::timestamp 
    , '2017-08-17'::timestamp 
    , '1 day'::interval) dd 

しかし、ローリングカウントは私を混乱させ、どのように続行するかわかりません。これはウィンドウ関数で解決できますか?

答えて

1

これが最速でなければなりません:あなたはcount(active OR NULL)を使用することができるようにhere

count() dbfiddle

SELECT day::date 
    , sum(ct) OVER (ORDER BY day) AS count 
FROM generate_series (timestamp '2017-08-12' 
         , timestamp '2017-08-17' 
         , interval '1 day') day 
LEFT JOIN (
    SELECT date_trunc('day', created_at) AS day, count(*) AS ct 
    FROM tbl 
    WHERE active -- fastest 
    GROUP BY 1 
    ) t USING (day) 
ORDER BY 1; 

は、null以外の行をカウントします。しかし、カウントのための最も速いオプションは、最初にWHERE句を持つ無関係な行を除外することです。とにかくgenerate_series()ですべての日を追加しているので、これが最適なオプションです。

は比較:私は(非常にわずかに速い)に一致するタイムスタンプを取得するためにdate_trunc()を使用

(ないdatetimestamp戻りgenerate_series()ので。

1

私はちょうど集約し、累積和を使用する - あなたは日ごとに少なくとも1つを持っていると仮定すると:

select date_trunc('day', created_at)::date as created_date, 
     sum(active::int) as actives, 
     sum(sum(active::int)) over (date_trunc('day', created_at)) as running_actives 
from t 
group by created_date; 

あなたはデータの穴を持っている場合は、日付だけを生成する必要があります。あなたがしても、私はwhere activeを含むことをお勧めします - あなたは今それを含めることができます、私はちょうど穴がないことを確認したいです。

+0

はい、穴がありますが、いくつかの日がありません。当時の私は、存在する最新の前回の日付を数えなければなりません。 –

0

私はこのようなクエリはあなたを助けることができると思う:

;with t as (SELECT date_trunc('day', dd):: date 
FROM generate_series 
    ('2017-08-12'::timestamp 
    , '2017-08-17'::timestamp 
    , '1 day'::interval) dd 
) 
select distinct t.date_trunc 
    , count(case when links.active = 'true' then 1 end) over (order by links.created_at) count 
from t 
left join links 
on t.date_trunc = cast(links.created_at as date) 
order by t.date_trunc; 

SQL Fiddle Demo

0

あなたのテーブルで不足している日を持っている場合は、あなたがそれらを作成するためにgenerate_series()を使用する必要があります。

しかし、この結合はGROUP BYの後に行われるほうが良いでしょう。これは前回ではなく1日1行だけを返すので、結果として大きなJOIN。

WITH dailydata AS (
    SELECT 
    d::DATE, COALESCE(n,0) n 
    FROM 
    generate_series( 
     '2000-01-01'::DATE, 
     '2000-10-01'::DATE, 
     '1 DAY'::INTERVAL) d 
    LEFT JOIN 
    (SELECT created_at::DATE d, count(*) AS n 
    FROM links WHERE active 
    GROUP BY d) data 
    USING (d) 
) 
SELECT d, n, sum(n) OVER (ORDER BY d) FROM dailydata; 
0
CREATE TABLE links 
     (created_at   timestamp 
     , active boolean 
     ); 
INSERT INTO links(created_at,active)VALUES 
('2017-08-12 15:46:01', false) 
,('2017-08-13 15:46:01', true) 
,('2017-08-14 15:46:01', true) 
,('2017-08-15 15:46:01', false) 
     ; 

WITH cal AS (
     select gs AS deet 
     FROM generate_series('2017-08-11'::date,'2017-08-16'::date, '1day'::interval)gs 
     ) 
SELECT cal.deet 
     , SUM(1) FILTER (WHERE l.active =True) OVER(ORDER BY l.created_at) AS cumsum 
FROM cal 
LEFT JOIN links l ON date_trunc('days', l.created_at)= cal.deet 
ORDER BY created_at 
     ; 
1

デモ

http://rextester.com/OGZV44492

SQL

SELECT date_trunc('day', dd):: date AS day, 
     (SELECT COUNT(*) FROM links 
     WHERE active = true 
      AND date(created_at) <= date_trunc('day', dd)) AS "count" 
FROM generate_series 
    ('2017-08-12'::timestamp 
    , '2017-08-17'::timestamp 
    , '1 day'::interval) dd 

説明

は、上記SQLは、その日付部未満又は発生範囲内の各日付に等しいlinksテーブル内の行の数をカウントする単純な副選択を行います。

+1

私はこの1つを本当に好きです!スティーブに感謝します。 –

+0

私はこのクエリが非常に大きなテーブルのためにうまくスケールされないことに気づき、アーウィンの答えを選択しました。 –

関連する問題