web-dev-qa-db-ja.com

SQLテーブルから(複数の列の値に基づいて)重複行を削除する

次の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。2番目の顧客(ARACODE顧客)も同様です。通常の顧客と通常の顧客-2のそれぞれには、単一のShipToCodeを持つ1つのレコードのみがあります。

ここで、このテーブルで結果のクエリを取得したいのですが、顧客ごとに1つのレコードしかありません。したがって、複数のレコードがある顧客の場合、ShipToCodeの値が最も高いレコードを保持したいと思います。

私はさまざまなことを試しました:

(1)テーブルにレコードが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-Test Customer)の適切なレコードを取得できます。したがって、上記のクエリ(クエリ2)でクエリ1のすべての結果を使用できる場合、複数のレコードを持つ顧客に必要な単一のレコードを取得できます。これをポイント(1)の結果と組み合わせて、目的の最終結果を得ることができます。

繰り返しますが、これは私が従っているアプローチよりも簡単です。どうすればいいのか教えてください。

[注:SQLクエリのみを使用してこれを行う必要があります。ストアドプロシージャは使用できません。最終的に 'Scribe Insight'を使用してこの処理を実行するため、クエリの作成のみが許可されます。]

19
Vikram

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句を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;
33

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
dnoeth

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()関数で必要です。

4
Hart CO

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
3
Giorgi Nakeuri