如何限制CASE语句每个订单仅返回一行结果?
我明白你的问题所在了——你遇到的核心问题不是CASE语句的逻辑,而是JOIN操作带来的多行记录,以及DISTINCT无法帮你按订单聚合结果。
为什么会返回多行?
你的查询把Orders表和OrderDet表做了JOIN,而一个订单(Orders里的单条记录)往往对应多个订单明细(OrderDet里的多条记录)。虽然CASE语句确实是逐行按顺序求值,但每个明细行都会生成自己的OrderTotal结果。如果同一个订单下的不同明细行满足不同的CASE条件(或者有的满足、有的不满足),DISTINCT会把这些不同的OrderTotal值都保留下来,导致一个订单返回多条记录。
解决方案:按订单分组,聚合结果
你需要确保每个订单只返回一条记录,同时在分组内按照你的优先级逻辑选取正确的OrderTotal值。这里有两种常用的实现方式:
方案1:GROUP BY + 聚合函数
利用聚合函数(比如MAX)忽略NULL的特性,按优先级依次取值:
SELECT o.OrderNo, COALESCE( -- 优先取符合条件的User_Currency2 MAX(CASE WHEN od.PartNo LIKE '%Amend%' THEN od.User_Currency2 END), -- 其次取符合条件的User_Currency1 MAX(CASE WHEN od.PartNo LIKE '%Amend%' THEN od.User_Currency1 END), -- 最后取订单的默认OrderTotal MAX(o.OrderTotal) ) AS OrderTotal FROM Orders o JOIN OrderDet od ON od.OrderNo = o.OrderNo WHERE o.OrderNo LIKE '%E' GROUP BY o.OrderNo ORDER BY o.OrderNo
MAX会自动忽略NULL值,所以只要订单下有任意一条明细满足PartNo LIKE '%Amend%'且User_Currency2非空,就会返回该值;如果没有,就找User_Currency1;都没有的话,返回订单的OrderTotal。
方案2:窗口函数 + 行号排序
如果需要更精准地控制“取第一个符合优先级的明细行”,可以用窗口函数给每个订单的明细行按优先级排序,然后取第一行:
WITH RankedOrders AS ( SELECT o.OrderNo, CASE WHEN od.PartNo LIKE '%Amend%' AND od.User_Currency2 IS NOT NULL THEN od.User_Currency2 WHEN od.PartNo LIKE '%Amend%' AND od.User_Currency1 IS NOT NULL THEN od.User_Currency1 ELSE o.OrderTotal END AS OrderTotal, -- 按你的CASE优先级给行排序,优先级高的行号为1 ROW_NUMBER() OVER ( PARTITION BY o.OrderNo ORDER BY CASE WHEN od.PartNo LIKE '%Amend%' AND od.User_Currency2 IS NOT NULL THEN 1 WHEN od.PartNo LIKE '%Amend%' AND od.User_Currency1 IS NOT NULL THEN 2 ELSE 3 END ) AS rn FROM Orders o JOIN OrderDet od ON od.OrderNo = o.OrderNo WHERE o.OrderNo LIKE '%E' ) SELECT OrderNo, OrderTotal FROM RankedOrders WHERE rn = 1 ORDER BY OrderNo
这个方法会给每个订单的所有明细行按你的CASE逻辑分配优先级,然后只保留每个订单的第一行(优先级最高的那一行),完美匹配你想要的“按顺序求值并仅返回一个值”的预期。
内容的提问来源于stack exchange,提问作者user1452574
相关产品推荐
相关产品推荐

