2015-03-29 5 views
2

У меня есть таблица со следующей схемой:Удалить дубликаты из двух столбцов

+---------------+-------------+------+-----+---------+----------------+ 
| Field   | Type  | Null | Key | Default | Extra   | 
+---------------+-------------+------+-----+---------+----------------+ 
| id   | int(11)  | NO | PRI | NULL | auto_increment | 
| system_one_id | int(11)  | NO | MUL | NULL |    | 
| system_two_id | int(11)  | NO | MUL | NULL |    | 
| type   | smallint(6) | NO |  | NULL |    | 
+---------------+-------------+------+-----+---------+----------------+ 

Я хочу, чтобы удалить дубликаты, где «дубликат» определяется как либо:

  1. значений соответствия для обоих system_one_id и system_two_id между двумя рядами или
  2. "крест соответствует" ценности, т.е. row1.system_one_id = row2.system_two_id и row1.system_two_id = row2.system_one_id

Есть ли способ удалить оба вида дубликатов в одном запросе?

+0

Я использую MySQL, но я хотел бы быть в RDBMS-агностик, насколько это возможно. – theofabry

+0

еще один важный вопрос: если у вас есть 3 дубликата записи, которую вы хотите удалить? – jfun

ответ

2

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

delete t1 
from mytable t1 
join mytable t2 on t1.id > t2.id 
    and ((t1.system_one_id = t2.system_one_id 
    and t1.system_two_id = t2.system_two_id) 
    or (t1.system_one_id = t2.system_two_id 
    and t1.system_two_id = t2.system_one_id)) 

Условие соединения t1.id > t2.id предотвращает объединение строк в себя и выбирает позже добавлен ряд дубликатов пары для удаления.


FYI, в Postgres, подобная функциональность существует, но с другим синтаксисом:

delete mytable t1 
using mytable t2 
where t1.id > t2.id 
    and ((t1.system_one_id = t2.system_one_id 
    and t1.system_two_id = t2.system_two_id) 
    or (t1.system_one_id = t2.system_two_id 
    and t1.system_two_id = t2.system_one_id)) 
+0

@theofabry Да, вы можете сделать что-то подобное в postgres, но, к сожалению, синтаксис отличается (это нестандартные функциональные возможности SQL, и каждый из них придумал свой собственный синтаксис для его выражения). отредактировать мой ответ для версии postgres – Bohemian

1

Вот инструкция (надеюсь), выбирая все идентификаторы повторяющихся записей, вам нужно только обернуть ее командой delete (это ваша роль). ;-)

select A.ID from MYTABLE A 
left join MYTABLE B on 
(
    (A.SYSTEM_ONE_ID = B.SYSTEM_ONE_ID and A.SYSTEM_TWO_ID = B.SYSTEM_TWO_ID) 
    or 
    (A.SYSTEM_ONE_ID = B.SYSTEM_TWO_ID AND A.SYSTEM_TWO_ID = B.SYSTEM_ONE_ID) 
) 
where B.ID is not null and A.ID <> B.ID; 
+0

Ваше заявление, похоже, сработало, но я не могу его обернуть в команду delete, я делаю это: '' 'УДАЛИТЬ ОТ ссылки C WHERE C.id IN (your_statement)' '' (заменяя MYTABLE ссылкой Link конечно), и я получаю это: '' 'ERROR 1064 (42000): У вас есть ошибка в синтаксисе SQL; проверьте руководство, соответствующее версии сервера MySQL, для правильного синтаксиса для использования рядом с C, где C.id IN (выберите A.id из Link A left join Link B on ((A.system_one_id = B. 'в строке 1') '' – theofabry

0

Вы можете сгруппировать по least и greatest выбрать минимальный идентификатор каждой группы и удалять строки с другими идентификаторами.

delete from mytable 
where id not in (
    select * from (
     select min(id) 
     from mytable 
     group by greatest(system_one_id, system_two_id), 
     least(system_one_id, system_two_id) 
    ) t1 
) 
+0

Я думаю, что логика ошибочна, этот запрос предполагает '(system_one_id = 1, system_two_id = 3)' и '(system_one_id = 2, system_two_id = 2)' как 'duplicate', которых нет. Также я не уверен возможно ли «удалить и выбрать в то же время по таблице»? – jfun

+0

@ Спасибо, я добавил «наименьшее (system_one_id, system_two_id)» в группу, чтобы убедиться, что системные идентификаторы всегда одинаковые. можно удалить в одно и то же время, если вы заключите подзапрос в производную таблицу, как показано выше – FuzzyTree

+0

Отлично, теперь все в порядке, я думаю. – jfun

0

этот запрос начинается с мин идентификатора, а затем выбирает только not selected records in previous selection with regard to system_ids (t.id > t2.id)

delete from your_table t 
where id not in (select id from 
       (select distinct t.id 
       from your_table t 
       where 
       (
         select count(*) 
         from your_table t2 
         where t.id > t2.id 
          and ((t.system_one_id=t2.system_one_id 
           and t.system_two_id=t2.system_two_id) 
           or (t.system_one_id=t2.system_two_id 
           and t.system_two_id=t2.system_one_id)) 
       ) =0 
      ) tbl 
      ) 
Смежные вопросы