Как удалить дубликаты из одной таблицы, но сохранить только одну запись? - PullRequest
0 голосов
/ 18 февраля 2020

Здравствуйте SQL запрос экспертов! У меня есть одна таблица с именем «mytable», которая имеет 2 столбца, такие как идентификатор и заголовок. Я попытался удалить дубликаты, кроме одной записи (строки), сравнивающей заголовок. Ниже был мой выбор:

DELETE FROM `myTable` AS `m1`
WHERE `m1`.`id` 
NOT IN (SELECT MIN(`b`.`id`) as `recordid` FROM `myTable` AS `b` GROUP BY `b`.`title`)

ошибка: Error in query (1064): Syntax error near '* FROM `myTable` AS `m1` WHERE `m1`.`id` NOT IN (SELECT MIN(`b`.`id`) as `reco' at line 1, но я столкнулся с проблемой и пытался решить эту проблему более 2 часов. Это кажется очень простой проблемой. Но я не могу понять это. Поэтому я прошу stackoverflow!

И в основном я вижу что-то странное. Я пробовал вот так, но в нем нет ошибок.

SELECT * FROM `myTable` AS `m1`
WHERE `m1`.`id` 
NOT IN (SELECT MIN(`b`.`id`) as `recordid` FROM `myTable` AS `b` GROUP BY `b`.`title`)

Когда я запускаю этот запрос, я могу получить список записей (строк), которые я хочу удалить из таблицы 'myTable'.

Почему я сталкиваюсь с проблемой удаления, хотя могу получить список для удаления?

Мне действительно нужна ваша помощь. Спасибо всем!

Ответы [ 4 ]

0 голосов
/ 18 февраля 2020

Я выяснил точную причину проблемы, с которой столкнулся, наконец. Я сослался на комментарий @Malakiyasanjay. Вы можете найти это здесь Как сохранить только одну строку таблицы, удалив дублирующиеся строки?

Я пытался так: (и это работало для меня также, но это заняло много времени для выполнения запроса для 30 000 строк)

delete from myTable
where id not in 
(select min(id) as min from (select * from myTable) as x group by title)

Проблема заключалась в том, что я не смог указать таблицу ' myTable ' в качестве целевой таблицы. поэтому я использовал (select * from myTable) as x и понял это.

Извините, я не могу объяснить более подробно об этом, потому что я не знаком с запросом mysql. Но вы должны отметить, что:

MySql не разрешает прямое использование целевой таблицы внутри подзапроса, подобного тому, который вы используете с NOT IN, но вы можете преодолеть это ограничение, Заключение подзапроса внутри другого. (Пожалуйста, обратитесь к ответу @forpas.)

Но вы должны заметить, что это занимает так много времени ... Это может вызвать ошибку тайм-аута. Я выполнил этот запрос для таблицы с 600 000 строк, но он не отвечал в течение нескольких дней. Итак, я пришел к выводу, что эта идея вполне подходит для небольшой таблицы базы данных.

Надеюсь, это полезно для всех! :)

0 голосов
/ 18 февраля 2020

Я думаю, что ответ Гордона лежит в основе. Недавно пришлось сделать что-то похожее, в итоге получилось следующее (применительно к вашей ситуации):

DELETE FROM mytable WHERE id IN (
    SELECT *
    FROM (
        SELECT m.id
        FROM my_table m
        WHERE m.id NOT IN (
            SELECT MAX(m.id)
            FROM my_table sub
            GROUP BY sub.title
            HAVING COUNT(sub.title) > 1
        )
        AND m.id NOT IN (
            SELECT MAX(sub2.id)
            FROM my_table sub2
            GROUP BY sub2.title
            HAVING COUNT(sub2.title) = 1
        )
  ) AS m
)

Требовалась дополнительная обертка (если я правильно помню), поскольку подзапрос не был разрешен в операторе DELETE ( но можно использовать как показано).

При этом будут удалены все записи по идентификатору, количество которых (title) больше 0, но не будет удалена самая последняя (max) запись.


ПРИМЕЧАНИЕ: это очень интенсивный запрос. Рекомендуются индексы для ID и заголовка, и даже тогда: sloooo wwww. Выполнено это всего за 100 тыс. Записей с индексами и все еще занимает около 10 секунд.

0 голосов
/ 18 февраля 2020

Синтаксис:

DELETE FROM `myTable` AS `m1`

неверен. Это должно быть:

DELETE m1 FROM `myTable` AS `m1`

, но вам не нужно псевдоним таблицы, вы можете просто сделать

DELETE FROM `myTable`

Также MySql не разрешает прямое использование цели таблица внутри подзапроса, подобная той, которую вы используете с NOT IN, но вы можете обойти это ограничение, заключив подзапрос в другой:

DELETE FROM `myTable` 
WHERE `id` NOT IN (
  SELECT `recordid`
  FROM (
    SELECT MIN(`id`) as `recordid` 
    FROM `myTable` 
    GROUP BY `title`
  ) t  
)

Я удалил псевдонимы вложенного подзапроса, поскольку они не нужны .

0 голосов
/ 18 февраля 2020

Вы можете сформулировать это как:

delete m
    from mytable m left join
         (select m2.title, min(m2.id) as min_id
          from mytable m2
          group by m2.title
         ) m2
         on m.title = m2.title and m.id > m.min_id;

Для производительности вы хотите в индексе на (title, id).

...