2017-09-13 28 views
5

Tôi có bảng sau:Cách tạo phạm vi ngày + đếm các ngày trước đó từ một bảng khác trong 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 

Khi được đưa ra một phạm vi ngày, tôi phải trích xuất theo chuỗi thời gian mà nói với tôi bao nhiêu liên kết tích cực được tạo ra vào một ngày bằng hoặc nhỏ hơn so với hiện tại (cán) ngày.

Output (đối với phạm vi ngày 2017/08/12 - 2017/08/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 

tôi đã đưa ra các truy vấn sau đây cho những ngày tạo:

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

Nhưng lăn đếm nhầm lẫn tôi và không chắc chắn làm thế nào để tiếp tục. Điều này có thể được giải quyết với một chức năng cửa sổ?

Trả lời

1

này cần được nhanh nhất:

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; 

dbfiddle here

count() chỉ đếm các hàng không được null, do đó bạn có thể sử dụng count(active OR NULL). Nhưng tùy chọn nhanh nhất để tính là loại trừ các hàng không có liên quan với mệnh đề WHERE để bắt đầu. Vì chúng tôi đang thêm tất cả các ngày với generate_series() dù sao, đây là tùy chọn tốt nhất.

Hãy so sánh:

Kể từ generate_series() lợi nhuận timestamp (không date) Tôi sử dụng date_trunc() để có được timestamps phù hợp (rất nhẹ nhanh hơn).

1

tôi sẽ chỉ cần sử dụng tập hợp và số tiền tích lũy - giả sử bạn có ít nhất một mỗi ngày:

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; 

Bạn chỉ cần để tạo ra ngày nếu bạn có lỗ hổng trong các dữ liệu. Tuy nhiên, nếu bạn thực hiện, tôi khuyên bạn nên bao gồm where active - bạn có thể bao gồm nó ngay bây giờ, tôi chỉ muốn chắc chắn rằng không có lỗ.

+0

Có, có lỗ hổng, một số ngày bị thiếu. Vì vậy, trong những ngày đó tôi phải tính số lượng ngày trước đó gần đây nhất. –

0

Tôi nghĩ rằng một truy vấn như thế này có thể giúp bạn:

;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

Nếu bạn có những ngày còn thiếu trong bảng của bạn, bạn sẽ cần phải sử dụng một generate_series() để tạo ra chúng . Vì điều này về cơ bản là tập hợp hai câu trả lời trước đây, nên có một khoản tín dụng ;;)

Tuy nhiên, việc tham gia này được thực hiện tốt hơn sau GROUP BY, sẽ chỉ trả về một hàng mỗi ngày, thay vì trước đó JOIN lớn hơn.

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

Demo

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 

Giải thích

SQL ở trên không một subselect đơn giản để đếm số lượng hàng trong links bảng có phần ngày là nhỏ hơn hoặc bằng cho mỗi ngày trong phạm vi được tạo ra.

+1

Tôi thực sự thích cái này! Cảm ơn Steve. –

+0

Tôi nhận ra truy vấn này không mở rộng tốt cho một bảng rất lớn, vì vậy tôi đã chọn câu trả lời của Erwin. –

Các vấn đề liên quan