Исправить повторяющиеся записи в таблице

Я только что узнал, что таблица на моем производственном сервере (которая содержит около 35 тысяч записей) содержит 588 повторяющихся записей в столбце INT (11) с AUTO_INCREMENT. Ключ UNIQUE отсутствует в этом столбце, поэтому, вероятно, причина в этом.

Любые идеи о том, как дать всем повторяющимся записям уникальный идентификатор, а затем добавить ключ UNIQUE в столбец, чтобы это никогда не повторилось?

Схема таблицы:

CREATE TABLE `items` (
 `item_ID` int(11) unsigned NOT NULL auto_increment,
 `u_ID` int(10) NOT NULL default '0',
 `user_ID` int(11) NOT NULL default '0',
 `p_ID` tinyint(4) NOT NULL default '0',
 `url` varchar(255) NOT NULL,
 `used` int(10) unsigned NOT NULL,
 `sort` tinyint(4) NOT NULL,
 `last_checked` int(11) NOT NULL,
 `unixtime` int(11) NOT NULL,
 `switched` int(11) NOT NULL,
 `active` tinyint(1) NOT NULL default '0',
 UNIQUE KEY `unique` (`p_ID`,`url`),
 KEY `index` (`u_ID`,`item_ID`,`sort`,`active`),
 KEY `index2` (`u_ID`,`switched`,`active`),
 KEY `item_ID` (`item_ID`),
 KEY `p_ID` (`p_ID`),
 KEY `u_ID` (`u_ID`)
) ENGINE=MyISAM AUTO_INCREMENT=42755 DEFAULT CHARSET=utf8

person Thomas G    schedule 12.12.2011    source источник
comment
Вам не нужен уникальный ключ в столбце auto_increment, он ДОЛЖЕН быть идентифицирован как первичный ключ в show create table. Можешь выложить свою схему?   -  person Mike Purcell    schedule 13.12.2011
comment
не знаю, как получить красивую схему DESC tablename, она плохо выводится в PHPMyAdmin   -  person Thomas G    schedule 13.12.2011
comment
У вас есть доступ к серверу? вы можете ввести команду из mysql cli: show create table. Мне любопытно посмотреть, как вы смогли добавить auto_increment к столбцу, не идентифицируя его как PRIMARY KEY (id)   -  person Mike Purcell    schedule 13.12.2011
comment
У вас может быть auto_increment, который не является первичным ключом. Все, что ему нужно, это простой индекс на нем.   -  person ypercubeᵀᴹ    schedule 13.12.2011
comment
stackoverflow.com/questions/8114535/   -  person ypercubeᵀᴹ    schedule 13.12.2011
comment
добавил схему. извините за форматирование, не удалось попасть в один блок кода..   -  person Thomas G    schedule 13.12.2011
comment
Если item_id не использовался в качестве внешнего ключа в других таблицах, вы можете просто удалить столбец и воссоздать его как AUTO_INCREMENT PRIMARY KEY.   -  person ypercubeᵀᴹ    schedule 13.12.2011
comment
@ypercube, к сожалению, это не вариант   -  person Thomas G    schedule 13.12.2011
comment
@ypercube: MySQL не будет жаловаться на повторяющиеся идентификаторы item_ID?   -  person Mike Purcell    schedule 13.12.2011
comment
@DigitalPrecision: дубликатов не будет, если вы сначала удалите столбец :)   -  person ypercubeᵀᴹ    schedule 13.12.2011


Ответы (3)


Как насчет чего-то подобного? Сначала снова проверьте его на резервной копии.

# Copy duplicate records
CREATE TABLE newitem SELECT * FROM items WHERE item_ID IN 
    (SELECT item_ID FROM itemd GROUP BY item_ID HAVING COUNT(*) > 1);

# remove auto increment from id in new table
ALTER TABLE newitem DROP INDEX Item_ID, MODIFY item_ID int;

# delete duplicates from original
DELETE FROM item WHERE item_ID IN (SELECT DISTINCT item_ID FROM newitem);

#Update column to be primary key
ALTER TABLE items DROP INDEX Item_ID, ADD PRIMARY KEY (Item_ID);

# Set new duplicate ID's to null
UPDATE newitem SET item_ID=NULL;

# Insert records back into old table
INSERT INTO item SELECT * FROM newitem;

# Get rid of work table
DROP newitem;
person Adrian Cornish    schedule 12.12.2011
comment
протестировал его на копии стола, вроде работает отлично! Спасибо! - person Thomas G; 13.12.2011
comment
Рад, что это сработало - просто обратите внимание, что ответ @ypercube намного элегантнее. - person Adrian Cornish; 13.12.2011

Поскольку у вас уже есть ключ UNIQUE в таблице, вы можете использовать его для создания оператора UPDATE, который переназначает уникальные идентификаторы для item_id:

UPDATE
        items AS it
    JOIN
        ( SELECT 
              i.p_ID, i.url, @id:= @id+1 AS id
          FROM 
                  items AS i  
              CROSS JOIN 
                  ( SELECT @id:=0 ) AS dummy
          ORDER BY
              i.p_ID, i.url
        ) AS unq
      ON 
      (unq.p_ID, unq.url) = (it.p_ID, it.url)
SET 
    it.item_id = unq.id ;

Затем вы можете добавить уникальный индекс на item_id

person ypercubeᵀᴹ    schedule 12.12.2011
comment
Это работает не совсем корректно. Он генерирует тысячи записей с item_ID='0' - person Thomas G; 13.12.2011
comment
@ThomasGG: Да, была небольшая ошибка, сейчас отредактировал. Попробуйте снова. - person ypercubeᵀᴹ; 13.12.2011

Интересно. У вас есть auto_increment без ссылки на первичный ключ, просто индекс, поэтому у вас есть дубликаты в первую очередь. Если вы попытаетесь обновить и назначить primary key (item_ID), MySQL будет жаловаться из-за дубликатов в столбце item_ID.

Ваш движок - MyISAM, что означает, что у вас нет ограничений FK, поэтому вы можете сделать mysqldump таблицы, обрезать таблицу, обновить схему, а затем повторно импортировать данные. При повторном импорте MySQL должен правильно вставить все строки с действительно уникальными Item_Ids.

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

$ mysqldump -u <user_name> -h <db_host> --opt <database_name> --single-transaction > backup.sql

mysql> truncate table `items`;

mysql> ALTER TABLE `items` DROP INDEX `Item_ID`, ADD PRIMARY KEY (`item_ID`), AUTO_INCREMENT = 1;

$ vi backup.sql # Remove the AUTO_INCREMENT reference from the Create Table syntax

$ mysql -h <host_name> <db_name> -u <username> -p < backup.sql    

Попробуйте, эти шаги не проверены, но должны направить вас на правильный путь.

person Mike Purcell    schedule 12.12.2011
comment
это восстановит правильное значение item_ID? Я хочу сохранить свой текущий идентификатор и обновить записи только с новым идентификатором, который имеет повторяющиеся идентификаторы. Я мог бы использовать цикл while в PHP, чтобы проверить, является ли запись дубликатом, и присвоить ей новый идентификатор с помощью MAX(item_ID), но это звучит рискованно. - person Thomas G; 13.12.2011
comment
Да, это регенерирует их. Если это так, то вы можете написать приложение, которое перебирает идентификаторы, идентифицирует дубликаты, а затем присваивает им новые значения. Затем, когда новые значения будут присвоены, запустите оператор alter, чтобы указать, что Item_ID является primary key, а не просто индексом. - person Mike Purcell; 13.12.2011