web-dev-qa-db-ja.com

テーブルを重複排除する最良の方法は何ですか?

私はこれに対するいくつかの解決策を見てきましたが、テーブルを重複排除するための最良かつ最も効率的な方法は何だろうと思っています。コード(SQLなど)を使用してポイントを説明できますが、基本的なアルゴリズムを探しています。 SOについてはこれに関する質問が既にあると思っていましたが、見つけることができなかったので、既に存在する場合は頭に入れてください。

(明確にするために-インクリメンタル自動PKを持ち、PKフィールド以外のすべてで重複する行があるテーブルの重複を取り除くことを指します。)

31
froadie

SELECT DISTINCT <insert all columns but the PK here> FROM foo。そのクエリを使用して一時テーブルを作成します(構文はRDBMSによって異なりますが、通常はSELECT … INTOまたはCREATE TABLE ASパターンが使用可能)、古いテーブルを吹き飛ばし、一時テーブルからデータを送り返します。

13
Hank Gay

分析関数row_numberを使用:

WITH CTE (col1, col2, dupcnt)
AS
(
SELECT col1, col2,
ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col1) AS dupcnt
FROM Youtable
)
DELETE
FROM CTE
WHERE dupcnt > 1
GO                                                                 
9
Katherine

重複排除はめったに簡単ではありません。これは、重複除去するレコードの値がフィールドの一部とわずかに異なる場合が多いためです。したがって、保持するレコードを選択すると問題が発生する可能性があります。さらに、重複は多くの場合、人の記録であり、2人のジョンスミスが2人であるか、1人が重複しているかを識別するのは困難です。だから、dupを構成するものと、相違点と子レコードを処理する方法を定義するために、多くの時間(プロジェクト全体の50%以上)を費やしてください。

どの値が正しい値であるかをどのようにして知るのですか?さらに重複除去を行うには、孤立しない子レコードをすべて処理する必要があります。子レコードのIDを変更することで、一意のインデックスまたは制約の1つに突然違反していることに気付いた場合、どうなりますか?これは最終的に発生し、プロセスで処理する必要があります。すべての制約をアプリケーション全体にのみ適用するという愚かな選択をした場合、制約に違反していることさえ知らないかもしれません。重複除去するレコードが10,000個ある場合、アプリケーションを一度に1つずつ重複除去することはありません。制約がデータベースにない場合は、重複除去時にデータの整合性を維持することができます。

さらに複雑なのは、名前や住所が常に正確に一致するとは限らないことです。たとえば、Joan Martinという名前の営業担当者は、特に同じアドレスと電子メールを持っている場合、Joan Martin-Jonesという営業担当者の重複である可能性があります。 OR名前にJohnまたはJohnnyを含めることができます。または、1つのレコードはSTを省略し、1つはStreetと表記する以外は同じ住所です。SQLサーバーでは、SSISとファジーグループを使用してマッチに近いもの:正確な一致ではなかったという事実が、最初にDUPとして入れられた理由であるため、これらは最も一般的なDUPです。

重複除去の種類によっては、重複除去を行う人が特定のフィールドに使用する2つの値のどちらかを選択できるように、ユーザーインターフェイスが必要な場合があります。これは、重複除去される人が2つ以上のロールにある場合に特に当てはまります。通常、特定の役割のデータは、別の役割のデータよりも優れている場合があります。または、どちらが正しい値であるかを確実に知るのはユーザーだけである可能性があります。あるいは、真のダップか単に同じ名前の2人かを確認するために連絡する必要があるかもしれません。

7
HLGEM

重複基準をgroup byステートメントに入れることができ、テーブルに一意性のためのid ID列がある場合に使用する方法は次のとおりです。

delete t
from tablename t
inner join  
(
    select date_time, min(id) as min_id
    from tablename
    group by date_time
    having count(*) > 1
) t2 on t.date_time = t2.date_time
where t.id > t2.min_id

この例では、date_timeがグループ化基準です。複数の列がある場合は、それらすべてで結合するようにしてください。

6
DShook

将来の参照用に実際のコードをここに追加する

したがって、3つのステップがあるため、3つのSQLステートメントがあります。

ステップ1:非重複(一意のタプル)を一時テーブルに移動する

