从SQL表中删除重复的行(基于多列中的值)

时间:2015-05-14 17:47:05

标签: sql sql-server tsql join duplicate-removal

我有以下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行属于同一客户(测试客户),谁具有不同的ShipToCodes:1,2和3.类似于第二个客户(ARACODE客户)的情况。普通客户和普通客户2中的每一个只有一条ShipToCode的记录。

现在,我想在此表上查询结果,每个客户只有1条记录。因此,对于任何有超过1条记录的客户,我希望保留ShipToCode的最高价值记录。

我尝试了各种各样的事情:

(1)我可以轻松获得表中只有一条记录的客户列表。

(2)通过以下查询,我可以获得表中有多条记录的所有客户的列表。

[查询-1]

SELECT ARDivisionNo, CustomerNo
FROM AR_Customer_ShipTo 
GROUP BY ARDivisionNo, CustomerNo
HAVING COUNT(*) > 1;

(3)现在,为了为上面的查询返回的每条记录选择合适的ShipToCode,我无法弄清楚如何遍历上述查询返回的所有记录。

如果我这样做:

[查询-2]

SELECT TOP 1 ARDivisionNo, CustomerNo, CustomerName, ShipToCode  
FROM AR_Customer_ShipTo 
WHERE ARDivisionNo = '00' and CustomerNo = '1234567'
ORDER BY ShipToCode DESC

然后我可以获得(00-1234567-测试客户)的相应记录。因此,如果我可以在上面的查询(query-2)中使用来自query-1的所有结果,那么我可以为具有多个记录的客户获得所需的单个记录。这可以与点(1)的结果组合以实现期望的最终结果。

同样,这比我正在遵循的方法更容易。请让我知道我该怎么做。

[注意:我必须仅使用SQL查询来执行此操作。我不能使用存储过程,因为我最终将使用'Scribe Insight'执行此操作,它只允许我编写查询。]

5 个答案:

答案 0 :(得分:28)

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)要删除记录,请使用删除查询而不是选择并将Where子句更改为> 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;

答案 1 :(得分:4)

您没有指定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

答案 2 :(得分:4)

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当然用于定义计数应该如何进行,并且在ROW_NUMBER()函数中是必需的。

答案 3 :(得分:3)

使用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

答案 4 :(得分:0)

您也可以使用分组。

SELECT ARDivisionNo, 
       CustomerNo,
       max(ShipToCode) as ShipToCode  
FROM AR_Customer_ShipTo 
GROUP BY ARDivisionNo, CustomerNo