如何单次查询获取单条记录?MySQL地址类型优先级查询需求
解决MySQL按优先级筛选peopleID对应记录的问题
嘿,这个需求我之前做项目的时候刚好遇到过,你说的没错,COALESCE确实搞不定——它只是用来取第一个非空的单个字段值,没法按优先级筛选整行记录;而单纯用CASE语句如果没搭配好逻辑,确实会出现重复记录。不过有两种靠谱的方法可以完美解决这个问题,分版本给你说说:
方法一:用窗口函数(MySQL 8.0+ 推荐)
窗口函数ROW_NUMBER()可以帮我们给每个peopleID下的记录按优先级排序,然后只取排名第一的那条,完全不会有重复。
思路:给每个peopleID分组,按照AddressTypeID的优先级(1 > 3)排序,然后筛选出每组排名为1的记录。
SELECT t.peopleID, t.AddressTypeID, t.其他字段名 FROM ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY peopleID ORDER BY CASE AddressTypeID WHEN 1 THEN 1 WHEN 3 THEN 2 ELSE 3 END ) AS rn FROM 你的数据表名 WHERE AddressTypeID IN (1, 3) -- 只关注我们需要的类型,提升查询效率 ) t WHERE t.rn = 1;
解释一下:
PARTITION BY peopleID:按peopleID分组,每组单独进行排序ORDER BY CASE ...:给AddressTypeID=1的记录排第1位,=3的排第2位,其他类型排最后(如果表中有其他类型的话)- 外层筛选
rn=1:只保留每组里优先级最高的那条记录
方法二:子查询+NOT EXISTS(兼容MySQL 5.x)
如果你的MySQL版本还没到8.0,没法用窗口函数,那就用这个方法:先取出所有AddressTypeID=1的记录,再补上那些没有1类型记录的peopleID对应的3类型记录,这样组合起来就不会有重复。
-- 先取所有AddressTypeID=1的记录 SELECT peopleID, AddressTypeID, 其他字段名 FROM 你的数据表名 WHERE AddressTypeID = 1 UNION ALL -- 再取那些没有1类型记录的peopleID的3类型记录 SELECT peopleID, AddressTypeID, 其他字段名 FROM 你的数据表名 t1 WHERE AddressTypeID = 3 AND NOT EXISTS ( SELECT 1 FROM 你的数据表名 t2 WHERE t2.peopleID = t1.peopleID AND t2.AddressTypeID = 1 );
解释一下:
UNION ALL比UNION效率更高,因为不需要去重(这里两个查询的结果不会有重复的peopleID,所以放心用)NOT EXISTS用来判断某个peopleID是否没有1类型的记录,只有满足这个条件才会取它的3类型记录
为什么COALESCE和单纯CASE不行?
COALESCE:它的作用是返回参数列表里第一个非NULL的值,比如COALESCE(字段1, 字段2),但你需要的是整行记录的优先级筛选,不是单个字段的非空判断,所以完全不适用。- 单纯
CASE:如果只是写SELECT CASE WHEN AddressTypeID=1 THEN ... ELSE ... END,会把所有符合AddressTypeID=1或3的记录都列出来,同一个peopleID可能既有1又有3的记录,自然会重复;必须配合分组或筛选逻辑才能拿到唯一记录。
内容的提问来源于stack exchange,提问作者simplycoding




