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);
是否将日期字段也加入索引,需要结合具体数据库、查询频率和执行计划判断。
备注:内容仅供参考。
