我有一个拥有1000个人事记录的MySQL数据库,通常有重复记录。
对于每个至少有一个副本的情况,我希望能够删除除了一个副本之外的所有副本,然后用我没有删除的外键更新对这些删除外键的任何引用。
例如,我们在下面看到Star Lord
的两个实例:
+-----------------------+
| `users` |
+------+----------------+
| id | name |
+------+----------------+
| 1 | Star Lord |
+------+----------------+
| 2 | Star Lord |
+------+----------------+
| 3 | Iron Man |
+------+-----+----------+
+-----------------------+
| `messages` |
+------+-----+----------+
| from | to | text |
+------+-----+----------+
| 1 | 5 | hi |
+------+-----+----------+
| 2 | 5 | how r u |
+------+-----+----------+
| 5 | 2 | Good, u? |
+------+-----+----------+
这两个表应该成为:
+-----------------------+
| `users` |
+------+----------------+
| id | name |
+------+----------------+
| 1 | Star Lord |
+------+----------------+
| 3 | Iron Man |
+------+-----+----------+
+-----------------------+
| `messages` |
+------+-----+----------+
| from | to | text |
+------+-----+----------+
| 1 | 5 | hi |
+------+-----+----------+
| 1 | 5 | how r u |
+------+-----+----------+
| 5 | 1 | Good, u? |
+------+-----+----------+
可以这样做吗?我很乐意根据需要使用PHP。
我找到了以下内容,但它仅用于查找外键使用情况,而不是替换特定键值的实例:MySQL: How to I find all tables that have foreign keys that reference particular table.column AND have values for those foreign keys?
奖励积分
可能是需要在users
表中合并的其他数据。例如,ID为#1的Star Lord
可能会填充phone
字段,而ID为#2的Star Lord
字段会为email
。
最坏情况:他们两个都有一个字段,数据存在冲突。
答案 0 :(得分:2)
我建议:
创建正确数据表。一个好的起点可能是:
CREATE TABLE users_new LIKE users;
ALTER TABLE users_new ADD UNIQUE (name);
INSERT INTO users_new
(id, name, phone, email)
SELECT MIN(id), name, GROUP_CONCAT(phone), GROUP_CONCAT(email)
FROM users
GROUP BY name;
请注意,由于您的情况最差"在" Bonus Points"下观察,您可能希望在归档基础users
数据之前手动验证此表的内容(我建议不要永久删除,以防万一)。
更新现有的外国关系:
UPDATE messages
JOIN (users uf JOIN users_new unf USING (name)) ON uf.id = messages.from
JOIN (users ut JOIN users_new unt USING (name)) ON ut.id = messages.to
SET messages.from = unf.id,
messages.to = unt.id
如果要更新许多表,可以在users
和users_new
之间缓存联接的结果 - 或者:
位于旧new_id
表中的users
列中:
ALTER TABLE users ADD new_id BIGINT UNSIGNED;
UPDATE users JOIN users_new USING (name)
SET users.new_id = users_new.id;
UPDATE messages
JOIN users uf ON uf.id = messages.from
JOIN users ut ON ut.id = messages.to
SET messages.from = uf.new_id,
messages.to = ut.new_id;
或者在新的(临时)表中:
CREATE TEMPORARY TABLE newid_cache (
PRIMARY KEY(old_id),
KEY(old_id, new_id)
) ENGINE=MEMORY
SELECT users.id AS old_id, users_new.id AS new_id
FROM users JOIN users_new USING (name);
UPDATE messages
JOIN newid_cache nf ON nf.old_id = messages.from
JOIN newid_cache nt ON nt.old_id = messages.to
SET messages.from = nf.new_id,
messages.to = nt.new_id;
将users
替换为users_new
,或者修改您的应用程序以使用新表代替旧表。
ALTER TABLE users RENAME TO users_old;
ALTER TABLE users_new RENAME TO users;
根据需要更新任何外键约束。
答案 1 :(得分:0)
我喜欢对此有条不紊,而你可以在一个复杂的查询中编写它,这是一个优化,除非它是显而易见的,是一个不必要的。
首先备份你的数据库:)
Create a table
来保存您要保留的用户的ID。
填写说明
Insert into Keepers Select keep_id From (Select Min(id) as keep_id,`name` From `users`)
之后,它只是加入了一些更新。
e.g。
UPDATE
`messages` m JOIN
keepers k
ON k.keeper_id = m.from
SET m.from = k.keeper_id
UPDATE
`messages` m JOIN
keepers k
ON k.keeper_id = m.to
SET m.to = k.keeper_id
然后摆脱你不想要的用户
Delete `users`
from `users` u
outer join keepers on k.keeper_id = u.id
where i.id is null
When
一切都很好,例如你开始时的消息数量相同,没有人和自己说话等等。
Delete the keepers table.
语法未选中,但应该关闭。