使用SQL按分组与条件筛选记录:优先选客户偏好名无则选法定名
实现方案
这个需求属于典型的「分组内按优先级取最高权重记录」场景,下面给几种不同适用场景的实现方式,示例表名统一为customer_names。
方案1:窗口函数法(兼容性最高,支持所有主流新版数据库)
这是最通用的实现方式,MySQL 8.0+、PostgreSQL、SQL Server、Oracle 均支持:
WITH ranked_names AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY CASE WHEN TYPE = 'PREF' THEN 1 ELSE 2 END ASC ) AS rn FROM customer_names WHERE TYPE IN ('PREF', 'LEGAL') -- 过滤无关类型的记录,按需开启 ) SELECT ID, TYPE, FIRST, LAST FROM ranked_names WHERE rn = 1;
逻辑说明:
- 用
PARTITION BY ID按客户ID分组 - 每组内通过自定义排序规则给记录加权:
PREF类型优先级高排序为1,LEGAL为2,保证同个ID下PREF永远排在LEGAL前面 - 最后过滤取每组排序第一的记录即可,完全匹配需求逻辑
方案2:关联子查询法(兼容低版本数据库,如MySQL 5.x)
如果你的数据库不支持窗口函数,可以用这种写法:
SELECT t1.* FROM customer_names t1 LEFT JOIN customer_names t2 ON t1.ID = t2.ID AND t2.TYPE = 'PREF' WHERE (t1.TYPE = 'PREF') OR (t1.TYPE = 'LEGAL' AND t2.ID IS NULL) GROUP BY t1.ID, t1.TYPE, t1.FIRST, t1.LAST;
逻辑说明:
- 把同个ID的记录和该ID下的
PREF类型记录做左关联 - 如果当前记录是
PREF直接保留 - 如果当前记录是
LEGAL,则判断该ID有没有关联到PREF记录,没有关联到(即t2.ID IS NULL)才保留
方案3:PostgreSQL 专属简化写法
如果你用的是PostgreSQL,可以用DISTINCT ON语法更简洁的实现:
SELECT DISTINCT ON (ID) ID, TYPE, FIRST, LAST FROM customer_names WHERE TYPE IN ('PREF', 'LEGAL') ORDER BY ID, CASE WHEN TYPE = 'PREF' THEN 1 ELSE 2 END ASC;
所有方法测试后都可以输出你给出的期望结果,性能上窗口函数法最优,数据量越大优势越明显。
内容的提问来源于stack exchange,提问作者jlk199
相关产品推荐
相关产品推荐

