5

ここでは、重複した列の値(Product)を持つ行を削除します。これは、主キーとして使用されます。

列はタイプnvarcharであり、1つの製品に2行は必要ありません。データベースは、削除する必要のある約数千行の大きなデータベースです。

すべての重複のクエリ中に、最初のアイテムを保持し、2番目のアイテムを重複として削除します。

主キーはまだありません。重複を削除するこのアクティビティの後で作成したいと思います。次に、Product列が主キーになる可能性があります。

データベースはSQLServerCEです。

私はいくつかの方法を試しましたが、ほとんどの場合、次のようなエラーが発生します:

クエリの解析中にエラーが発生しました。[トークン行番号=2、トークン行オフセット= 1、エラーのトークン= FROM]

私が試した方法:

DELETE FROM TblProducts
FROM TblProducts w
    INNER JOIN (
            SELECT Product
            FROM TblProducts
            GROUP BY Product
            HAVING COUNT(*) > 1
            )Dup ON w.Product = Dup.Product

私のコードを似たようなもので学習して調整しようとする好ましい方法(まだ正しくありません):

SELECT Product, COUNT(*) TotalCount
FROM TblProducts
GROUP BY Product
HAVING COUNT(*) > 1
ORDER BY COUNT(*) DESC

--
;WITH cte   -- These 3 lines are the lines I have more doubt on them
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY Product
                                       ORDER BY ( SELECT 0)) RN
         FROM   Word)
DELETE FROM cte
WHERE  RN > 1
4

4 に答える 4

4

同じProduct列を持つ2つの異なるレコードがある場合は、いくつかの基準で不要なレコードを選択できます。

 CREATE TABLE victims AS
     SELECT MAX(entryDate) AS date, Product, COUNT(*) AS dups FROM ProductsTable WHERE ...
     GROUP BY Product HAVING dups > 1;

次に、ProductTableとVictimsの間でDELETEJOINを実行できます。

または、[製品のみ]を選択してから、他のJOIN条件(たとえば、無効なCustomerId、EntryDate NULLなど)に対してDELETEを実行することもできます。これは、Productの有効なコピーが1つだけあり、他のすべてが無効なデータによって認識できることがわかっている場合に機能します。

代わりに、同一のレコードがあるとします(または、同一と非同一の両方があるか、一部の製品に複数の重複があり、どちらかわからない場合があります)。まったく同じクエリを実行します。次に、ProductsTableでSELECTクエリを実行し、重複排除する製品コードに一致するすべての製品をSELECT DISTINCTして、製品ごとにグループ化し、すべてのフィールドに適切な集計関数を選択します(同一の場合は、どの集計でもかまいません。それ以外の場合は、通常、MAXを試します。またはMIN)。これにより、製品ごとに1行だけが「保存」されます。

その時点で、DELETE JOINを実行し、複製されたすべての製品を強制終了します。次に、保存して重複排除したサブセットをメインテーブルに再インポートするだけです。

もちろん、DELETEJOINとINSERTSELECTの間では、DBが不安定な状態になり、少なくとも1つの重複があるすべての製品が単に消えてしまいます。

MySQLで機能する別の方法:

-- Create an empty table
CREATE TABLE deduped AS SELECT * FROM ProductsTable WHERE false;

CREATE UNIQUE INDEX deduped_ndx ON deduped(Product);

-- DROP duplicate rows, Joe the Butcher's way
INSERT IGNORE INTO deduped SELECT * FROM ProductsTable;

ALTER TABLE ProductsTable RENAME TO ProductsBackup;

ALTER TABLE deduped RENAME TO ProductsTable;
-- TODO: Copy all indexes from ProductsTable on deduped.

:「適切なレコード」と「無効な重複」を区別する場合は、上記の方法は機能しません。これは、冗長なDUPLICATEレコードがある場合、またはどの行を保持し、どの行を破棄するかを気にしない場合にのみ機能します。

編集:あなたは「重複」が無効なフィールドを持っていると言います。その場合、ソートのトリックで上記を変更できます。

  SELECT * FROM ProductsTable ORDER BY Product, FieldWhichShouldNotBeNULL IS NULL;

次に、製品の行が1つしかない場合は、すべてが良好で、選択されます。それ以上ある場合は、(FieldWhichShouldNeverBeNull IS NULL)がFALSEであるもの(つまり、FieldWhichShouldNeverBeNullが実際にはnullではないもの)が最初に選択され、挿入されます。他のすべての製品は、IGNORE条項により、製品の独自性に反して静かに跳ね返ります。それを行うのは本当にきれいな方法ではありませんが(そして、節でtrueとfalseを混ぜていないことを確認してください!)、それは機能するはずです。

実際にもっと新しい答えを編集する

これは問題を説明するための簡単な表です

CREATE TABLE ProductTable ( Product varchar(10), Description varchar(10) );
INSERT INTO ProductTable VALUES ( 'CBPD10', 'C-Beam Prj' );
INSERT INTO ProductTable VALUES ( 'CBPD11', 'C Proj Mk2' );
INSERT INTO ProductTable VALUES ( 'CBPD12', 'C Proj Mk3' );

インデックスはまだなく、主キーもありません。それでも、Productを主キーとして宣言できます。

しかし、何か悪いことが起こります。2つの新しいレコードが入り、両方ともNULLの説明があります。

しかし、これまでCBPD14について何も知らなかったため、2つ目は有効な製品であり、この記録を完全に失いたくはありません。ただし、偽のCBPD10を削除したいと思います

INSERT INTO ProductTable VALUES ( 'CBPD10', NULL );
INSERT INTO ProductTable VALUES ( 'CBPD14', NULL );

