【问题标题】:Fix duplicate entries in table修复表中的重复条目
【发布时间】:2012-01-18 20:33:08
【问题描述】:

我刚刚发现我的生产服务器上的一个表(包含大约 35K 条记录)在具有 AUTO_INCREMENT 的 INT(11) 列中包含 588 个重复条目。 该列上缺少 UNIQUE 键,因此这可能是原因。

关于如何为所有重复条目提供唯一 ID,然后将 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

【问题讨论】:

  • 您不需要 auto_increment 列上的唯一键,它必须在 show create table 中被标识为主键。您可以发布您的架构吗?
  • 不确定如何获得一个好的架构 DESC tablename 在 PHPMyAdmin 中不能很好地输出它
  • 您有权访问服务器?您可以从 mysql cli 发出命令:show create table。我很想知道您如何能够将 auto_increment 添加到列而不将其标识为 PRIMARY KEY (id)
  • 你可以有一个不是主键的 auto_increment。它所需要的只是一个简单的索引。
  • 如果 item_id 没有在其他表中用作外键,您可以简单地删除该列并将其重新创建为 AUTO_INCREMENT PRIMARY KEY

标签: mysql auto-increment unique-index


【解决方案1】:

这样的事情怎么样?首先在备份上再次对其进行测试。

# 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;

【讨论】:

  • 在表格的副本上对其进行了测试,它似乎工作得很好!谢谢!
  • 很高兴它奏效了——请注意@ypercube 的答案要优雅得多。
【解决方案2】:

由于您已经有一个UNIQUE 键,您可以使用它来创建一个UPDATE 语句,将唯一ID 重新分配给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上添加唯一索引

【讨论】:

  • 这并不完全正确。它使用item_ID='0'生成数千条记录
  • @ThomasGG:是的,有一个小错误,现在编辑。再试一次。
【解决方案3】:

有趣。你有一个没有主键引用的 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    

试一试,这些步骤未经测试,但应该会让你走上正确的道路。

【讨论】:

  • 这会重新生成 item_ID 对吗?我想保留我当前的 ID,并且只使用具有重复 ID 的新 ID 更新记录。我可以在 PHP 中使用 while 循环来检查记录是否重复,并使用 MAX(item_ID) 给它一个新 ID,但这听起来很冒险
  • 是的,它会重新生成它们。如果是这种情况,那么您可以编写一个应用程序来循环遍历 id,识别受骗者,然后为受骗者分配新值。然后在分配了新值后,运行 alter 语句以指示 Item_IDprimary key 而不仅仅是索引。
猜你喜欢
  • 2016-05-31
  • 2016-08-11
  • 1970-01-01
  • 2013-03-09
  • 1970-01-01
  • 2012-04-10
  • 2012-12-06
  • 2021-02-02
  • 1970-01-01
相关资源
最近更新 更多