优化 MySQL-Query 以去除重复和调整外键引用

问题描述

我有以下表格: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;

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

我想删除地址表中的重复项,因此将 customer_address 表中的引用调整为每个位置剩余的一个(最低)ID。 我写了以下有效的查询,问题是,它需要永远执行(估计 73 小时)。有大约。地址表中的 900'000 行和 ca。其中 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,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);

有什么方法可以提高该查询性能?我尝试为 join-on 子句使用的属性建立索引,但这没有任何帮助。

解决方法

如果我猜对了,您需要一个表将 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. 找到“下一个”重复项
  2. 修复它们。
  3. 进入下一批

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

第一步

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

步骤 2

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

第三步

$left_off = $left_off + 100

如果没有就退出。

完成后最好添加

UNIQUE(city,country)

以防止进一步的重复。如果添加索引失败,则有更多的 dup 需要清理;从上次中断的地方继续。

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