SQL 服务器:为什么在插入分块时此逻辑不起作用?
SQL Server: Why isn't this logic working when Chunking on inserts?
技术同仁--
我这里发生了无限循环情况。为什么@@rowcount 永远不会设置回 0?我一定没有理解 @@rowcount 的真正作用——或者我在错误的地方设置了值。我认为每次通过时该值都应该递减,直到我最终达到零。
DECLARE @ChunkSize int = 250000;
WHILE @ChunkSize <> 0
BEGIN
BEGIN TRANSACTION
INSERT TableName
(col1,col2)
SELECT TOP (@ChunkSize)
col1,col2
FROM TableName2
COMMIT TRANSACTION;
SET @ChunkSize = @@ROWCOUNT
END -- transaction block
END -- while-loop block
根据您发布的内容,我不确定您将如何确保捕获尚未插入的行。如果你不这样做,那当然是一个无限循环。这是一种使用测试数据的方法——但您自然希望它基于 PK 或其他独特的列。也许你只是把那部分遗漏了,或者我遗漏了一些东西。我只是对您的分块最终代码及其背后的逻辑感兴趣,所以这是一个答案和询问。
if object_id('tempdb..#source') is not null drop table #source
if object_id('tempdb..#destination') is not null drop table #destination
create table #source(c1 int, c2 int)
create table #destination (c1 int, c2 int)
insert into #source (c1,c2) values
(1,1),
(2,1),
(3,1),
(4,1),
(5,1),
(6,1),
(7,1),
(8,1),
(9,1),
(10,1),
(11,1),
(12,1)
DECLARE @ChunkSize int = 2;
WHILE @ChunkSize <> 0
BEGIN
INSERT INTO #destination (c1,c2)
SELECT TOP (@ChunkSize) c1,c2 FROM #source WHERE c1 NOT IN (SELECT DISTINCT c1 FROM #destination) ORDER BY ROW_NUMBER() OVER (ORDER BY C1)
SET @ChunkSize = @@ROWCOUNT
--SELECT @ChunkSize
END
select * from #source
select * from #destination
没有发生任何事情,因为您将 chunksize 设置为自身,而没有查看您已经插入的内容。使用您的示例,@Chunksize = 250000
。首先,select 执行 SELECT TOP 250000
和 returns(大概)250000 行。然后您使用 @@RowCount
更新 @Chunksize
,但返回的行数将为 250000,因此您只需再次将其设置为 250000。这可能没问题,除了如果不排除您已经插入的行,数字永远不会改变 - 您将一遍又一遍地插入相同的 250000 行。
您需要 NOT EXISTS
之类的东西来过滤掉您已经插入的行:
DECLARE @ChunkSize int = 250000;
WHILE @ChunkSize > 0
BEGIN
BEGIN TRANSACTION
INSERT INTO TableName
(col1,col2)
SELECT TOP (@ChunkSize)
col1,col2
FROM TableName2 T2
WHERE NOT EXISTS (SELECT *
FROM TableName T
WHERE T.Col1 = T2.Col1
AND T.Col2 = T2.Col2)
SET @ChunkSize = @@ROWCOUNT
PRINT CONVERT(nvarchar(10),@ChunkSize) + ' Rows Inserted.';
COMMIT TRANSACTION
END -- while-loop block
已实施解决方案
最后,我决定通过 SSIS 抽取 SQL,在那里我可以相应地设置提交批大小。如果我没有选择 hat route,我将不得不遵循 @scsimon 的建议并基本上保持跟踪 table 完成的记录和剩下的记录循环。
技术同仁--
我这里发生了无限循环情况。为什么@@rowcount 永远不会设置回 0?我一定没有理解 @@rowcount 的真正作用——或者我在错误的地方设置了值。我认为每次通过时该值都应该递减,直到我最终达到零。
DECLARE @ChunkSize int = 250000;
WHILE @ChunkSize <> 0
BEGIN
BEGIN TRANSACTION
INSERT TableName
(col1,col2)
SELECT TOP (@ChunkSize)
col1,col2
FROM TableName2
COMMIT TRANSACTION;
SET @ChunkSize = @@ROWCOUNT
END -- transaction block
END -- while-loop block
根据您发布的内容,我不确定您将如何确保捕获尚未插入的行。如果你不这样做,那当然是一个无限循环。这是一种使用测试数据的方法——但您自然希望它基于 PK 或其他独特的列。也许你只是把那部分遗漏了,或者我遗漏了一些东西。我只是对您的分块最终代码及其背后的逻辑感兴趣,所以这是一个答案和询问。
if object_id('tempdb..#source') is not null drop table #source
if object_id('tempdb..#destination') is not null drop table #destination
create table #source(c1 int, c2 int)
create table #destination (c1 int, c2 int)
insert into #source (c1,c2) values
(1,1),
(2,1),
(3,1),
(4,1),
(5,1),
(6,1),
(7,1),
(8,1),
(9,1),
(10,1),
(11,1),
(12,1)
DECLARE @ChunkSize int = 2;
WHILE @ChunkSize <> 0
BEGIN
INSERT INTO #destination (c1,c2)
SELECT TOP (@ChunkSize) c1,c2 FROM #source WHERE c1 NOT IN (SELECT DISTINCT c1 FROM #destination) ORDER BY ROW_NUMBER() OVER (ORDER BY C1)
SET @ChunkSize = @@ROWCOUNT
--SELECT @ChunkSize
END
select * from #source
select * from #destination
没有发生任何事情,因为您将 chunksize 设置为自身,而没有查看您已经插入的内容。使用您的示例,@Chunksize = 250000
。首先,select 执行 SELECT TOP 250000
和 returns(大概)250000 行。然后您使用 @@RowCount
更新 @Chunksize
,但返回的行数将为 250000,因此您只需再次将其设置为 250000。这可能没问题,除了如果不排除您已经插入的行,数字永远不会改变 - 您将一遍又一遍地插入相同的 250000 行。
您需要 NOT EXISTS
之类的东西来过滤掉您已经插入的行:
DECLARE @ChunkSize int = 250000;
WHILE @ChunkSize > 0
BEGIN
BEGIN TRANSACTION
INSERT INTO TableName
(col1,col2)
SELECT TOP (@ChunkSize)
col1,col2
FROM TableName2 T2
WHERE NOT EXISTS (SELECT *
FROM TableName T
WHERE T.Col1 = T2.Col1
AND T.Col2 = T2.Col2)
SET @ChunkSize = @@ROWCOUNT
PRINT CONVERT(nvarchar(10),@ChunkSize) + ' Rows Inserted.';
COMMIT TRANSACTION
END -- while-loop block
已实施解决方案
最后,我决定通过 SSIS 抽取 SQL,在那里我可以相应地设置提交批大小。如果我没有选择 hat route,我将不得不遵循 @scsimon 的建议并基本上保持跟踪 table 完成的记录和剩下的记录循环。