3

Есть таблица

Таблица

Как мне удалить дубликаты запросом sql?

gudfar
  • 759
  • http://ru.stackoverflow.com/questions/193369/%D0%97%D0%B0%D0%BF%D1%80%D0%BE%D1%81-sql-%D0%BD%D0%B0-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B4%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%82%D0%BE%D0%B2-%D0%B8%D0%B7-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8B-%D0%BF%D0%BE-%D0%BE%D0%B4%D0%BD%D0%BE%D0%BC%D1%83-%D0%BF%D0%BE%D0%BB%D1%8E – Viktorov Jul 06 '16 at 11:53
  • Это круто, я смотрел. Проблема в том что ни одно поле не имеет первичного ключа – gudfar Jul 06 '16 at 11:55
  • А при чем тут первичный ключ? Там есть много вариантов. Точно ни 1 из них Вам не подходит? – Viktorov Jul 06 '16 at 11:56

4 Answers4

4

PostgreSQL

Можно решить задачу одним запросом с CTE (где T - исходная таблица):

    with td as
    (delete from T returning *),
    tt as
    (select row_number() over(partition by id_category,id_product,position 
                  order by id_category,id_product,position) num, * from td)
    insert into T select id_category,id_product,position from tt where num=1;
msi
  • 11,453
  • Я правильно понимаю, что в данном случае будут удалены все записи, а потом вставлены уникальные? Синтаксис сбивает с толку, не видел такого раньше... – pegoopik Jul 07 '16 at 08:15
  • Да, но без создания временной таблицы. Кстати, решение в стиле MS SQL в PG работать не будет. – msi Jul 07 '16 at 09:11
3

MySQL:

Можно создать столбец с уникальными значениями. С помощью него удалить дубликаты, потом удалить столбец(а лучше оставить и навесить на него уникальный индекс). Что довольно ресурсоёмко, но вариант.

Создать столбец и заполнить его натуральными числами можно так:

ALTER TABLE Test ADD Id INT;

UPDATE Test
  SET Id = @I := @I + 1
/*тут можно задать нужную сортировку при желании, я добавил по A, B*/
ORDER BY A, B, (SELECT @I := 0)

В итоге в таблице Test появится колонка id, заполненная числовой последовательностью отсортированной по столбцам A, B.

UPD: Есть несколько экстравагантный способ:) Сначала пометить строки на удаление, потом удалить. Используя опять же накопление в переменную.

Для наглядности покажу все скрипты в рабочем виде.

Создаём табличку и заполняем:

CREATE TABLE TEST_DUPLICATE(
  A VARCHAR(20),
  B VARCHAR(20)
);
INSERT TEST_DUPLICATE SELECT 'AAA', 'BBB';
INSERT TEST_DUPLICATE SELECT 'AAA', 'BBB';
INSERT TEST_DUPLICATE SELECT 'BBB', 'BBB';
INSERT TEST_DUPLICATE SELECT 'AAA', 'AAA';
INSERT TEST_DUPLICATE SELECT 'BBB', 'BBB';
INSERT TEST_DUPLICATE SELECT 'AAA', 'AAA';
INSERT TEST_DUPLICATE SELECT 'AAA', 'BBB';

SELECT *
FROM TEST_DUPLICATE;

Вот её содержимое:

AAA       BBB
AAA       BBB
BBB       BBB
AAA       AAA
BBB       BBB
AAA       AAA
AAA       BBB

Теперь пометим в поле B дубликаты строкой DUPLICATED

UPDATE TEST_DUPLICATE
SET B = CONCAT(
  CASE WHEN A=@A AND B=@B THEN 'DUPLICATED' ELSE B END
  , /*тут фейковое слагаемое, просто чтобы изменить значения @A и @B*/
  CASE WHEN CONCAT((@A:=A),(@B:=B)) >= '' THEN '' END)
ORDER BY A, B, (SELECT @A:=''), (SELECT @B:='')  ;

SELECT *
FROM TEST_DUPLICATE;

Теперь содержимое таблицы:

AAA       BBB
AAA       DUPLICATED
BBB       BBB
AAA       AAA
BBB       DUPLICATED
AAA       DUPLICATED
AAA       DUPLICATED

Удаляем помеченные строки:

DELETE FROM TEST_DUPLICATE 
WHERE B = 'DUPLICATED';

SELECT *
FROM TEST_DUPLICATE;

получили что хотели:

AAA       BBB
BBB       BBB
AAA       AAA

Есть определённая критика такого решения. Но я для простоты самого подхода описал. При желании можно тему развить и использовать.

Дополнение: Всё то же самое можно сделать и на других СУБД, заменив накопление в переменную аналитическими функциями ROW_NUMBER, LEAD. В других СУБД это и выглядеть будет "симпатичней".

pegoopik
  • 3,550
1

1) Через другую таблицу

CREATE TEMPORARY TABLE tmp_tab AS SELECT DISTINCT * FROM your_table;
DELETE FROM your_table;
INSERT INTO your_table SELECT * FROM tmp_tab;
DROP TABLE tmp_tab;

2) Добавлением индекса. Лично сам так не пробовал, но говорят работает. Добавляется уникальный индекс, а дубли удаляются. Актуально для MySQL

ALTER IGNORE TABLE your_table ADD UNIQUE INDEX(id_category, id_product, position);
Viktorov
  • 7,195
  • 10
  • 37
  • 66
  • Полностью пересоздать таблицу, чтобы удалить дубликаты? мда... – pegoopik Jul 07 '16 at 05:25
  • @pegoopik насколько мне известно, в MySql это стандартный подход. Если у Вас есть решение лучше, поделитесь – Viktorov Jul 07 '16 at 05:29
  • можно создать столбец с уникальными значениями. С помощью него удалить дубликаты, потом удалить столбец. Что тоже довольно ресурсоёмко. Можно попробовать покопать в сторону накопления в переменную ещё. – pegoopik Jul 07 '16 at 06:02
  • @pegoopik то есть мы сначала добавляем колонку, потом не простым запросом ее заполняем(аналитических функций у нас нет), потом удаляем лишние строки, потом удаляем колонку. Выглядит если и лучше пересоздания, то не намного. Второй предложенный мой вариант один из самых быстрых для MySql, но создает индекс по всем полям, что не есть хорошо. Однако он явно быстрее предложенного вами варианта. Подведем итог. Вы критикуете мое решение, но лучше ничего так и не предложили. – Viktorov Jul 07 '16 at 06:36
  • не простым запросом ее заполняем(аналитических функций у нас нет)<< Да? Отчего же не простым. Три строчки в отформатированном виде(см. мой ответ)

    – pegoopik Jul 07 '16 at 07:24
  • @pegoopik, а Ваш вариант будет работать в Postgree ? Если нет, то по прежнему не понимаю, чем он лучше 2го предложенного мной. – Viktorov Jul 07 '16 at 07:53
  • Добавил в ответ пример без модификации структуры таблицы(DDL). Это я и имел ввиду в своём первом комментарии "Можно попробовать покопать в сторону накопления в переменную ещё.". Получилось кривовато, но явно быстрее создания уникального индекса или удаления всей таблицы. – pegoopik Jul 07 '16 at 07:59