列值的SQL总和,每个用户每天唯一

Mar*_*ock 5 sql postgresql ruby-on-rails

我有一个postgres表,看起来像这样:

id | user_id | state | created_at
Run Code Online (Sandbox Code Playgroud)

州可以是以下任何一种:

new, paying, paid, completing, complete, payment_failed, completion_failed
Run Code Online (Sandbox Code Playgroud)

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

  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)
Run Code Online (Sandbox Code Playgroud)

通过将此选项添加到选择中,可以轻松完成正在进行和失败的状态:

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 
Run Code Online (Sandbox Code Playgroud)

但我无法弄清楚如何每天每个user_id只进行一次in_progress,并计算失败的状态.

我需要这个的原因是操纵我们的统计数据中的失败率,因为许多触发失败或不完整订单的用户继续触发更多会使我们的失败率膨胀.

提前感谢你.

Igo*_*nko 2

尝试类似的方法:

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);
Run Code Online (Sandbox Code Playgroud)

主要思想 -COUNT (DISTINCT ...)将计算唯一值user_id并且不会计算NULL值。

详细信息:聚合函数4.2.7。聚合表达式

具有相同样式的整个查询计数并简化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);
Run Code Online (Sandbox Code Playgroud)