如何简洁查询所有非空手机号均设标记的客户?
简洁实现你的客户查询需求
嘿,这个场景我碰到过!你现在的写法虽然能跑通,但确实太啰嗦了,尤其是以后要是再加手机号列,维护起来会很麻烦。给你分享两种更简洁且易维护的实现方式:
方法一:利用BIT类型特性简化判断
因为你的标记位是BIT类型(0/1),我们可以通过合并空值判断来简化条件,同时满足两个核心要求:至少一个非空手机号,且所有非空手机号的标记位都是1。
SELECT * FROM #Listing WHERE -- 核心判断:所有非空手机号的标记位都不能是0 (COALESCE(Phone1 IS NOT NULL AND Phone1Flagged = 0, 0) + COALESCE(Phone2 IS NOT NULL AND Phone2Flagged = 0, 0) + COALESCE(Phone3 IS NOT NULL AND Phone3Flagged = 0, 0) + COALESCE(Phone4 IS NOT NULL AND Phone4Flagged = 0, 0)) = 0 -- 确保至少有一个手机号非空 AND (Phone1 IS NOT NULL OR Phone2 IS NOT NULL OR Phone3 IS NOT NULL OR Phone4 IS NOT NULL)
逻辑说明:
- 对于每个手机号,如果它非空且标记位是0,就返回1,否则返回0(空手机号用
COALESCE转成0) - 把这些值加起来,如果总和是0,说明没有任何非空手机号的标记位是0
- 最后加上至少一个非空手机号的判断,排除全空的客户(比如id=1的Dave)
方法二:列转行(UNPIVOT)实现扩展性更强的查询
如果以后可能增加更多手机号列,推荐用这种方法——把分散的手机号和标记位列转成行,用聚合函数统一判断,扩展性拉满:
WITH CustomerPhones AS ( SELECT id, Customer, Phone, Flagged FROM #Listing -- 把手机号列转成行 UNPIVOT ( Phone FOR PhoneCols IN (Phone1, Phone2, Phone3, Phone4) ) p -- 把标记位列转成行 UNPIVOT ( Flagged FOR FlagCols IN (Phone1Flagged, Phone2Flagged, Phone3Flagged, Phone4Flagged) ) f -- 确保手机号和对应的标记位匹配(比如Phone1对应Phone1Flagged) WHERE RIGHT(p.PhoneCols, 1) = RIGHT(f.FlagCols, 1) ) SELECT l.* FROM #Listing l JOIN ( SELECT id FROM CustomerPhones GROUP BY id -- 至少一个非空手机号 HAVING COUNT(Phone) > 0 -- 所有非空手机号的标记位都是1(没有标记位为0的非空手机号) AND MAX(CASE WHEN Phone IS NOT NULL AND Flagged = 0 THEN 1 ELSE 0 END) = 0 ) c ON l.id = c.id
逻辑说明:
- 通过
UNPIVOT把每一组手机号+标记位转成单独的行,让数据结构更规整 - 分组后用聚合函数判断:
COUNT(Phone) > 0确保有非空手机号;MAX(CASE...) = 0确保没有违规的标记位 - 以后加Phone5/Phone6,只需要修改
UNPIVOT里的列名即可,不需要改判断逻辑
两种方法都能得到你想要的结果:id=4的Bill和id=6的Andy。
内容的提问来源于stack exchange,提问作者ImmortalStrawberry
相关产品推荐
相关产品推荐

