2011-12-21 21 views
2

を作成せずに重複レコードを削除します。は、私は多くの重複レコードを持つテーブルが一時テーブル

shop 
ID  tax_id 
1  10 
1  10 
1  11 
2  10 
2  12 
2  10 
2  10 

私は一時テーブルを作成することなく、すべての重複レコードを削除したいです。 更新クエリの後、表は次のようになります。

shop 
ID  tax_id 
1  10 
1  11 
2  10 
2  12 
+0

一時テーブルに問題がありますか? –

+0

dublicateとは何ですか?ID/tax_idが同じ値を持っているか、あなたは、同じIDを持つ複数の行を持っている場合ならば - – Rufinus

+0

@SergeiTulentsev(IDは一意である必要がありPK、あるべきか、あなたの「ID」をFKで):それはスペースをとります。 –

答えて

5

ここでインプレースソリューション(ただし、ワンライナー)です

最大のidを調べる:

select max(id) as maxid 
    from shop; 

は、この値を覚えておいてください。それが1000に等しいとしよう。

で、一意の値を再度挿入オフセット:

insert into shop (id, tax_id) 
select distinct id + 1000, tax_id 
    from shop; 

ドロップ古い値:

update shop 
    set id = id - 1000; 

利益:

delete from shop 
    where id <= 1000; 

は、通常のIDを復元します!

+0

これまでのところ、このソリューションはすべての基準に一致する唯一のソリューションです。代わりにそれをupvoteする必要があります。 :-) –

+0

エントリを複製することは丁度良い練習や実用的ではないことは、記録 の小さな数以上とテーブルの上にこれを実装する想像INSERT文はtax_idの無差別であるためのみに応じて2つの(おそらく間違って保存しますテーブルがソートされているかどうかは関係ありません)。 – CBusBus

+0

これは、500回/秒で実行する操作ではありません。これはデータ破損の修正です。数秒かかる場合でも、それは容認されるべきです(状況によっては)。私は、あなたが話している無差別を知りませんが、この解決策はOPが探している答えを正確に示しています。 –

3

まず第一に、あなたは今後の参考のために、これらの二つのフィールドに一意のインデックスを作成することでこれを防ぐことができます。

液として、MySQLでは同じ構造を持つ新しいテーブルshopnewを作成するか、または単にレコードリストが生成されたテーブルからすべてのレコードを削除する(バックアップを持っていることを確認してください!):

