使用父ID将表连接到自身,直到不再有行

时间:2017-06-05 10:04:08

标签: mysql

我有一个表格,描述了对end_customers表所做的更改。当有人更改并end_customer我们在end_customers表格中创建新行并在end_customer_history表格中添加一行,其中end_customer_parent_id是旧end_customer的ID ,end_customer_child_id是新end_customer的ID。

最终客户表:

CREATE TABLE `end_customers` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `reference_person` varchar(255) DEFAULT NULL,
  `phone_number` varchar(255) NOT NULL,
  `email` varchar(255) DEFAULT NULL,
  `social_security_number` varchar(255) DEFAULT NULL,
  `comment` longtext,
  `token` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=101107 DEFAULT CHARSET=utf8;

最终客户历史表:

CREATE TABLE `end_customer_history` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `end_customer_parent_id` bigint(20) NOT NULL,
  `end_customer_child_id` bigint(20) NOT NULL,
  `user_id` bigint(20) NOT NULL,
  `date` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `FK_end_customer_parent` (`end_customer_parent_id`),
  KEY `FK_end_customer_child` (`end_customer_child_id`),
  KEY `FK_user` (`user_id`),
  CONSTRAINT `end_customer_history_old_ibfk_1` FOREIGN KEY (`end_customer_parent_id`) REFERENCES `end_customers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `end_customer_history_old_ibfk_2` FOREIGN KEY (`end_customer_child_id`) REFERENCES `end_customers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `end_customer_history_old_ibfk_3` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8;

我们现在正在重构架构,以便对end_customers表所做的更改直接编辑行而不是创建新行,并将旧数据的副本放在end_customer_history_new表中与end_customers相同的架构。

我需要将所有旧数据迁移到这个新表。 因此,对于我所拥有的每个end_customer,我需要检查end_customer_history中的条目是否为end_customer_child_id(已被修改),然后检查是否也存在该父级在end_customer_history作为一个孩子,然后检查那个托管父母是否也作为孩子出现在end_customer_history中,依此类推,直到没有更多行为止。

如何在一个迁移SQL脚本中执行此操作?

2 个答案:

答案 0 :(得分:0)

与Oracle不同,MySQL不提供递归查询父子层次结构的功能。如果您事先知道了级别,则可以编写自联接查询(或内部查询)。如果没有,则必须执行单个查询并在应用程序或存储过程中处理resursiveness。

Here是一个查询父子层次结构的示例,如果您已经知道该级别,here是如何在Oracle中执行此操作的示例(供您参考)。

答案 1 :(得分:0)

在我提出的解决方案中,它没有使用任何存储的函数来遍历行,因为我永远无法让它工作。它需要最终版本的最终客户,然后使用我们知道始终相同且唯一的token加入类似的行。

-- Move data from old end customer history to the new schema
-- May remove some redundant history, this will be ok
-- If an end customer has been changed more than once, all changes will have the same date (latest change) and the same user responsible for change
-- This is also ok

-- Insert the latest version of changed end_customers into new history table
INSERT INTO end_customer_history (name, reference_person, phone_number, email, social_security_number, comment, end_customer_id, user_id, date)
SELECT
ec.name, ec.reference_person, ec.phone_number, ec.email, ec.social_security_number, ec.comment, ech.end_customer_child_id, ech.user_id, ech.date
FROM end_customer_history_old AS ech
JOIN end_customers AS ec ON ec.id = ech.end_customer_parent_id
WHERE ech.end_customer_child_id IN (SELECT end_customer_id FROM orders);

-- Remove all the old data
DROP TABLE end_customer_history_old;

-- Insert all versions of an end_customer based on the latest history, joined on token (always the same)
INSERT INTO end_customer_history (name, reference_person, phone_number, email, social_security_number, comment, end_customer_id, user_id, date)
SELECT
parent.name, parent.reference_person, parent.phone_number, parent.email, parent.social_security_number, parent.comment, ech.end_customer_id, ech.user_id, ech.date
FROM end_customer_history AS ech
JOIN end_customers AS ec ON ec.id = ech.end_customer_id
JOIN end_customers AS parent ON parent.token = ec.token AND parent.id <> ec.id;

-- This unfortunately creates duplicates of some rows, so it looks like changes have been made multiple times (with no new data)
-- Create a temporary table that copies over unique rows from end_customer_history
CREATE TABLE temp AS
SELECT * FROM end_customer_history
GROUP BY
name, reference_person, phone_number, email, social_security_number, comment, end_customer_id, user_id, date;

-- Clear the end_customer_history table all together
TRUNCATE TABLE end_customer_history;

-- Copy over filtered unique history rows
INSERT INTO end_customer_history (name, reference_person, phone_number, email, social_security_number, comment, end_customer_id, user_id, date)
SELECT
name, reference_person, phone_number, email, social_security_number, comment, end_customer_id, user_id, date
FROM temp;

-- Remove temporary table
DROP TABLE temp;