begin-end 块内的许多插入
Posted
技术标签:
【中文标题】begin-end 块内的许多插入【英文标题】:Many inserts inside a begin-end block 【发布时间】:2017-03-27 08:55:00 【问题描述】:我有一个包含超过 150k 行语句的 sql 脚本(其中大部分是插入语句)。如果表为空,我只想执行这些插入:
IF EXISTS (SELECT * FROM [MyTable])
BEGIN
PRINT 'No need to insert data'
--Stop executing script
END
ELSE
BEGIN
--INSERT #1
--...
--Insert #150000
END
问题:ELSE 块似乎太大了。我尝试使用GO
,但它在 BEGIN-END 块中不起作用。我该如何解决这个问题?
【问题讨论】:
BEGIN...END block in SQL Server的可能重复 我会将它分成两部分来解决:脚本的第一部分将简单地将您的数据插入临时表中。然后,如果 `[MyTable]` 为空,则从临时表中选择数据到目标表中。 好主意,谢谢!问题是,这将需要大量时间来修改所有脚本(目前太多时间)。但我会记住这一点,如果我没有找到任何其他方法:) 另一种选择是创建一个包含所有插入语句的存储过程,然后在 else 块中简单地执行它。 没有简单的方法来重构代码。您能做的最好的事情是使用 Notepad ++ 来替换类似 ReplaceINSERT
with \r\nEND\r\nIF EXISTS (SELECT * FROM Nonexistingtable)\r\nBEGIN\r\n PRINT 'No need to insert data'\r\n --Stop executing script\r\nEND\r\nELSE\r\nBEGIN\r\nINSERT
的字符串。请确保在替换选项卡中扩展搜索模式。您仍然需要手动将 Nonexistingtable 替换为正确的表。可以再次使用 Notepad++ 提供帮助,方法是搜索 Nonexistingtable 并单击查找选项卡下的“在文档中查找所有内容”。希望对你有帮助
【参考方案1】:
要对抗巨大的 else 块,您可以使用以下 3 种策略中的任何一种。
STRATEGY 1
使用 SQL Server 中的批量插入。只需将所有数据转储到 csv 文件中,然后在 else 块中使用以下语句。将 csv 文件存储在您的计算机上,并在下面的语句中在 FROM 之后给出它的 UNC 路径。这样,您的 else 块中将只有一行。
BULK INSERT dbo.MyTable
FROM '\\share\somepath\myTableInsertData.csv'
WITH (FORMAT = 'CSV');
STRATEGY 2
编写一个一次只插入x
行的存储过程。然后,您可以在 While loop
中调用此存储过程,并且您的 else 块将非常小。您将在原始 SQL 的 while 循环中重复调用此存储过程,然后 else 块将结束,只是几行 t-sql 代码。
请注意,您可以通过为变量@numberOfRowsAtaTime
使用适当的值来控制存储过程一次插入多少行。我已经使用了 1000 个,所以在一次存储过程调用中插入了 1000 行。
当然,根据您的业务规则,您可以编写存储过程的插入语句。如果您的 INSERTS 中有一个模式,那么您可以将该模式编写到下面的存储过程逻辑中。
存储过程
CREATE PROCEDURE dbo.insertXRows
@startIndex INT,
@numberOfRows INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @counter INT;
SET @counter = @startIndex;
WHILE @counter < (@startIndex + @numberOfRows -1)
BEGIN
-- Insert statements for rows goes here
--INSERT FOR @counter -- @counter would be different for each iteration
SET @counter = @counter + 1;
END
END
GO
你的 SQL
IF EXISTS (SELECT * FROM [MyTable])
BEGIN
PRINT 'No need to insert data'
--Stop executing script
END
ELSE
BEGIN
declare @insertRowCounter int;
set @insertRowCounter = 1;
declare @numberOfRowsAtaTime int;
set @numberOfRowsAtaTime = 1000;
WHILE @insertRowCounter <= 150000
EXEC dbo.insertXRows @insertRowCounter, @numberOfRowsAtaTime -- insert 1000 rows at a time
SET @insertRowCounter = @insertRowCounter + @numberOfRowsAtaTime;
END
END
STRATEGY 3
想出10个存储过程,这样每个存储过程就有15000个INSERTS。然后简单地从你的 else 块中调用这 10 个存储过程。
10 个存储过程
CREATE PROCEDURE dbo.insertProc1
AS
BEGIN
SET NOCOUNT ON;
--INSERT1
--INSERT2
--INSERT15000
END
END
GO
CREATE PROCEDURE dbo.insertProc2
AS
BEGIN
SET NOCOUNT ON;
--INSERT15001
--INSERT15002
--INSERT3000
END
END
GO
你的 SQL
IF EXISTS (SELECT * FROM [MyTable])
BEGIN
PRINT 'No need to insert data'
--Stop executing script
END
ELSE
BEGIN
EXEC insertProc1
EXEC insertProc2
EXEC insertProc3
EXEC insertProc4
EXEC insertProc5
EXEC insertProc6
EXEC insertProc7
EXEC insertProc8
EXEC insertProc9
EXEC insertProc10
END
【讨论】:
我不是 SQL 专家,很抱歉这个(愚蠢的)问题:我该怎么做:--INSERT FOR @counter -- @counter would be different for each iteration
?
你能用一个模式来表达你的 INSERT 吗?所以 INSERT1、INSERT2 等,在插入的值中遵循一个模式。如果有模式,那么我的意思是当计数器是 100 或 101 等值时,您需要为 INSERT 语句编写该逻辑。
你能写下前 10 个插入,以便我看看是否有模式吗?
那么你需要一个不同的策略,因为你没有模式。我建议编写多个存储过程,例如 10 个。每个过程将有 15000 次插入。第一个存储过程将具有 INSERT1、INSERT2、....INSERT15000。第二个存储过程将具有 INSERT15001,... INSERT30000。等等。然后只是 else 块中的存储过程,例如 EXEC insertProc1; EXEC insertProc2; EXEC insertProc3; EXEC insertProc4; EXEC insertProc5; EXEC insertProc6; EXEC insertProc7; EXEC insertProc8; EXEC insertProc9; EXEC insertProc10;
。
查看Strategy 1
或Strategy 3
。我认为Strategy 1
可能最适合你。【参考方案2】:
你可以使用NOEXEC
:
create table T (ID int not null)
go
if 1=1
begin
set noexec on
end
go
insert into T(ID) values (1),(2),(3)
--Repeat more inserts
go
set noexec off
go
select * from T
这只是打印一个空表 - 希望您能看到它如何适应您的脚本。
【讨论】:
【参考方案3】:你不能简单地在你的大脚本的开头添加这个块,没有else begin ... end
构造吗?
IF EXISTS (SELECT * FROM [MyTable])
BEGIN
PRINT 'No need to insert data'
--Stop executing script and return
RETURN
END
--Your old script starts here
【讨论】:
嘿,谢谢你的建议。我已经尝试过“返回”,它似乎没有退出脚本/停止执行脚本。还是我弄错了? 也许你的脚本中有一些GO
? GO
只是 SSMS 的批处理分隔符;它将通过这些拆分脚本并分别运行所有部分。【参考方案4】:
使用GOTO
语句跳出插入语句怎么样?
IF EXISTS (SELECT * FROM [MyTable])
BEGIN
GOTO NODATA
END
--INSERT #1
--...
--Insert #150000
NODATA:
PRINT 'No need to insert data'
【讨论】:
以上是关于begin-end 块内的许多插入的主要内容,如果未能解决你的问题,请参考以下文章