【问题标题】:Why PostgreSQL returns more rows than expected?为什么 PostgreSQL 返回的行数比预期的多?
【发布时间】:2015-11-03 15:04:23
【问题描述】:

我有下一张表(仅显示重要的列):

clients: id, created_at
orders: id, client_id, created_at

我正在尝试选择所有有 1 个订单的客户

SELECT c.phone, c.created_at, o.created_at
FROM clients c
JOIN orders o ON c.id = o.client_id
GROUP BY c.phone, c.created_at, o.created_at
HAVING COUNT(o.id) = 1

当我只有 27,000 个客户时获得 33k+ 行。
我做错了什么?
我应该在 SQL 中更改什么?

【问题讨论】:

  • 部分客户有多个订单。
  • 当然,我知道。但我只需要选择有 1 个订单的客户。这就是使用组/聚合函数的原因。
  • 是吗?和?每个订单大概有不同的created_at 日期,因此根据您的分组规则,每个订单都是唯一记录,因此计数为 1。换句话说,除非您更改您的group by,否则您的having count()=1 是无用的,因为所有结果行的计数均为 1。
  • @MarcB 所以,看来我需要用子查询替换 join 或 ..?
  • 没有。只需消除订单表上的任何分组。您只需要按 client.id 分组

标签: sql postgresql aggregate


【解决方案1】:

您正在使用c.phone, c.created_at, o.created_at 作为您的分组。该语句将创建这些值的唯一分组。如果客户有多个订单,它仍然会返回多个值,因为您已包含订单表中的created_at 字段。

如果您从group by 中删除o.created_at 字段,并在您的select(例如MIN(o.created_at))中对其使用聚合函数,那么您应该很高兴。

【讨论】:

  • 我在结果中需要这个字段。
  • @Miraage 您的问题是您按订单和客户分组。您的分组只能是c.phone, c.created_at。在您的选择中,您只需取最小值/最大值即可获得created_at 值;即MIN(o.created_at)
  • @AdamMartin 听起来不错,但如果客户有超过 1 个订单怎么办?
  • @Miraage 您正在过滤您的 HAVING 子句中只有一个订单的客户...之前每个人都出现在您面前的原因是您按订单和客户分组,所以每一行只有一个订单。
【解决方案2】:

详细信息和摘要范围之间的基本问题:您想要订单的详细信息(created_at),但您想要的订单由汇总事实确定(count if o.id)。将问题分成两部分:哪些 client_id 仅使用订单表有 1 个订单,然后将客户表连接到该订单上。

SELECT 
    c.phone, c.created_at, o.created_at
FROM 
    clients c
JOIN 
(
 Select
     id, client_id, created_at,
     count(id) over (partition by client_id) count_oid 
 From orders
 ) o 
ON c.id = o.client_id
WHERE o.count_oid = 1

【讨论】:

  • 这比必要的要复杂得多。答案已经作为对 OP 查询的小调整提供。
猜你喜欢
  • 2011-11-08
  • 2021-08-01
  • 2019-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-16
  • 2020-12-18
  • 2019-05-25
相关资源
最近更新 更多