2013-01-11 3 views
5

Tôi có một bảng postgres trông như thế này:SQL tổng giá trị cột, độc đáo cho mỗi người dùng mỗi ngày

id | user_id | state | created_at 

Tình trạng có thể là bất kỳ những điều sau đây:

new, paying, paid, completing, complete, payment_failed, completion_failed 

Tôi cần một tuyên bố trả lại báo cáo với các thông tin sau:

  1. tổng của tất cả các trạng thái thanh toán theo ngày
  2. tổng của tất cả các quốc gia hoàn thành vào ngày
  3. tổng của tất cả mới, thanh toán, hoàn thành tiểu bang theo ngày với chỉ có một cho mỗi người dùng mỗi ngày được tính
  4. tổng của tất cả payment_failed, completion_failed theo ngày với chỉ có một cho mỗi người dùng mỗi ngày được tính

cho đến nay tôi có điều này:

SELECT 
    DATE(created_at) AS date, 
    SUM(CASE WHEN state = 'complete' THEN 1 ELSE 0 END) AS complete, 
    SUM(CASE WHEN state = 'paid' THEN 1 ELSE 0 END) AS paid 
FROM orders 
WHERE created_at BETWEEN ? AND ? 
GROUP BY DATE(created_at) 

Một tổng của các cơ bản dở dang và các quốc gia thất bại là đủ dễ dàng bằng cách thêm này để chọn:

SUM(CASE WHEN state IN('new','paying','completing') THEN 1 ELSE 0 END) AS in_progress, 
SUM(CASE WHEN state IN('payment_failed','completion_failed') THEN 1 ELSE 0 END) AS failed 

Nhưng tôi đang gặp khó khăn trong việc tìm ra cách chỉ thực hiện một lần cho mỗi user_id mỗi ngày in_progress và các trạng thái không được tính.

Lý do tôi cần điều này là thao túng tỷ lệ thất bại trong số liệu thống kê của chúng tôi, vì nhiều người dùng kích hoạt lỗi hoặc không đầy đủ sẽ kích hoạt nhiều hơn làm tăng tỷ lệ lỗi của chúng tôi.

Cảm ơn bạn trước.

+0

phiên bản PostgreSQL của bạn? –

+0

@IgorRomanchenko 9.1.6 –

Trả lời

2

Hãy thử một cái gì đó như:

SELECT 
    DATE(created_at) AS date, 
    SUM(CASE WHEN state = 'complete' THEN 1 ELSE 0 END) AS complete, 
    SUM(CASE WHEN state = 'paid' THEN 1 ELSE 0 END) AS paid, 
    COUNT(DISTINCT CASE WHEN state IN('new','paying','completing') THEN user_id ELSE NULL END) AS in_progress, 
    COUNT(DISTINCT CASE WHEN state IN('payment_failed','completion_failed') THEN user_id ELSE NULL END) AS failed 
FROM orders 
WHERE created_at BETWEEN ? AND ? 
GROUP BY DATE(created_at); 

Ý tưởng chính - COUNT (DISTINCT ...) sẽ được tính độc đáo user_id và sẽ không đếm NULL giá trị.

chi tiết: aggregate functions, 4.2.7. Aggregate Expressions

Toàn bộ truy vấn có số phong cách giống nhau và đơn giản CASE WHEN ...:

SELECT 
    DATE(created_at) AS date, 
    COUNT(CASE WHEN state = 'complete' THEN 1 END) AS complete, 
    COUNT(CASE WHEN state = 'paid' THEN 1 END) AS paid, 
    COUNT(DISTINCT CASE WHEN state IN('new','paying','completing') THEN user_id END) AS in_progress, 
    COUNT(DISTINCT CASE WHEN state IN('payment_failed','completion_failed') THEN user_id END) AS failed 
FROM orders 
WHERE created_at BETWEEN ? AND ? 
GROUP BY DATE(created_at); 
+0

Ồ, điều này gần như đồng bộ hóa hoàn hảo. –

+0

@ErwinBrandstetter Yep. Và các truy vấn gần giống nhau. :) –

+0

Tôi sẽ chấp nhận câu trả lời của bạn quá nếu tôi có thể nhưng nó xuất hiện Erwin đánh bại bạn với nó bằng một vài phút. Cảm ơn bạn đã giúp đỡ. –

3
SELECT created_at::date AS the_date 
     ,SUM(CASE WHEN state = 'complete' THEN 1 ELSE 0 END) AS complete 
     ,SUM(CASE WHEN state = 'paid' THEN 1 ELSE 0 END) AS paid 
     ,COUNT(DISTINCT CASE WHEN state IN('new','paying','completing') 
         THEN user_id ELSE NULL END) AS in_progress 
     ,COUNT(DISTINCT CASE WHEN state IN('payment_failed','completion_failed') 
         THEN user_id ELSE NULL END) AS failed 
FROM orders 
WHERE created_at BETWEEN ? AND ? 
GROUP BY created_at::date 

tôi sử dụng the_date như bí danh, vì nó là không khôn ngoan (trong khi cho phép) để sử dụng ngày từ khóa là số nhận dạng.

Bạn có thể sử dụng một kỹ thuật tương tự cho completepaid, một là tốt như người kia có:

COUNT(CASE WHEN state = 'complete' THEN 1 ELSE NULL END) AS complete 
+0

Lòng tốt của tôi đã nhanh chóng! Cảm ơn bạn rất nhiều! –