如何在SQL中结合DISTINCT与ORDER BY?以及如何按OrderID去重并优先选取ProductID为1的记录(无则选ProductID为2)
搞定你的两个SQL需求
嘿,我来帮你梳理清楚这两个SQL问题,尤其是你想要实现的按OrderID去重且优先选ProductID=1的核心需求~
一、先搞懂DISTINCT和ORDER BY怎么配合用
首先得明确:DISTINCT是用来移除结果集里的重复行,而ORDER BY是给最终结果排序。两者结合的时候要注意:
ORDER BY里的列,要么是你SELECT出来的列,要么是通过聚合函数算出来的——毕竟DISTINCT会先处理重复行,再给剩下的行排序。- 你之前写的
Select DISTINCT OrderDetailID,OrderID,ProductID,Quantity from Products ORDER BY OrderID,ProductID为啥没达到去重效果?因为OrderDetailID是每一行的唯一标识,每一行的这个值都不一样,所以DISTINCT相当于没起作用,所有行都会被返回,自然没法实现按OrderID去重的目的。
二、核心需求:按OrderID去重,优先选ProductID=1的记录
你的需求是每个OrderID只留一行,有ProductID=1就选它,没有就选ProductID=2。这种带优先级的去重,用**窗口函数ROW_NUMBER()**是最顺手的方案,几乎所有主流SQL数据库(比如MySQL 8+、PostgreSQL、SQL Server)都支持。
直接能用的SQL代码
WITH ranked_orders AS ( SELECT OrderDetailID, OrderID, ProductID, Quantity, -- 按OrderID分组,给组内记录排优先级:ProductID=1排第1,ProductID=2排第2 ROW_NUMBER() OVER ( PARTITION BY OrderID ORDER BY CASE ProductID WHEN 1 THEN 1 WHEN 2 THEN 2 END ASC ) AS rn FROM Products ) SELECT OrderDetailID, OrderID, ProductID, Quantity FROM ranked_orders WHERE rn = 1; -- 只留每个OrderID下排名第一的那条
逻辑拆解
- 第一步(CTE部分):给每个OrderID分组,用
CASE语句定义排序规则——把ProductID=1的记录标记为排名1,ProductID=2的标记为排名2,这样每个组里优先级高的记录就会排在最前面。 - 第二步(筛选):只取排名为1的记录,就是每个OrderID下你想要的那条唯一行。
运行后得到的结果
完全符合你期望的输出:
| OrderDetailID | OrderID | ProductID | Quantity |
|---|---|---|---|
| 1 | 10248 | 1 | 12 |
| 3 | 10249 | 1 | 5 |
| 4 | 10250 | 1 | 9 |
| 6 | 10251 | 2 | 10 |
| 7 | 10252 | 1 | 35 |
| 9 | 10253 | 2 | 6 |
| 10 | 10254 | 2 | 15 |
兼容旧版数据库的备选方案
如果你的SQL环境不支持窗口函数(比如MySQL 5.x),可以用子查询+关联的方式实现:
SELECT p1.OrderDetailID, p1.OrderID, p1.ProductID, p1.Quantity FROM Products p1 LEFT JOIN Products p2 ON p1.OrderID = p2.OrderID AND p2.ProductID = 1 -- 找同OrderID下有没有ProductID=1的记录 WHERE -- 没有ProductID=1的情况,就选ProductID=2的 (p2.OrderDetailID IS NULL AND p1.ProductID = 2) -- 有ProductID=1的情况,直接选它 OR p1.ProductID = 1;
这个逻辑就是先判断每个OrderID是否存在ProductID=1的记录,存在就选,不存在就选ProductID=2的。
内容的提问来源于stack exchange,提问作者yanivz
相关产品推荐
相关产品推荐

