Удаление SQL дублирует одну строку за раз - PullRequest
2 голосов
/ 24 августа 2009

У меня есть таблица, в которой я сохраняю все изменения строк, которые когда-либо происходили. Проблема в том, что в начале приложения была ошибка, из-за которой создавалось несколько копий каждой строки.

Таблица выглядит примерно так:

copies
|ID |CID |DATA
| 1 | 1  |  DA
| 2 | 2  |  DO
| 2 | 3  |  DO (copy of CID 2)
| 1 | 4  |  DA (copy of CID 1)
| 2 | 5  |  DA
| 1 | 6  |  DA (copy of CID 1)
| 2 | 7  |  DO

CID уникален в табличных копиях.

Я хочу удалить все дубликаты DATA GROUP BY ID, которые друг за другом отсортированы по CID.

Как видно из таблицы, CID 2 и 3 одинаковы и расположены друг за другом. Я хотел бы удалить CID 3. То же самое с CID 4 и CID 6; между ними нет идентификатора 1, и они являются копиями CID 1.

После удаления дубликатов мне бы хотелось, чтобы таблица выглядела следующим образом:

copies
|ID |CID |DATA
| 1 | 1  |  DA
| 2 | 2  |  DO
| 2 | 5  |  DA
| 2 | 7  |  DO

Есть предложения? :)

Я думаю, что мой вопрос был плохо задан, потому что ответ, который кажется всем, кажется, лучший, дает этот результат:

ID   | DATA | DATA | DATA | DATA | DATA |     DATA |        CID          |
                                                   |Expected |  Quassnoi |
1809 |    1 |    0 |    1 |    0 |    0 |     NULL |  252227 |    252227 |
1809 |    1 |    0 |    1 |    1 |    0 |     NULL |  381530 |    381530 |
1809 |    1 |    0 |    1 |    0 |    0 |     NULL |  438158 | (missing) |
1809 |    1 |    0 |    1 |    0 | 1535 | 20090113 |  581418 |    581418 |
1809 |    1 |    1 |    1 |    0 | 1535 | 20090113 |  581421 |    581421 |

CID 252227 и CID 438158 являются дубликатами, но потому что CID 381530 находится между ними; Я хочу оставить это. Это только дубликаты, которые располагаются непосредственно друг за другом при заказе по CID и ID.

Ответы [ 4 ]

5 голосов
/ 24 августа 2009
DELETE   c.*
FROM     copies c
JOIN     (
         SELECT  id, data, MIN(copies) AS minc
         FROM    copies
         GROUP BY
                 id, data
         ) q
ON       c.id = q.id
         AND c.data = q.data
         AND c.cid <> q.minc

Обновление:

DELETE  c.*
FROM    (
        SELECT  cid
        FROM    (
                SELECT  cid,
                        COALESCE(data1 = @data1 AND data2 = @data2, FALSE) AS dup,
                        @data1 := data1,
                        @data2 := data2
                FROM    (
                        SELECT  @data1 := NULL,
                                @data2 := NULL
                        ) vars, copies ci
                ORDER BY
                        id, cid
                ) qi
        WHERE   dup
        ) q
JOIN    copies c
ON      c.cid = q.cid

В этом решении используются MySQL переменные сеанса.

Существует чистое решение ANSI, которое будет использовать NOT EXISTS, однако оно будет медленным из-за работы оптимизатора MySQL (он не будет использовать метод доступа range в коррелированном подзапросе).

См. Эту статью в моем блоге для деталей о производительности для довольно близкой задачи:

1 голос
/ 24 августа 2009
// EDITED for @Jonathan Leffler comment
//$sql = "SELECT ID,CID,DATA FROM copies ORDER BY CID, ID";
$sql = "SELECT ID,CID,DATA FROM copies ORDER BY ID, CID";
$result = mysql_query($sql, $link); 
$data = "";
$id = "";
while ($row = mysql_fetch_row($result)){ 
       if (($row[0]!=$id) && ($row[2]!=$data) && ($id!="")){
            $sql2 = "DELETE FROM copies WHERE CID=".$row[1];
            $res = mysql_query($sql2, $link); 
       }
       $id=$row[0];
       $data=$row[2];
} 
1 голос
/ 24 августа 2009

Вы можете использовать count в подзапросе для этого:

delete from copies
where
    (select count(*) from copies s where s.id = copies.id 
                                   and s.data = copies.data 
                                   and s.cid > copies.cid) > 0
0 голосов
/ 24 августа 2009

удалить из копий c, где c.cid in (выберите max (cid) как max_cid, count (*) как num из копий, где num> 1 группа по id, данным)

...