如何合并 MySQL 表中的两条冗余记录,维护所有 PK/FK 关系?

Posted

技术标签:

【中文标题】如何合并 MySQL 表中的两条冗余记录,维护所有 PK/FK 关系?【英文标题】:How can I merge two redundant records in a MySQL table, maintaining all PK/FK relationships? 【发布时间】:2011-07-11 08:32:13 【问题描述】:

假设我有一个表customers,其中包含以下字段和记录:

id   first_name   last_name   email                  phone
------------------------------------------------------------------------
1    Michael      Turley      mturley@whatever.com   555-123-4567
2    John         Dohe        jdoe@whatever.com      
3    Jack         Smith       jsmith@whatever.com    555-555-5555
4    Johnathan    Doe                                123-456-7890

还有其他几个表,例如ordersrewardsreceipts,它们的外键customer_id与该表的customers.id相关。

如您所见,我的用户凭借他们的无限智慧,为 John Doe 创建了重复记录,其中包含不一致的拼写和缺失的数据。管理员注意到这一点,选择客户 2 和 4,然后单击“合并”。然后提示他们为每个字段选择正确的值,等等,我的 php 确定合并的记录应该如下所示:

id   first_name   last_name   email                  phone
------------------------------------------------------------------------
?    John         Doe         jdoe@whatever.com      123-456-7890

让我们假设 Doe 先生已经下了几个订单,获得了奖励,生成了收据.. 但其中一些与 id 2 相关联,一些与 id 4 相关联。合并的行需要匹配所有外国与原始行匹配的其他表中的键。

这就是我不知道该怎么做的地方。我的直觉是这样做:

DELETE FROM customers WHERE id = 4;

UPDATE customers
SET first_name = 'John',
    last_name  = 'Doe',
    email      = 'jdoe@whatever.com',
    phone      = '123-456-7890'
WHERE id = 2;

UPDATE orders, rewards, receipts
SET customer_id = 2
WHERE customer_id = 4;

我认为这可行,但如果稍后我添加另一个具有 customer_id 外键的表,我必须记住返回并将该表添加到我的合并函数中的第二个 UPDATE 查询中,否则可能会失去完整性.

必须有更好的方法来做到这一点。

【问题讨论】:

您可能会从 information_schema 中查询以 customer_id 作为列的表的列表,并相应地更新这些表。虽然这会带来其他风险。 【参考方案1】:

简短的回答是,没有更好的方法(我能想到)。

这是一个权衡。如果您发现有很多这样的情况,可能值得花一些时间编写一个更强大的算法来在添加新客户之前检查现有客户(即检查名字/姓氏的变化,将它们呈现给添加的人客户,问他们 2 或 3 次他们是否真的确定要添加这个新客户,等等)。如果这些实例不多,那么可能不值得投资。

除此之外,您的方法是我能想到的唯一方法。我实际上会删除这两条记录,并使用合并的数据创建一个新记录,从而产生一个新的客户 ID,而不是重新使用旧的,但这只是个人喜好 - 从功能上讲,它与您的方法相同。您仍然必须记住返回并修改合并函数以反映 customer.id 字段上的新关系。

【讨论】:

出于好奇,您为什么要避免重用旧 ID?在我看来,这只是增加了一个额外的步骤,因为您必须更新两组外键,而不仅仅是一组。 你是对的——就像我说的,只是个人喜好。从概念上讲,我认为它是从两个旧记录创建一个新记录,所以我希望能够在合并操作后进行完整性检查,以验证任何一个旧 ID 的实例都没有留在任何地方。在功能上,与您的方法没有什么不同。【参考方案2】:

至少,为了防止任何删除触发器导致一些级联效应,我会先做

更新 SomeTable 设置 CustomerID = CorrectValue where CustomerID = WrongValue

(对所有表执行此操作)...

那么 从 CustomerID = WrongValue 的客户中删除

至于重复数据...如果您缺少某些信息,请尝试找出“Will Smith、Bill Smith、William Smith”...有些可能是完全合法的不同人。

【讨论】:

好点,我会调换我的订单。至于重复,我不会尝试自动检测重复,合并将手动执行..所以由我的管理员来确定这些人是否合法地不同。合并将主要在客户抱怨他们的奖励总额错误或其他情况下执行,并且我们意识到“丢失”数据是由于重复客户造成的。【参考方案3】:

作为我评论的更新:

use information_schema;
select table_name from columns where column_name = 'customer_id';

然后循环遍历结果表并进行相应更新。

就个人而言,我会使用您的直觉解决方案,因为如果存在包含需要豁免的 customer_id 列的表,这可能会很危险。

【讨论】:

嗯。我想如果在同一台服务器上还有另一个带有 customer_id 列的数据库,那可能会很危险。假设我想在它出现的任何地方更新 customer_id,有没有办法将此查询限制为仅来自特定数据库的表?还是因为其他原因也很危险? 您可以将此查询限制为特定数据库,如下所示: select table_name from columns where column_name = 'customer_id' and table_schema = 'database_name'【参考方案4】:

我从 google 到这里,这是我的 2 美分:

SELECT `TABLE_NAME` 
FROM `information_schema`.`KEY_COLUMN_USAGE` 
WHERE REFERENCED_TABLE_SCHEMA='DATABASE'
  AND REFERENCED_TABLE_NAME='customers'
  AND REFERENCED_COLUMN_NAME='customer_id'

为保险添加数据库(您永远不会知道有人何时复制数据库)。

这里我们看的是外键本身,而不是寻找列名

如果您更改删除限制以限制在删除/迁移子项之前不能删除任何内容

【讨论】:

以上是关于如何合并 MySQL 表中的两条冗余记录,维护所有 PK/FK 关系?的主要内容,如果未能解决你的问题,请参考以下文章

如何为mysql中的每条记录检索表中的两条记录

如何用通配符比较同一张表中的两条记录?

是否有在 sql 表中的两条记录之间添加新记录的语法?

Netezza SQL 比较同一表中的两条记录

MySQL如何同时删除主外键关联的两张表中的数据

如何合并 MySQL 表中的重复行