如何复制嵌套的 SQL 表(实现更改历史记录)

How to copy nested SQL tables (implementing a change history)

我是一名软件开发学徒,目前正在尝试实施一个简单的清单,但要跟踪每个 version/every 所述清单的更改会增加麻烦。这意味着无论何时添加新类别或更改其名称(问题相同),都会在“ChecklistVersion”table 中创建一个新行,并将最新 ChecklistVersion 的所有类别和问题复制到新的那一个。这是保存整个更改历史所必需的。见下文database scheme.

这个数据库的另一个怪癖是,它是由不使用主键自动递增的 CMS (Intrexx) 使用和创建的。

为了进一步说明,此方案用于表示以下内容:

到目前为止,复制过程适用于类别,但我一直坚持复制问题。这些是我的查询:

首先,创建一个新的 ChecklistVersion 条目和 return 它的 ID:

insert into ChecklistVersion
(
    ID,
    ChecklistID
)
values
(
    (
        select coalesce(max(ID)+1, 1) /* Get next ID */
        from ChecklistVersion
    ),
    ? /* This value gets replaced by the checklist ID in Java code */
)
returning ID;

下一步是复制类别:

insert into Category
(
    ID,
    ChecklistVersionID,
    Name
)
select
       (select max(ID)+1 from Category) - 1 + ROW_NUMBER() over (order by ID), /* See (1) below */
       ?, /* This gets replaced by the ChecklistVersion ID returned by the previous query */
       Name
from Category
where ChecklistVersionID = ?; /* Gets replaced in code by the most recent ChecklistVersion ID
                                 which serves as the source for the categories to be copied */

(1):该行生成类别的下一个最高 ID,它很难看,但我不知道更好的方法。子选择只执行一次,因此 ROW_NUMBER() 需要进一步递增。

现在想象以下 table 个条目:

           Category                              Question

| ID | ChecklistVersionID | Name |    | ID | CategoryID | Text      |
|----|--------------------|------|    |----|------------|-----------|
| 1  | 1                  | Cat1 |    | 1  | 2          | Question1 |
| 2  | 1                  | Cat2 |    | 2  | 1          | Question2 |
                                      | 3  | 2          | Question3 |

成功复制 ID 为 1 的 ChecklistVersion 后,预期结果如下:

           Category                              Question

| ID | ChecklistVersionID | Name |    | ID | CategoryID | Text      |
|----|--------------------|------|    |----|------------|-----------|
| 1  | 1                  | Cat1 |    | 1  | 2          | Question1 |
| 2  | 1                  | Cat2 |    | 2  | 1          | Question2 |
| 3  | 2                  | Cat1 |    | 3  | 2          | Question3 |
| 4  | 2                  | Cat2 |    | 4  | 4          | Question1 |
                                      | 5  | 3          | Question2 |
                                      | 6  | 4          | Question3 |

所以不仅每个问题都需要复制,还需要分配相应的newCategoryID。因此,我需要以某种方式获取类别的先前 ID 和新 ID 之间的映射(本例中为 1 -> 3 和 2 -> 4)。

我希望我的问题很清楚,感谢任何帮助!

我找到了使用临时 table 的解决方案,以便在插入类别和问题时使用来自选择语句的映射,这是我的解决方案:

/* Create temporary table with the categories that will be copied,
   provides the mapping between the old and new category IDs (CopyID and ID).
   This is pretty much equivalent to the select statement in the original insertion
   query. */
create temp table CategoriesToCopy as
select (select max(ID)+1 from Category) - 1 + ROW_NUMBER() over (order by ID) as CopyID,
       ? as ChecklistVersionID,
       Name,
       ID
from Category
where ChecklistVersionID = ?;

/* Insert categories just like before, but using the temporary
   table instead */
insert into Category
(
    ID,
    ChecklistVersionID,
    Name
)
select CopyID, ChecklistVersionID, Name
from CategoriesToCopy;

/* Insert questions using mapping of temporary table */
insert into Question
(
    ID,
    CategoryID,
    QuestionText
)
select
    (select max(ID)+1 from Question) - 1 + ROW_NUMBER() over (order by Question.ID),
    CategoriesToCopy.CopyID, /* use new category ID for copied question */
    Question.QuestionText
from Question
inner join CategoriesToCopy /* join rows which correspond to the old category IDs */
    on CategoriesToCopy.ID = Question.CategoryID;

/* Delete temporary table */
drop table CategoriesToCopy;