从 SQL 表中删除重复的行(基于多列的值)
Posted
技术标签:
【中文标题】从 SQL 表中删除重复的行(基于多列的值)【英文标题】:Removing duplicate rows (based on values from multiple columns) from SQL table 【发布时间】:2015-07-26 11:02:07 【问题描述】:我有以下 SQL 表:
AR_Customer_ShipTo
+--------------+------------+-------------------+------------+
| ARDivisionNo | CustomerNo | CustomerName | ShipToCode |
+--------------+------------+-------------------+------------+
| 00 | 1234567 | Test Customer | 1 |
| 00 | 1234567 | Test Customer | 2 |
| 00 | 1234567 | Test Customer | 3 |
| 00 | ARACODE | ARACODE Customer | 1 |
| 00 | ARACODE | ARACODE Customer | 2 |
| 01 | CBE1EX | Normal Customer | 1 |
| 02 | ZOCDOC | Normal Customer-2 | 1 |
+--------------+------------+-------------------+------------+
(ARDivisionNo, CustomerNo,ShipToCode)
构成该表的主键。
如果您注意到前 3 行属于同一客户(测试客户),他们具有不同的 ShipToCode:1、2 和 3。第二个客户(ARACODE 客户)的情况类似。普通客户和普通客户 2 中的每一个都只有 1 条记录,带有一个 ShipToCode
。
现在,我想在此表上查询结果,每个客户只有 1 条记录。因此,对于任何有超过 1 条记录的客户,我希望将价值最高的记录保留为 ShipToCode
。
我尝试了各种方法:
(1) 表格中只有一条记录,我可以轻松获取客户列表。
(2) 通过以下查询,我可以获得所有客户的列表,这些客户在表中拥有多条记录。
[Query-1]
SELECT ARDivisionNo, CustomerNo
FROM AR_Customer_ShipTo
GROUP BY ARDivisionNo, CustomerNo
HAVING COUNT(*) > 1;
(3) 现在,为了为上述查询返回的每条记录选择正确的ShipToCode
,我无法弄清楚如何遍历上述查询返回的所有记录。
如果我这样做:
[Query-2]
SELECT TOP 1 ARDivisionNo, CustomerNo, CustomerName, ShipToCode
FROM AR_Customer_ShipTo
WHERE ARDivisionNo = '00' and CustomerNo = '1234567'
ORDER BY ShipToCode DESC
然后我可以获得(00-1234567-Test Customer)的相应记录。因此,如果我可以在上述查询 (query-2) 中使用来自 query-1 的所有结果,那么我可以获得具有多个记录的客户所需的单个记录。这可以与第 (1) 点的结果相结合,以获得所需的最终结果。
同样,这比我所遵循的方法更容易。请让我知道我该怎么做。
[注意:我必须仅使用 SQL 查询来执行此操作。我不能使用存储过程,因为我最终将使用“Scribe Insight”来执行这个东西,它只允许我编写查询。]
【问题讨论】:
How to delete duplicate rows in sql server?的可能重复 【参考方案1】:Sample SQL FIDDLE
1) 使用 CTE 根据 ARDivisionNo, CustomerNo 获取最大船码值记录 每个客户
WITH cte AS (
SELECT*,
row_number() OVER(PARTITION BY ARDivisionNo, CustomerNo ORDER BY ShipToCode desc) AS [rn]
FROM t
)
Select * from cte WHERE [rn] = 1
2) 要删除记录,请使用 Delete 查询而不是 Select 并将 Where 子句更改为 rn > 1. Sample SQL FIDDLE
WITH cte AS (
SELECT*,
row_number() OVER(PARTITION BY ARDivisionNo, CustomerNo ORDER BY ShipToCode desc) AS [rn]
FROM t
)
Delete from cte WHERE [rn] > 1;
select * from t;
【讨论】:
非常感谢您的回答和示例 SQL FIDDLES! :) 我提出的所有其他答案(以及我在其下方的评论)都帮助了我并解决了我的问题。我将此标记为答案,因为 Piyush 努力创建和发布 Sample SQL FIDDLES。我希望我也可以将其他人标记为答案(或至少一个由 Hart CO 提供的解释),但 *** 只允许我选择一个! 谢谢..维克拉姆...希望你能解决你的问题!!我也喜欢@Hart CO :- 解释【参考方案2】:ROW_NUMBER()
非常适合:
;WITH cte AS (SELECT *,ROW_NUMBER() OVER(PARTITION BY ARDivisionNo,CustomerNo ORDER BY ShipToCode DESC) AS RN
FROM AR_Customer_ShipTo
)
SELECT *
FROM cte
WHERE RN = 1
您提到删除重复项,如果您想DELETE
,您可以简单地:
;WITH cte AS (SELECT *,ROW_NUMBER() OVER(PARTITION BY ARDivisionNo,CustomerNo ORDER BY ShipToCode DESC) AS RN
FROM AR_Customer_ShipTo
)
DELETE cte
WHERE RN > 1
ROW_NUMBER()
函数为每一行分配一个数字。 PARTITION BY
是可选的,但用于为给定字段或字段组中的每个值重新编号,即:如果您 PARTITION BY Some_Date
则对于每个唯一日期值,编号将从 1 开始。ORDER BY
of course 用于定义计数应该如何进行,并且在ROW_NUMBER()
函数中是必需的。
【讨论】:
非常感谢您的回答和详细的解释! :) 一点更正:对于我提出的问题中的特定示例,它应该是partition by ARDivisionNo, CustomerNo
而不是 partition by CustomerNo
。
@Vikram 已相应更新。,【参考方案3】:
您没有指定 SQL Server 的版本,但可能支持 ROW_NUMBER:
select *
from
(
select ...
,row_number()
over (partition by ARDivisionNo, CustomerNo
order by ShipToCode desc) as rn
from tab
) as dt
where rn = 1
【讨论】:
非常感谢您的回答! :)【参考方案4】:带有row_number
功能:
SELECT * FROM(
SELECT ARDivisionNo, CustomerNo, CustomerName, ShipToCode,
row_number() over(partition by CustomerNo order by ShipToCode desc) rn
FROM AR_Customer_ShipTo) t
WHERE rn = 1
【讨论】:
非常感谢您的回答! :) 一点更正:对于我提出的问题中的特定示例,它应该是partition by ARDivisionNo, CustomerNo
而不是 partition by CustomerNo
。【参考方案5】:
您也可以使用分组。
SELECT ARDivisionNo,
CustomerNo,
max(ShipToCode) as ShipToCode
FROM AR_Customer_ShipTo
GROUP BY ARDivisionNo, CustomerNo
【讨论】:
以上是关于从 SQL 表中删除重复的行(基于多列的值)的主要内容,如果未能解决你的问题,请参考以下文章