订阅更新

每月交易二:批准和退单放在一起统计

SQL 实战 123 题 · 第 77 题 中等 约 2 分钟读完

LeetCode 1205 · UNION ALL 合并事件、条件聚合、过滤全零行

本文目录
  1. 面试怎么回答
  2. 题意
  3. 表结构
  4. 思路
  5. 参考解法
  6. 易错点

面试怎么回答

我把退单关联原交易拿到国家和金额,按退单日期构造成 chargeback 事件,和交易表 UNION ALL 成一张事件流水,再按月份、国家条件聚合出批准和退单的笔数、金额,HAVING 去掉全零行。把不同来源的事件统一成同一个结构再聚合,是做多事件指标的通用办法。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

题意

交易表记录交易,退单表记录某笔交易后来发生了退单(退单日期和交易日期不同)。按「月份 + 国家」统计已批准交易的笔数和金额、退单的笔数和金额;四个值全为 0 的行不输出。

表结构

  • Transactions:id(主键)、country、state(approved / declined)、amount、trans_date
  • Chargebacks:trans_id(对应交易 id)、trans_date(退单日期)

思路

难点在于退单要按「退单日期」归月,国家和金额却要从原交易里拿。做法是把退单关联交易表,构造成和交易表同样结构的「退单事件」(状态记为 chargeback、日期用退单日期),再和交易表 UNION ALL,最后按月份和国家做条件聚合,HAVING 排除全零行。

参考解法

select 
    date_format(trans_date, '%Y-%m') month, 
    country,
    sum(state = 'approved') approved_count,
    sum(if(state = 'approved', amount, 0)) approved_amount,
    sum(state = 'chargeback') chargeback_count,
    sum(if(state = 'chargeback', amount, 0)) chargeback_amount
from (
    select * from transactions
    union all
    select id, country, 'chargeback' state, amount, c.trans_date
    from chargebacks c left join transactions t 
    on c.trans_id = t.id
) tmp 
group by month, country
having approved_amount or chargeback_amount

易错点

  • 退单要用退单日期归月,不是原交易日期。
  • 只从交易表出发 LEFT JOIN 退单,会让退单按交易月份统计,而且某个月只有退单没有交易时这一行会缺失。
刷题进度保存在当前浏览器