【问题标题】:SQL How to select customers with highest transaction amount by stateSQL如何按状态选择交易金额最高的客户
【发布时间】:2019-12-27 01:01:46
【问题描述】:

我正在尝试编写一个 SQL 查询,返回每个州花费最多的五个客户的姓名和购买金额。

表架构

customers
|_state
|_customer_id
|_customer_name

transactions
|_customer_id
|_transact_amt

尝试看起来像这样

SELECT state, Sum(transact_amt) AS HighestSum
FROM (
    SELECT name, transactions.transact_amt, SUM(transactions.transact_amt) AS HighestSum
    FROM customers
    INNER JOIN customers ON transactions.customer_id = customers.customer_id
    GROUP BY state
) Q
GROUP BY transact_amt
ORDER BY HighestSum 

我迷路了。谢谢。

预期结果是每个州交易量最高的前 5 名客户的姓名。

ERROR:  table name "customers" specified more than once
SQL state: 42712

【问题讨论】:

  • 如果出现并列第五名,您是想要全部(每个州可能超过 5 个),还是总是最多 5 个?
  • 我只想返回每个州的前5名,谢谢
  • 前10名花费相同的情况下,如何挑选“前5名”?请始终公开您的 Postgres 版本。

标签: sql database postgresql


【解决方案1】:

首先,您的JOIN 需要正确。二、你要使用窗口函数:

SELECT ct.*
FROM (SELECT c.customer_id, c.name, c.state, SUM(t.transact_amt) AS total,
             ROW_NUMBER() OVER (PARTITION BY c.state ORDER BY SUM(t.transact_amt) DESC) as seqnum
      FROM customers c JOIN
           transaactions t
           ON t.customer_id = c.customer_id
      GROUP BY c.customer_id, c.name, c.state
     ) ct
WHERE seqnum <= 5;

您的 SQL 似乎有几个问题。我将从了解聚合函数开始。您有一个别名为 HighestSumSUM()。这只是每位客户的总数。

【讨论】:

    【解决方案2】:

    您可以使用聚合获取它们,然后使用RANK() 窗口函数。例如:

    select
      state,
      rk,
      customer_name
    from (
      select
        *,
        rank() over(partition by state order by total desc) as rk
      from (
        select
          c.customer_id,
          c.customer_name,
          c.state,
          sum(t.transact_amt) as total
        from customers c
        join transactions t on t.customer_id = c.customer_id
        group by c.customer_id
      ) x
    ) y
    where rk <= 5
    order by state, rk
    

    【讨论】:

    • 由于问题不清楚,如果 OP 正好想要 5 个(即如果有 10 个客户花费了相同的金额,则只获取其中 5 个并忽略其余的),他可以替换 @ 987654323@ 和row_number()
    【解决方案3】:

    已经有两个有效的答案。这是第三个:

    SELECT *
    FROM  (
       SELECT c.state, c.customer_name, t.*
            , row_number() OVER (PARTITION BY c.state ORDER BY t.transact_sum DESC NULLS LAST, customer_id) AS rn
       FROM  (
          SELECT customer_id, sum(transact_amt) AS transact_sum
          FROM   transactions
          GROUP  BY customer_id
          ) t
       JOIN   customers c USING (customer_id)
       ) sub
    WHERE  rn < 6
    ORDER  BY state, rn;
    

    要点

    【讨论】:

    • 只是想知道。像WHERE rn &lt; 6 这样的条件意味着整个数据集(100-1000-1000000 个项目)应该在之前被检索/计算,并且查询将只返回其中的几 (6) 个项目。或许,使用 pl/pgsql 会更高效?
    • @Abelisto:那么,如果不查看所有行,您将如何识别前 5 个?
    • 一些澄清:select * from (select row_number() over () as rn, * from sometable) as t where t.rn &lt; 6 会比rn := 1; for v in select * from sometable loop return next; rn := rn + 1; if rn &gt; 6 then break; end loop 更有效
    • @Abelisto:啊,你的意思是:避免排序长尾?不确定有没有潜力。替代方案具有昂贵的开销。也不确定 Postgres 是否足够聪明,可以自动(和完全)优化它。 CTE 可能对此有所帮助。可能值得在 dba.SE 上提问 ...
    • 是的。在第一种情况下,我们需要 1)检索整个数据 2)在每一行上计算一些值 3)在与计算值相关的某些条件下过滤结果。在第二种情况下,我们需要 1) 仅检索 n-s 行......仅此而已。这就是为什么我不喜欢与窗口函数过滤器相关的答案。
    猜你喜欢
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    • 2016-03-24
    • 1970-01-01
    相关资源
    最近更新 更多