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 ++ 来替换类似 Replace INSERTwith \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 1Strategy 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

【讨论】:

嘿,谢谢你的建议。我已经尝试过“返回”,它似乎没有退出脚本/停止执行脚本。还是我弄错了? 也许你的脚本中有一些GOGO 只是 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 块内的许多插入的主要内容,如果未能解决你的问题,请参考以下文章

redis之事务

将所有查询结果转储到 plsql 块内的文件中

详谈:Redis事务和消息订阅

描述块内的 jest.setTimeOut() 是不是仅将超时应用于描述块内的测试

4537: [Hnoi2016]最小公倍数|分块

05-redis事物