PAYATHON 2026

SQL query:按 Customer 查询重复出现的 Payment

支付老李

结论

要找出同一客户的重复付款,需要按 Customer 和 Payment 共同分组,用 HAVING COUNT(*) > 1 筛出重复组合,再关联原表获取每条记录的 Date。

假设表名是 Payments,查询如下:

SELECT
    p.Customer,
    p.Payment,
    p.Date
FROM Payments AS p
INNER JOIN (
    SELECT
        Customer,
        Payment
    FROM Payments
    GROUP BY
        Customer,
        Payment
    HAVING COUNT(*) > 1
) AS duplicated
    ON duplicated.Customer = p.Customer
   AND duplicated.Payment = p.Payment
ORDER BY
    p.Customer,
    p.Payment,
    p.Date;

为什么要按两个字段分组

这里的重复是指同一个 Customer 支付了相同的 Payment,所以分组条件必须同时包含这两个字段:

GROUP BY Customer, Payment

如果只按 Payment 分组,不同客户支付了相同金额时,也会被当作重复记录。

例如:

A  5
A  5
B  5

按 Customer, Payment 分组后:

  • 客户 A 的金额 5 出现了两次,属于重复。
  • 客户 B 的金额 5 只出现一次,不应包含在结果中。

下面的子查询会筛出出现次数超过一次的组合:

SELECT
    Customer,
    Payment
FROM Payments
GROUP BY
    Customer,
    Payment
HAVING COUNT(*) > 1;

得到这些组合后,再关联原表,即可返回所有重复付款记录及其日期。

使用窗口函数的写法

如果数据库支持窗口函数,可以直接计算每个 Customer + Payment 组合的记录数:

WITH payment_counts AS (
    SELECT
        Customer,
        Payment,
        Date,
        COUNT(*) OVER (
            PARTITION BY Customer, Payment
        ) AS occurrence_count
    FROM Payments
)
SELECT
    Customer,
    Payment,
    Date
FROM payment_counts
WHERE occurrence_count > 1
ORDER BY
    Customer,
    Payment,
    Date;

这种写法省去了关联原表的步骤。PARTITION BY Customer, Payment 按客户和金额划分记录,COUNT(*) OVER (...) 计算各组的记录数,最后由 WHERE occurrence_count > 1 排除只出现一次的金额。

如果只需要重复金额及次数

如果不需要每笔记录的日期,只想查看哪些金额重复以及各自的出现次数,可以直接使用聚合查询:

SELECT
    Customer,
    Payment,
    COUNT(*) AS occurrence_count
FROM Payments
GROUP BY
    Customer,
    Payment
HAVING COUNT(*) > 1
ORDER BY
    Customer,
    Payment;

注意事项

Date 可能是关键字

在部分数据库中,Date 是数据类型名或保留字。实际项目中最好使用含义更明确的列名,例如:

PaymentDate

如果不能修改列名,需要按照相应数据库的语法引用它:

-- SQL Server
[Date]

-- MySQL
`Date`

-- PostgreSQL、Oracle
"Date"

NULL 金额的处理

GROUP BY 会把同一客户的多个 NULL Payment 分到同一组。如果不想将空金额视为重复,应在统计前将其排除:

SELECT
    p.Customer,
    p.Payment,
    p.Date
FROM Payments AS p
INNER JOIN (
    SELECT
        Customer,
        Payment
    FROM Payments
    WHERE Payment IS NOT NULL
    GROUP BY
        Customer,
        Payment
    HAVING COUNT(*) > 1
) AS duplicated
    ON duplicated.Customer = p.Customer
   AND duplicated.Payment = p.Payment
ORDER BY
    p.Customer,
    p.Payment,
    p.Date;

金额字段应使用精确数值类型

Payment 建议使用 DECIMAL 或 NUMERIC,不要使用 FLOAT、REAL 等近似数值类型。近似数值的底层值可能存在细微差异,即使显示的金额相同,分组结果也可能不符合预期。

数据量较大时添加索引

表中记录较多时,可以考虑为分组和关联字段建立联合索引:

CREATE INDEX idx_payments_customer_payment
ON Payments (Customer, Payment);

是否将日期字段也加入索引,需要结合具体数据库、查询频率和执行计划判断。

备注:内容仅供参考。