PAYATHON 2026

如何使用 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 建立合适的联合索引。
  • 如果一笔购买允许多次部分退款,仅检查退款记录是否存在,只能说明这笔购买发生过退款,无法判断是否已经全额退款。要判断全额退款,还需要汇总退款金额,并与原购买金额比较。

备注:内容仅供参考。