CREATE TABLE new_table as
SELECT * FROM old_table WHERE 1 GROUP BY [column to remove duplicates by];

ステップ2:古いテーブルを削除する(または名前を変更する)

DROP TABLE old_table;

ステップ3:new_tableの名前をold_tableの名前に変更します

RENAME TABLE new_table TO old_table;

もちろん、バグのあるコードを修正して重複の挿入を停止することを忘れないでください!

6
DropHit

私はDShookからのものを取り上げ、日付が最も高いレコードのみを保持する重複排除の例を提供しています。

この例では、すべてapp_idが同じ3つのレコードがあり、最も高い日付のレコードのみを保持したいとします。

DELETE t
FROM @USER_OUTBOX_APPS t
INNER JOIN  
(
    SELECT 
         app_id
        ,max(processed_date) as max_processed_date
    FROM @USER_OUTBOX_APPS
    GROUP BY app_id
    HAVING count(*) > 1
) t2 on 
    t.app_id = t2.app_id
WHERE 
    t.processed_date < t2.max_processed_date
2
Taylor Brown

迅速で汚いアプローチを好む人のために、一緒に一意のレコードを定義するすべての列をリストし、それらの列で一意のインデックスを作成します。

ALTER IGNORE TABLE TABLE_NAME一意に追加(column1column2column3

一意の索引の後書きを削除できます。

1
Demian Perry

これにより、c1の重複値が重複排除されます。

select * from foo
minus
select f1.* from foo f1, foo f2
where f1.c1 = f2.c1 and f1.c2 > f2.c2
0
Jim X.

各行(PKを除く)のハッシュを生成し、新しい列に保存します(または、新しい列を追加できない場合、テーブルを一時ステージング領域に移動できますか?)同じハッシュを持つ行。もちろん、ハッシュ関数が異なる行に対して同じコードを生成しないようにする必要があります。

2つの行が重複している場合、どちらを削除するかは重要ですか?他のデータが両方の重複に依存している可能性はありますか?その場合、いくつかの手順を実行する必要があります。

  • Pesを見つける
  • dupeAとしてそれらのいずれかを選択して削除します
  • dupeAに依存するすべてのデータを検索
  • dupeBを参照するようにそのデータを変更します
  • dupeAを削除します。

これは、既存のデータモデルに応じて、簡単または複雑になる可能性があります。

このシナリオ全体は、保守および再設計プロジェクトのように聞こえます。もしそうなら、幸運を!

SQLの場合、INSERT IGNORE INTOテーブルSELECT xy FROM unkeyed_tableを使用できます。

アルゴリズムの場合、主キーとなるキーが繰り返されると想定できるが、主キーとなるキーは行のコンテンツを一意に識別し、主キーとなるキーのみをハッシュして繰り返しをチェックする場合。

0
ron

これには、id以外のすべての列でグループ化し、すべてのグループから1行を選択するだけでよいのではないかと思います-簡単にするために、最初の行だけを指定しますが、idに追加の制約がある以外は実際には問題になりません。

または、他の方法で行を削除します...すべての行を削除して、すべてのグループから単一の行を受け入れます。

0

これが実際の生活で出会ったものです。

ユーザーの外部/サードパーティログインのテーブルがあり、2人のユーザーをマージして、プロバイダー/プロバイダーのキー値で重複排除を行うと仮定します。

    ;WITH Logins AS
    (
        SELECT [LoginId],[UserId],[Provider],[ProviderKey]
        FROM [dbo].[UserLogin] 
        WHERE [UserId]=@FromUserID -- is the user we're deleting
              OR [UserId]=@ToUserID -- is the user we're moving data to
    ), Ranked AS 
    (
        SELECT Logins.*
            , [Picker]=ROW_NUMBER() OVER (
                       PARTITION BY [Provider],[ProviderKey]
                       ORDER BY CASE WHEN [UserId]=@FromUserID THEN 1 ELSE 0 END)
        FROM Logins
    )
    MERGE Logins AS T
    USING Ranked AS S
    ON S.[LoginId]=T.[LoginID]
    WHEN MATCHED AND S.[Picker]>1 -- duplicate Provider/ProviderKey
                 AND T.[UserID]=@FromUserID -- safety check 
    THEN DELETE
    WHEN MATCHED AND S.[Picker]=1 -- the only or best one
                 AND T.[UserID]=@FromUserID
    THEN UPDATE SET T.[UserID]=@ToUserID
    OUTPUT $action, DELETED.*, INSERTED.*;
0
IDisposable
delete from yourTable 
where Id not in (
    select min(id) 
    from yourTable
    group by <Unique Columns>
)

idは、テーブル内の一意のIDです。 (customerNumberまたは何でも可能)

一意のIDがない場合は、追加できます(すべてのSQLテーブルには最初の列として既にIDがありますが、

ALTER TABLE yourTable
ADD Id int identity(1,1)

削除(上記)を行ってから、列をドロップします。

まったく新しいテーブル、または私が見た他の不可解なものを作成するよりも優れています。ここでのコメントとほとんど同じですが、これは私が長年行ってきたことです。

0
Traderhut Games

重複排除/重複排除/重複の削除/繰り返し行の削除/データベースの削除/データの削除重複の削除には、複数の方法があります。

  1. 複製された行がまったく同じ場合は、group byを使用します

    テーブルTABLE_NAME_DEDUPを作成します
    column1、column2でTABLE_NAMEグループからcolumn1、column2、...(すべての列名)を選択-すべての列名

TABLE_NAME_DEDUPは重複排除されたテーブルです。

例えば、

create table test (t1 varchar(5), t2 varchar(5));
insert into test  values ('12345', 'ssdlh');
insert into test  values ('12345', 'ssdlh');
create table test_dedup as
select * from test 
group by t1, t2;
-----optional
--remove original table and rename dedup table to previous table
--this is not recommend in dev or qa. DROP table test; Alter table test_dedup rename to test;
  1. ROWIDがあり、ROWIDに重複がありますが、他の列は部分的に同じレコードです。これは、行の更新中にトランザクションシステムで発生する可能性があり、更新に失敗した行にはnullが含まれます。重複を削除したい

    cnからselect column1、column2、...(すべての列名)from(select *、row_number()over(rowid by partition by column1、column2、...(rowidを除くすべての列名))としてテーブルtest_dedupを作成しますtest)ここでcn = 1

これは、order byを使用すると、null値がnull以外の値の後ろに並べられる機能を使用しています。

create table test (rowid_ varchar(5), t1 varchar(5), t2 varchar(5));
insert into test  values ('12345', 'ssdlh', null);
insert into test  values ('12345', 'ssdlh', 'lhbzj');
create table test_dedup as
select rowid_, t1, t2 from
(select *
  , row_number() over (partition by rowid_ order by t1, t2) as cn
  from  test)
 where cn =1
 ;

-----optional
--remove original table and rename dedup table to previous table
--this is not recommend in dev or qa. DROP table test; Alter table test_dedup rename to test;
0
Decula

今日問題に走りましたが、既存の答えのどれも私を助けませんでした。 your_tableという名前のテーブルを重複排除するとします。

ステップ1:重複排除された値で新しいテーブルを作成する

StackOverflowの別の場所からこのコードを借りたが、それを再び見つけることができない場合PostgreSQLに対して正常に動作します。 your_table_dedupedが一意であるテーブル(col1, col2)を作成します。

CREATE TABLE your_table_deduped AS
SELECT * FROM your_table WHERE ctid NOT IN
(SELECT ctid FROM
  (SELECT ctid, ROW_NUMBER() OVER
    (PARTITION BY col1, col2 ORDER BY ctid) AS rnum
  FROM your_table) t
WHERE t.rnum > 1);

ステップ2:最初のテーブルを重複排除されたコピーで置き換える

このステップで値を削除するのは、テーブルにインデックス、制約などを保持できるようにするためです。

DELETE FROM your_table;
INSERT INTO your_table
SELECT * FROM your_table_deduped;

ステップ3:重複排除されたコピーを削除する

DROP TABLE site_daily_kpis_dedup;

そして出来上がり、あなたはあなたのテーブルを重複排除しました!

0

これらのメソッドは機能しますが、PKとしての明示的なIDがなければ、削除する行を決定することが問題になる可能性があります。一時テーブルにバウンスアウトして、元から削除し、重複なしで再挿入するのが最も簡単なようです。

0