优化MySQL-查询去重和调整外键引用

Optimizing MySQL-Query for removing duplicates and adjusting foreign key references

我有以下 tables:address_table

CREATE TABLE `address` (
  `id` varchar(255) NOT NULL,
  `city` varchar(255) DEFAULT NULL,
  `street` varchar(255) DEFAULT NULL,
  `house_number` varchar(255) DEFAULT NULL,
  `zip_code` varchar(255) DEFAULT NULL,
  `country` varchar(2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

和customer_address_table

CREATE TABLE `customer_address` (
  `customer_id` int DEFAULT NULL,
  `address_id` varchar(255) DEFAULT NULL,
  `id` int NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`),
  KEY `address_id` (`address_id`),
  KEY `customer_id` (`customer_id`),
  CONSTRAINT `customer_address_ibfk_1` FOREIGN KEY (`address_id`) REFERENCES `address` (`id`),
  CONSTRAINT `customer_address_ibfk_2` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`)
) ENGINE=InnoDB;

地址table存储地址,customer_addresstable存储客户与地址的关系。一个客户可以有多个地址,因此第二个 table。 在地址 table 中有重复的行(不同的 id 但相同的位置)并且在 customer_address table 中的每个地址都有一个 address_id 引用,所以每一行在地址 table 被引用。

我想删除地址 table 中的重复项,因此将 customer_address table 中的引用调整为每个位置剩余的一个(最低)ID。 我编写了以下有效的查询,问题是它需要永远执行(估计 73 小时)。有大约。地址 table 和 ca 中的 900'000 行。其中 390'000 个是唯一的(由 group by 语句过滤)。

update customer_address as ca set ca.address_id = 
        (select dfa.id from (select min(id) as id, zip_code, city, street, house_number from varys_dev.address group by zip_code, city, street, house_number) as dfa
        join address as a on (dfa.city = a.city and dfa.zip_code = a.zip_code and dfa.street = a.street and dfa.house_number = a.house_number)
        where a.id = ca.address_id limit 1);

有什么方法可以提高该查询的性能吗?我尝试为连接子句使用的属性编制索引,但这没有任何帮助。

如果我没猜错,你需要 table 将 id 解码为组内的最小 id

update `customer_address` as ca 
join (select id, min(id) over (partition by zip_code, city, street, house_number) mid
      from `address` 
     ) as a on ca.id = a.id
set ca.address_id = a.mid

不要一次性完成。相反,编写一个循环(在客户端代码中)到

  1. 找到 'next' 几个重复项
  2. 修复它们。
  3. 进入下一批

这可能仍需要几天时间,但不会影响系统的其余部分。

步骤 1

SELECT a.id, b.id
    FROM address AS a
    JOIN address AS b  USING(city, street, house_number, state, zip_code, country)
    WHERE a.id BETWEEN $left_off AND $left_off + 100

第 2 步

利用这个简短的、可能为空的成对列表,修复链接。

第 3 步

$left_off = $left_off + 100

没有就退出

完成后最好加上

UNIQUE(city, street, house_number, state, zip_code, country)

以防止进一步重复。如果添加索引失败,那么还有更多的重复数据需要清理;从上次停下的地方继续。

有关删除分块等的更多信息:http://mysql.rjweb.org/doc.php/deletebig#deleting_in_chunks