优化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
不要一次性完成。相反,编写一个循环(在客户端代码中)到
- 找到 'next' 几个重复项
- 修复它们。
- 进入下一批
这可能仍需要几天时间,但不会影响系统的其余部分。
步骤 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
我有以下 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
不要一次性完成。相反,编写一个循环(在客户端代码中)到
- 找到 'next' 几个重复项
- 修复它们。
- 进入下一批
这可能仍需要几天时间,但不会影响系统的其余部分。
步骤 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