失礼なDELETEFROMProductTable WHERE Description IS NULLは問題外であり、重複していないCBPD14を強制終了します。

だから私たちはこのようにします。まず、重複のリストを取得します。

SELECT Product, COUNT(*) AS Dups FROM ProductTable GROUP BY Product HAVING Dups > 1;

「悪いレコードのセットごとに少なくとも1つの良いレコードがある」と仮定します。

反対のことを仮定し、それを照会することによって、この仮定を確認します。すべてが共食いである場合、このクエリは何も返さないと予想します。

SELECT Dups.Product FROM ProductTable
RIGHT JOIN ( SELECT Product, COUNT(*) AS Dups FROM ProductTable GROUP BY Product HAVING Dups > 1 ) AS Dups
ON (ProductTable.Product = Dups.Product
        AND ProductTable.Description IS NOT NULL)
WHERE ProductTable.Description IS NULL;

さらに確認するために、この障害モードを表す2つのレコードを挿入します。今、私は上記のクエリが新しいコードを返すことを期待しています。

INSERT INTO ProductTable VALUES ( "AC5", NULL ), ( "AC5", NULL );

これで、「チェック」クエリは実際に戻ります。

AC5

したがって、Dupsの生成は良さそうです。

次に、無効な重複レコードをすべて削除します。重複する有効なレコードがある場合、何らかの条件が見つからない限り、それらは重複したままになり、1つの「良好な」レコードを区別し、他のすべてのレコードを「無効」と宣言します(説明とは異なるフィールドで手順を繰り返す場合があります)。

しかし、ええ、摩擦があります。現在、テーブルから削除して、サブクエリ(http://dev.mysql.com/doc/refman/5.0/en/delete.html )で同じテーブルから選択することはできません。したがって、少し回避策が必要です。

CREATE TEMPORARY TABLE Dups AS
     SELECT Product, COUNT(*) AS Duplicates
         FROM ProductTable GROUP BY Product HAVING Duplicates > 1;

DELETE ProductTable FROM ProductTable JOIN Dups USING (Product)
    WHERE Description IS NULL;

これで、Dupsテーブルに表示されている場合、すべての無効なレコードが削除されます。

したがって、CBPD14レコードは表示されないため、変更されません。CBPD10の「良好な」レコードは、その説明がNULLであることが真実ではないため、変更されません。他のすべて-poof。

レコードに有効なレコードがなくて重複し いる場合、そのレコードのすべてのコピーが強制終了され、生存者は存在しません

これを回避するには、最初にこの失敗モードを表す行を別のTEMPORARY TABLEにSELECT(上記のクエリを使用して「何も返さない」チェック)し、削除後にメインテーブルに挿入し直します(トランザクションを使用すると、順番に)。

于 2012-07-15T11:59:01.897 に答える
1

古いテーブルのスクリプトを作成して名前を変更することにより、新しいテーブルを作成します。また、古いテーブルから新しいテーブルへのすべてのオブジェクト(インデックスなど)のスクリプトを作成します。キーパーを新しいテーブルに挿入します。データベースが一括ログまたは単純リカバリモデルの場合、この操作は最小限にログに記録されます。古いテーブルを削除してから、新しいテーブルの名前を古い名前に変更します。

削除に対するこれの利点は、挿入が最小限にログに記録されることです。データが削除されるだけでなく、削除をトランザクションログに書き込む必要があるため、削除は二重の作業を行います。大きなテーブルの場合、最小限にログに記録された挿入は、削除よりもはるかに高速になります。

于 2012-07-15T17:00:29.137 に答える
1

それほど大きくなく、ダウンタイムがあり、SQL Server Management Studioを使用している場合は、GUIを使用してテーブルにIDフィールドを配置できます。これで、行自体が本当に異なることを除いて、CTEのような状況になります。これで、次のことができます

SELECT MIN(table_a.MyTempIDField)
FROM
table_a lhs
join table_1 rhs
 on lhs.field1 = rhs.field1
 and lhs.field2 = rhs.field2 [etc]
WHERE
 table_a.MyTempIDField <> table_b.MyTempIDField
GROUP BY
 lhs.field1, rhs.field2 etc

これにより、すべての「適切な」複製が得られます。これで、このクエリをDELETEFROMクエリでラップできます。

DELETE FROM lhs
FROM table_a lhs
join table_b rhs
 on lhs.field1 = rhs.field1
 and lhs.field2 = rhs.field2 [etc]
WHERE
 lhs.MyTempIDField <> rhs.MyTempIDField
 and lhs.MyTempIDField not in (

SELECT MIN(lhs.MyTempIDField)
FROM
table_a lhs
join table_a rhs
 on lhs.field1 = rhs.field1
 and lhs.field2 = rhs.field2 [etc]
WHERE
 lhs.MyTempIDField <> rhs.MyTempIDField
GROUP BY
  lhs.field1, lhs.field2 etc
)
于 2012-07-15T17:01:46.773 に答える
-2

これを試して:

DELETE FROM TblProducts     
WHERE Product IN
      (
     SELECT Product
     FROM TblProducts
     GROUP BY Product
     HAVING COUNT(*) > 1)

これには、重複した製品を含むすべてのレコードが削除されるという欠点があります。おそらくやりたいことは、特定の製品のレコードの各グループの1つを除くすべてを削除することです。最初にすべての重複を別のテーブルにコピーしてから、何らかの方法でそのテーブルから重複を削除し、次に上記を適用してから、残りの製品を元のテーブルにコピーして戻すことをお勧めします。

于 2012-07-15T11:57:04.470 に答える