将 sql while 循环重构为常规插入

refactoring sql while loop to regular inserts

我在存储过程中同时插入父记录和子记录。

我没有让外部代码进行嵌套调用来创建每个父项,然后创建该父项的每个子项(这比我目前的方法还要慢),我给 sql 一个逗号分隔的子项列表我放入临时 table (#TempParentChildUpdateTable) 的类型,它与 table 值参数 (@PenguinParentChildUpdate) 中的父记录关联。

然后我遍历两者,插入父项,然后插入所有相关的子项。

问题是 while 循环在每秒 13 个请求时表现不佳。

如何让它更快?如何取出 while 循环?

有没有办法用非循环插入来做到这一点?如果是这样,字符串解析可以发生在内部插入中吗?

DECLARE @RowCnt int = 0;
DECLARE @CounterId int = 1;
SELECT @RowCnt = COUNT(*) FROM @PenguinParentChildUpdate;
WHILE @CounterId <= @RowCnt
BEGIN
    SELECT @GrandparentId = WorkflowInstanceId,
        @ParentType = ParentType,
        @ChildIds = ChildIds
        FROM @PenguinParentChildUpdate

    -- insert parent record

    INSERT INTO WorkflowInstanceRole (ParentType, GrandparentId)
        VALUES(@ParentType, @GrandparentId)
    SET @ParentId = SCOPE_IDENTITY()
                
    -- identify children

    -- convert comma separated list (e.g. 4,91,12,6) into separate rows

    INSERT INTO #TempParentChildUpdateTable (sq.ChildId, sq.ParentId)
        select convert(int, value) ChildId, @ParentId RoleId FROM string_split(@ChildIds, CHAR(44))

    -- insert children 7787+ =
    IF (@ChildIds IS NOT NULL AND LEN(LTRIM(RTRIM(@ChildIds))) > 0)
    BEGIN
        INSERT INTO dbo.PenguinParentChild
        (
            grandparentId,
            childid,
        )
        select
            @ParentId,
            childId,
        from
            #TempParentChildUpdateTable tsru
        where
            tsru.ParentId = @ParentId
    END

    SET @CounterId = @CounterId + 1
END

我正在努力遵循循环中的一些逻辑,但我认为前提是

  1. 插入 WorkFlowInstanceRole
  2. 捕获插入的记录然后在此基础上进一步插入children。

由于第 2 步需要 WorkFlowInstanceRole 中没有的数据,因此您需要使用 MERGE 添加新行,而不是标准插入。这允许您做的是从源 table 捕获 ChildIds,即使您没有插入它们。应该这样做(我不得不猜测某些数据类型):

DECLARE @Output TABLE 
(   
    ParentId INT, 
    ParentType INT, 
    GrandParentId INT, 
    ChildIDs VARCHAR(MAX)
);

MERGE INTO WorkflowInstanceRole AS t
USING @PenguinParentChildUpdate AS s
    ON 1 = 0 -- Will never be true so will always insert
WHEN NOT MATCHED THEN 
    INSERT (ParentType, GrandparentId)
    VALUES (s.ParentType, s.WorkflowInstanceId)
OUTPUT inserted.ParentId, inserted.ParentType, inserted.GrandParentId, s.ChilDIds
    INTO @Output (ParentId,ParentType, GrandParentId, ChildIDs);

INSERT INTO dbo.PenguinParentChild (GrandParentId, ChildId)
SELECT  o.ParentId,
        CONVERT(int, ss.value) 
FROM    @Output AS o
        CROSS APPLY STRING_SPLIT(i.ChileIds, CHAR(44)) AS ss;

关键部分确实是 MERGE*,因为条件是 1=0 那么这将始终插入。与 INSERT 不同,合并中的 OUTPUT 子句将允许您捕获新插入的标识值和 non-inserted ChildIds 列。

这是输出到一个临时 table 中,然后您可以使用它与 CROSS APPLY STRING_SPLIT() 一起插入 child 条记录。

可能存在一些数据错误,逻辑可能不是 100% 完美,但这应该是朝着正确方向迈出的一小步。


*MERGE 有一个 number of known bugs,我通常建议避开,但我不知道有任何替代方法可以让您捕获新的插入的标识值和 non-inserted ChildIds 列,据我所知 none 这些错误会影响此操作(有趣的是,我从未遇到过使用此方法的问题).