如何在MySQL中将关系层次结构转换为闭包表?

问题描述 投票:1回答:1

我最近开始使用closure table,当我不得不将简单的父子关系从人转换为血统时,它工作得很好,但是现在我必须转换祖父母/父母子女和曾祖父母/父母子女关系,甚至可能比关闭表中的关系更长。这是我需要从中提取关系的表:

CREATE TABLE `person` (
    `person` BIGINT UNSIGNED NOT NULL ,
    `parent` BIGINT UNSIGNED NULL ,
    PRIMARY KEY (`person`) ,
    INDEX `idx_person_has_parent` (`parent` ASC),
    CONSTRAINT `fk_person_has_parent`
        FOREIGN KEY (`parent`)
        REFERENCES `person` (`person`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION
);

这是我需要转换为的表:

CREATE TABLE `lineage` (
    `ancestor` BIGINT UNSIGNED NOT NULL,
    `descendant` BIGINT UNSIGNED NOT NULL,
    `length` INT UNSIGNED NOT NULL,
    PRIMARY KEY (`ancestor`, descendant`),
    CONSTRAINT `fk_ancestor_has_descendant`
        FOREIGN KEY (`descendant`)
        REFERENCES `person` (`person`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
    CONSTRAINT `fk_descendant_has_ancestor`
        FOREIGN KEY (`ancestor`)
        REFERENCES `person` (`person`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION
);

我遇到的最大问题是为祖父母/外祖父母的条目保存正确的长度,该长度应为2。祖父母-父母和父母-子女都为1,并且它们彼此关联时的长度均为0 (孩子,父母,父母等)。我可以在查询中执行此操作,还是需要某种程序?

mysql select foreign-keys relational-database hierarchy
1个回答
0
投票
这是一个古老的脚本,我在6/7年前完成了此工作,以在PHP / Propel中完成此任务。希望它对其他人有用:

require_once 'common/Autoloader.php'; require_once 'propel/Propel.php'; \Propel::init('db/runtime-conf.php'); function truncateHierarchy(\PropelPDO $propel) { /* @var $state \PropelStatement */ $state = $propel->prepare('TRUNCATE database.person_hierarchy'); $state->execute(); } function insertHierarchy(\PropelPDO $propel, $length) { if ($length == 0) { $state = $propel->prepare('INSERT INTO database.person_hierarchy SELECT id, id, 0 FROM person;'); $state->execute(); return $state->rowCount(); } else if ($length == 1) { $state = $propel->prepare('INSERT INTO database.person_hierarchy SELECT parent_person_id, id, 1 FROM person WHERE id != parent_person_id;'); $state->execute(); return $state->rowCount(); } else { $sql = "INSERT INTO database.person_hierarchy \n"; $sql .= "SELECT p.parent_person_id, c" . ($length - 1) . ".id, " . $length . " FROM database.person AS p \n"; for ($i = 1; $i <= $length - 1; $i++) { $sql .= "LEFT JOIN person AS c" . $i . " ON " . ($i == 1 ? 'p.id' : 'c' . ($i - 1) . '.id') . " = c" . $i . ".parent_person_id \n"; } $sql .= "WHERE p.parent_person_id != p.id \n"; for ($i = 1; $i <= $length - 1; $i++) { $sql .= "AND c" . $i . ".parent_person_id != c" . $i . ".id \n"; } echo $sql; $state = $propel->prepare($sql); $state->execute(); return $state->rowCount(); } } /* @var $connect \PropelConnection */ $propel = \Propel::getConnection(); $propel->beginTransaction(); try { truncateHierarchy($propel); $propel->commit(); } catch (\Exception $e) { error_log_exc($e); echo "Failed to truncate person hierarchy!\n"; $propel->rollBack(); exit(); } $length = 0; $inserts = -1; while ($inserts !== 0 || $length != 10) { $propel->beginTransaction(); try { $inserts = insertHierarchy($propel, $length); if ($inserts == 0) { echo "No persons exist at length " . $length . ".\n"; } else { echo $inserts . " rows inserted for length " . $length . ".\n"; } $length++; $propel->commit(); } catch (\Exception $e) { error_log_exc($e); echo "Failed to regenerate person hierarchy!\n"; $propel->rollBack(); exit(); } } echo "Regenerated person hierarchy!\n"; exit();

© www.soinside.com 2019 - 2024. All rights reserved.