5

我有一个如下所示的 postgres 表:

id | user_id | state | created_at

状态可以是以下任何一种:

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

我需要一个返回报告的语句,其中包含以下内容:

  1. 按日期计算的所有已付款状态的总和
  2. 按日期列出的所有已完成状态的总和
  3. 按日期计算的所有新的、付费的、完成状态的总和,每个用户每天只计算一个
  4. 按日期计算的所有 payment_failed、completion_failed 的总和,每个用户每天只计算一个

到目前为止,我有这个:

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)

通过将其添加到选择中,进行中和失败状态的总和很容易:

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 

但是我无法弄清楚如何每天只为每个 user_id 制作一个 in_progress 和失败的状态来计算。

The reason I need this is to manipulate the failure rate in our stats, as many users who trigger a failure or incomplete order go on to trigger more which inflates our failure rate.

Thanking you in advance.

4

2 回答 2

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

I use the_date as alias, since it is unwise (while allowed) to use the key word date as identifier.

You could use a similar technique for complete and paid, one is as good as the other there:

COUNT(CASE WHEN state = 'complete' THEN 1 ELSE NULL END) AS complete
于 2013-01-11T21:55:01.810 回答
2

Try something like:

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);

The main idea - COUNT (DISTINCT ...) will count unique user_id and wont count NULL values.

Details: aggregate functions, 4.2.7. Aggregate Expressions

The whole query with same style counts and simplified 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);
于 2013-01-11T21:53:14.647 回答