将 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
我正在努力遵循循环中的一些逻辑,但我认为前提是
- 插入 WorkFlowInstanceRole
- 捕获插入的记录然后在此基础上进一步插入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 这些错误会影响此操作(有趣的是,我从未遇到过使用此方法的问题).
我在存储过程中同时插入父记录和子记录。
我没有让外部代码进行嵌套调用来创建每个父项,然后创建该父项的每个子项(这比我目前的方法还要慢),我给 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
我正在努力遵循循环中的一些逻辑,但我认为前提是
- 插入 WorkFlowInstanceRole
- 捕获插入的记录然后在此基础上进一步插入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 这些错误会影响此操作(有趣的是,我从未遇到过使用此方法的问题).