//Get every record from mysql 
$sSQL = "Select ID, tax_id from shop"; 
$oRes = mysql_query($sSQL); 
$aRecordList = array(); 
while($aRow = mysql_fetch_assoc($oRes)){ 
     //If record is a duplicate, it will be 'overwritten' 
     $aRecordList[$aRow['id'].".".$aRow['tax_id']] =1; 
} 
//You could delete every record from shop here, if you dont want an additional table 
//recordList now only contains unique records 
foreach($aRecordList as $sRecord=>$bSet){ 
    $aExpRecord = explode(".",$sRecord); 
    mysql_query("INSERT INTO shopnew set id=".$aExpRecord[0].", tax_id = ".$aExpRecord[1] 
} 
+0

この例では、IDもtax_idも一意ではありません。各ショップには複数のtax_idが関連付けられています。 – Grexis

+2

はい、両方でユニークなインデックスを作成できます!それがあなたが探しているものです。したがって、2つのフィールドを組み合わせて表示することはできません。チェック:http://www.mysqlfaqs.net/mysql-faqs/Indexes/Unique-Key-or-Index/How-to-create-multi-column-unique-key-or-index-in-MySQL –

+0

うん、そう?彼の解決策は、対(id、tax_id)の一意性を維持する(少なくとも、私が知る限り)。新しいテーブルを作成しないという条件に違反しますが。 –

5

解決策。

$res = mysql_query("SELECT id, tax_id, count(*) - 1 AS cnt 
         FROM shop 
         GROUP BY id 
         HAVING cnt > 1") 
while($row = mysql_fetch_assoc($res)){ 
    mysql_query("DELETE 
        FROM shop 
        WHERE id=".$row['id']." 
         AND tax_id=". $row['tax_id']." 
        LIMIT ".$row['cnt'] -1 . "); 
} 

編集、削除を行うために

//Sql query to find duplicates 
SELECT id, tax_id, count(*) - 1 AS cnt 
    FROM shop 
    GROUP BY id 
    HAVING cnt > 1 

--- res 

+------+--------+-----+ 
| id | tax_id | cnt | 
+------+--------+-----+ 
| 1 |  10 | 2 | 
| 2 |  10 | 3 | 
+------+--------+-----+ 


//Iterate through results with your language of choice 
DELETE 
    FROM shop 
    WHERE id=<res id> 
    AND tax_id=<res tax_id> 
    LIMIT <cnt - 1> 

---res (iterated) 

+------+--------+ 
| id | tax_id | 
+------+--------+ 
| 1 |  10 | 
| 1 |  11 | 
| 2 |  12 | 
| 2 |  10 | 
+------+--------+ 

2つのクエリは、PHPの小片が必要になります:それは価値がある何のために、この最近再考、ここでは一時的なカラムを用いた代替ソリューションですスクリプト言語の必要性を排除します。

ALTER TABLE shop ADD COLUMN place INT; 

SET @i = 1 

UPDATE shop SET place = @i:= @i + 1; 

DELETE FROM shop WHERE place NOT IN (SELECT place FROM items GROUP BY id, tax_id); 

ALTER TABLE shop DROP COLUMN place; 
+0

メモリの制限があるので、私は似たようなものに行きます。 – newtover

+0

** this **はより良いです:-) –

+0

Sergeiありがとうございました、あなたは建設的ですが、忌まわしいコメントでも、私に必要なドライブを与えました。 – CBusBus

2

実際には、現在の制限事項は非常に難しい課題です。私は夕方の解決策について考えました(解決策が決して使用されないことを理解する)。私は野生の自然の中でこのソリューションを使用しませんでしたが、MySQLを使用することが可能かどうかを調べようとしました。

私の定式化の質問:重複する行を一意の制約なしに2列のテーブルから削除する一連のDELETE文を書くことは可能ですか?

問題:1が

  • を維持する必要があり、我々はグループ行にする必要があります単一の行を参照する方法を考える必要がありますので

    1. 行は、IDキーまたは主キーを持っていません何らかの形でそれは注文と条件を適用することですが、ORDER BYをサポートするDELETEの形式はWHERE句しか持つことができず、HAVINGをサポートしません。つまり、条件が満たされた後に注文が適用されます。
    2. 値がクラスター化された主キーによって整理される場合、行をソートする必要はありませんが、それはありません。

    たちは、テーブルがあるとします。

    CREATE TABLE `tablename` (
        `a_id` int(10) unsigned NOT NULL, 
        `b_id` int(10) unsigned NOT NULL, 
        KEY `Index_1` (`a_id`,`b_id`) 
    ) ENGINE=InnoDB COLLATE utf8_bin; 
    

    私はより速く検索を行うために(UNIQUEまたはPRIMARYない)のキーを追加し、グループでそれを使用することを望んで。

    あなたはいくつかの値を持つテーブルを養うことができ

    INSERT INTO tablename (a_id, b_id) VALUES (2, 3), (1, 1), (2, 2), (1,4); 
    INSERT INTO tablename (a_id, b_id) VALUES (2, 3), (1, 1), (2, 2), (1,4); 
    INSERT INTO tablename (a_id, b_id) VALUES (2, 3), (1, 1), (2, 2), (1,4); 
    

    を副作用として、キーは、カバレッジ指標となり、私たちはテーブルからのSELECTを行う際に表示される値はソートされているが、我々は削除を行うとき値は挿入された順に読み込まれます。

    SELECT @c, @a_id as a, @b_id as b, a_id, b_id 
    FROM tablename, (SELECT @a_id:=0, @b_id:=0, @c:=0) as init 
    WHERE (@c:=IF(LEAST(@a_id=(@a_id:=a_id), @b_id=(@b_id:=b_id)), @c+1, 1)) >= 1 
    ; 
    

    し、その結果を::

    それでは、次のクエリを見てみましょう

    @c, a, b, a_id, b_id 
    1, 1, 1, 1, 1 
    2, 1, 1, 1, 1 
    3, 1, 1, 1, 1 
    1, 1, 4, 1, 4 
    2, 1, 4, 1, 4 
    3, 1, 4, 1, 4 
    1, 2, 2, 2, 2 
    2, 2, 2, 2, 2 
    3, 2, 2, 2, 2 
    1, 2, 3, 2, 3 
    2, 2, 3, 2, 3 
    3, 2, 3, 2, 3 
    

    結果が自動的にIndex_1を使用してソートされ、重複ペア(a_id, b_id)は、列@cに列挙されています。今私たちの仕事は、すべての行を削除することです。@c > 1。私たちが持っている唯一の問題は、追加の条件を適用せずに、MySQLを使用することをやめさせることです(強制的に削除するにはIndex_1です)。しかし、我々はa_id上の平等のチェックや、複数の平等のチェックを使用してこれを行うことができます。

    DELETE FROM t 
    USING tablename t FORCE INDEX (Index_1) 
    JOIN (SELECT @a_id:=0, @b_id:=0, @c:=0) as init 
    WHERE a_id IN (1) 
        AND (@c:=IF(LEAST(@a_id=(@a_id:=a_id), @b_id=(@b_id:=b_id)), @c+1, 1)) > 1; 
    
    DELETE FROM t 
    USING tablename t FORCE INDEX (Index_1) 
    JOIN (SELECT @a_id:=0, @b_id:=0, @c:=0) as init 
    WHERE a_id IN (2) 
        AND (@c:=IF(LEAST(@a_id=(@a_id:=a_id), @b_id=(@b_id:=b_id)), @c+1, 1)) > 1; 
    
    SELECT * FROM tablename t; 
    
    a_id, b_id 
        1, 1 
        1, 4 
        2, 2 
        2, 3 
    

    私はIN()内のすべての可能なa_idを置くことはできませんMySQLはインデックスが、この場合には役に立たないとクエリがないことを理解するため、すべての重複を削除します(隣接しているだけですが、10通りあります)a_id 2つのDELETE文で重複を削除できます。各INには5つの明示的なIDがあります。

    希望、これは=誰かの役に立つかもしれません)

  • 3

    多分これは役立ちます。

    $query="SELECT * FROM shop ORDER BY id"; 
    $rez=$dbh->query($query); 
    $multi=$rez->fetchAll(PDO::FETCH_ASSOC); 
    foreach ($multi as $key=>$row){ 
    $rest=array_slice($multi,$key+1); 
    foreach ($rest as $rest){ 
        if(($row['id']==$rest['id']) && ($row['tax_id']==$rest['tax_id'])){ 
         $dbh->query("DELETE FROM shop WHERE id={$rest['id']} and tax_id=  {$rest['tax_id']}"); 
    
        } 
    } 
    

    }

    まずforeach反復するすべての行、およびcomparationをやっ秒1。 私はPDOを使用していますが、もちろん、手続き的なやり方で行うことができます。

    関連する問題