如何使用 SQL 筛选 purchase 并判断是否已 refund
支付阿杰
结论
可以用 EXISTS 检查当前 purchase 是否有对应的 refund 记录,也就是退款记录的 Refund_ID 是否等于购买记录的 Transaction_ID。主查询筛选购买记录,再用 CASE 返回退款状态:
SELECT
p.Transaction_ID,
p.Amount,
p.Transaction_Type,
p.Transaction_Date,
CASE
WHEN EXISTS (
SELECT 1
FROM Transaction AS r
WHERE r.Transaction_Type = 'refund'
AND r.Refund_ID = p.Transaction_ID
AND r.Transaction_Status = 'OK'
) THEN 'refunded'
ELSE NULL
END AS Refund_Status
FROM Transaction AS p
WHERE p.Transaction_Type = 'purchase'
AND p.Transaction_Status = 'OK'
ORDER BY p.Transaction_Date DESC;
原理
主查询中的 p 是购买记录,子查询中的 r 是退款记录。
对于每条 purchase,EXISTS 会检查是否至少有一条记录满足以下条件:
r.Transaction_Type = 'refund'
AND r.Refund_ID = p.Transaction_ID
有匹配记录时,CASE 返回 refunded;没有则返回 NULL。
子查询还包含以下条件:
r.Transaction_Status = 'OK'
因此,只有状态为 OK 的退款才会被视为已退款。如果业务规则不关心退款状态,只要有退款记录就显示 refunded,可以删除该条件。
也可以使用 LEFT JOIN
如果一笔购买最多对应一条退款记录,也可以使用自连接:
SELECT
p.Transaction_ID,
p.Amount,
p.Transaction_Type,
p.Transaction_Date,
CASE
WHEN r.Transaction_ID IS NOT NULL THEN 'refunded'
ELSE NULL
END AS Refund_Status
FROM Transaction AS p
LEFT JOIN Transaction AS r
ON r.Refund_ID = p.Transaction_ID
AND r.Transaction_Type = 'refund'
AND r.Transaction_Status = 'OK'
WHERE p.Transaction_Type = 'purchase'
AND p.Transaction_Status = 'OK'
ORDER BY p.Transaction_Date DESC;
如果同一笔购买可能对应多条退款记录,例如部分退款或重复退款,普通 LEFT JOIN 会使这条 purchase 在结果中重复出现。遇到这种情况,更适合使用 EXISTS。它只检查退款记录是否存在,不会增加结果行数。
注意事项
Refund_ID和Transaction_ID的数据类型应保持一致。- 如果表名
Transaction是所用数据库的保留字,需要按该数据库的语法转义标识符,例如 SQL Server 使用[Transaction],MySQL 使用反引号。 - 数据量较大时,可以考虑为
Refund_ID、Transaction_Type和Transaction_Status建立合适的联合索引。 - 如果一笔购买允许多次部分退款,仅检查退款记录是否存在,只能说明这笔购买发生过退款,无法判断是否已经全额退款。要判断全额退款,还需要汇总退款金额,并与原购买金额比较。
备注:内容仅供参考。
