第一次 GO 后存储过程中的 Temp table 不可用
Temp table in stored procedure is unavailable after first GO
我有 140,000 个 INSERT 语句需要 运行。我正在尝试将记录插入数据库 table,然后将创建的 PK 输入临时 table,然后我 return。它看起来像这样:
CREATE PROCEDURE sp_MassInsertProcedure
@PassInVariable int
AS
CREATE TABLE #TempTable(OriginalID int IDENTITY(1,1), NewID int);
--These two inserts are essentially repeated 70,000 times
INSERT INTO MyDBTable (something, something, something) VALUES (1, 2, @PassInVariable);
INSERT INTO #TempTable(NewID) SELECT SCOPE_IDENTITY();
SELECT * FROM #TempTable;
DROP TABLE #TempTable;
我还有一些其他的存储过程,就像这样,最多有 2,000 个插入语句,这种方式非常有效,但我认为由于这个特定 SP 中的语句数量,它给了我 "Query Finished with Errors" 当我尝试创建过程,但实际上并没有创建过程。
然后我每 200 个插入语句添加一个 GO,但是在第一个 GO 执行后必须释放临时 table 并且我得到 "TempTable is unavailable" 错误(我也得到与 @ 相同的错误第一个 GO 执行后的 PassInVariable)。奇怪的是,当我将上面解释的 SQL 放在标准脚本(而不是存储过程)中时,它起作用了。
所以我的问题是,有谁知道如何通过 GO 命令在使用多个批处理执行的单个存储过程中保留临时 table/variable?
CREATE PROCEDURE sp_MassInsertProcedure
@PassInVariable int
AS
CREATE TABLE #TempTable(OriginalID int IDENTITY(1,1), NewID int);
--These inserts are essentially repeated 70,000 times
INSERT INTO MyDBTable (something, something, something) VALUES (1, 2, @PassInVariable);
INSERT INTO #TempTable(NewID) SELECT SCOPE_IDENTITY();
GO
INSERT INTO MyDBTable (something, something, something) (1, 2, @PassInVariable);
INSERT INTO #TempTable(NewID) SELECT SCOPE_IDENTITY();
SELECT * FROM #TempTable;
DROP TABLE #TempTable;
Ben,这是对 SQLServer 中 "GO" 的常见误解。
"GO" 不是 T-SQL 的一部分,它是 SQL 服务器工具(ssms、sqlcmd、sqlwb、isql 等)在不同语句中拆分脚本的指令。
所以你不能在 SP 和函数中使用它(即使在动态 SQL 中),只能在脚本中使用。
虽然我不明白为什么你的 SP 不能被创建,但是如果你想 运行 你的程序来自 Java,.NET,你需要添加
SET NOCOUNT ON
TempTable is unavailable
您收到此错误是因为本地临时 table 仅对创建它的连接可见,并在该连接关闭时被删除。在存储过程中使用 GO
结束批处理,从而结束存储过程。
一个解决方案是使用单个 INSERT
查询并使用 OUTPUT
子句来存储新插入的 IDs
:
INSERT INTO MyDBTable (something, something, something)
OUTPUT Inserted.ID INTO #TempTable(NewID)
VALUES (1, 2, @PassInVariable);
用 MyDBTable
中的正确 ID 替换 INSERTED.ID
中的 ID
要多次插入一行,可以使用Tally Table:
WITH E1(N) AS(
SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b),
Tally(N) AS(
SELECT TOP(70000)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E8
)
INSERT INTO MyDBTable(something, something, something)
OUTPUT Inserted.ID INTO #TempTable(NewID)
SELECT
1, 2, @PassInVariable
FROM Tally
将 TOP
替换为您要插入的行数。
MS SQL 中的 GO 语句释放资源并清理您的会话;这就是为什么 temp table 和你的变量都消失了。
在你的存储过程或至少上面的 SQL 脚本中,你不需要 go 语句。
你在别人的脚本中看到的GO语句是为了防止解析器在前面的语句出错后停止执行。它类似于 Visual Basic "On Error Resume Next" 语句。这样您的脚本将继续执行直到脚本文件结束。
您会看到GO语句主要用在包含多个交易的脚本文件中;每笔交易之后是一个 go 语句。例如,一个脚本文件包含针对不同存储过程的多个 CREATE 语句。但是在一个事务中,您不想使用 GO 语句,因为您将丢失所有变量(包括 temp table(s)),正如您在脚本中看到的那样。
不过我认为您的存储过程中没有必要。
临时 table 不可用的原因是存储过程是:
CREATE PROCEDURE sp_MassInsertProcedure
@PassInVariable int
AS
CREATE TABLE #TempTable(OriginalID int IDENTITY(1,1), NewID int);
--These inserts are essentially repeated 70,000 times
INSERT INTO MyDBTable (something, something, something) VALUES (1, 2, @PassInVariable);
INSERT INTO #TempTable(NewID) SELECT SCOPE_IDENTITY();
GO
由于其他答案中解释的原因,定义在 GO
处停止。
现在,一些有用的建议:
- 将存储过程的主体放在
BEGIN
/END
块或 BEGIN CATCH
/END TRY
块中。那么它的存在或结束就没有歧义了。
- 您不需要删除存储过程末尾的临时 table。它自动发生。不过,我承认,我个人更喜欢 table 变量,因为删除它们会更明显。
- 获取返回值的最佳方法是使用
OUTPUT
子句。 SCOPE_IDENTITY()
很安全。但 OUTPUT
更好,通常是推荐的方法。
感谢大家的回答...我最终得到以下解决方案:
我从调用我的存储过程的脚本创建了#TempTable
CREATE TABLE #TempTable (OriginalID int IDENTITY(1,1), NewID int);
EXEC sp_InsertMassRecords 777;
然后在我的存储过程中我有:
CREATE PROCEDURE sp_InsertMassRecords
@IncomingVariable int
AS
BEGIN
SET NOCOUNT ON;
INSERT MyDBTable (col1, col2, col3)
OUTPUT Inserted.ID INTO #TempTable(NewID)
SELECT 1, @IncomingVariable, 3 UNION ALL
SELECT 4, @IncomingVariable, 6 UNION ALL...
我在大约每 100 个 select 语句后重复了 INSERT/OUTPUT 行,整个过程 运行 非常快地成功了!
我有 140,000 个 INSERT 语句需要 运行。我正在尝试将记录插入数据库 table,然后将创建的 PK 输入临时 table,然后我 return。它看起来像这样:
CREATE PROCEDURE sp_MassInsertProcedure
@PassInVariable int
AS
CREATE TABLE #TempTable(OriginalID int IDENTITY(1,1), NewID int);
--These two inserts are essentially repeated 70,000 times
INSERT INTO MyDBTable (something, something, something) VALUES (1, 2, @PassInVariable);
INSERT INTO #TempTable(NewID) SELECT SCOPE_IDENTITY();
SELECT * FROM #TempTable;
DROP TABLE #TempTable;
我还有一些其他的存储过程,就像这样,最多有 2,000 个插入语句,这种方式非常有效,但我认为由于这个特定 SP 中的语句数量,它给了我 "Query Finished with Errors" 当我尝试创建过程,但实际上并没有创建过程。
然后我每 200 个插入语句添加一个 GO,但是在第一个 GO 执行后必须释放临时 table 并且我得到 "TempTable is unavailable" 错误(我也得到与 @ 相同的错误第一个 GO 执行后的 PassInVariable)。奇怪的是,当我将上面解释的 SQL 放在标准脚本(而不是存储过程)中时,它起作用了。
所以我的问题是,有谁知道如何通过 GO 命令在使用多个批处理执行的单个存储过程中保留临时 table/variable?
CREATE PROCEDURE sp_MassInsertProcedure
@PassInVariable int
AS
CREATE TABLE #TempTable(OriginalID int IDENTITY(1,1), NewID int);
--These inserts are essentially repeated 70,000 times
INSERT INTO MyDBTable (something, something, something) VALUES (1, 2, @PassInVariable);
INSERT INTO #TempTable(NewID) SELECT SCOPE_IDENTITY();
GO
INSERT INTO MyDBTable (something, something, something) (1, 2, @PassInVariable);
INSERT INTO #TempTable(NewID) SELECT SCOPE_IDENTITY();
SELECT * FROM #TempTable;
DROP TABLE #TempTable;
Ben,这是对 SQLServer 中 "GO" 的常见误解。 "GO" 不是 T-SQL 的一部分,它是 SQL 服务器工具(ssms、sqlcmd、sqlwb、isql 等)在不同语句中拆分脚本的指令。 所以你不能在 SP 和函数中使用它(即使在动态 SQL 中),只能在脚本中使用。
虽然我不明白为什么你的 SP 不能被创建,但是如果你想 运行 你的程序来自 Java,.NET,你需要添加
SET NOCOUNT ON
TempTable is unavailable
您收到此错误是因为本地临时 table 仅对创建它的连接可见,并在该连接关闭时被删除。在存储过程中使用 GO
结束批处理,从而结束存储过程。
一个解决方案是使用单个 INSERT
查询并使用 OUTPUT
子句来存储新插入的 IDs
:
INSERT INTO MyDBTable (something, something, something)
OUTPUT Inserted.ID INTO #TempTable(NewID)
VALUES (1, 2, @PassInVariable);
用 MyDBTable
INSERTED.ID
中的 ID
要多次插入一行,可以使用Tally Table:
WITH E1(N) AS(
SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b),
Tally(N) AS(
SELECT TOP(70000)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E8
)
INSERT INTO MyDBTable(something, something, something)
OUTPUT Inserted.ID INTO #TempTable(NewID)
SELECT
1, 2, @PassInVariable
FROM Tally
将 TOP
替换为您要插入的行数。
MS SQL 中的 GO 语句释放资源并清理您的会话;这就是为什么 temp table 和你的变量都消失了。
在你的存储过程或至少上面的 SQL 脚本中,你不需要 go 语句。
你在别人的脚本中看到的GO语句是为了防止解析器在前面的语句出错后停止执行。它类似于 Visual Basic "On Error Resume Next" 语句。这样您的脚本将继续执行直到脚本文件结束。
您会看到GO语句主要用在包含多个交易的脚本文件中;每笔交易之后是一个 go 语句。例如,一个脚本文件包含针对不同存储过程的多个 CREATE 语句。但是在一个事务中,您不想使用 GO 语句,因为您将丢失所有变量(包括 temp table(s)),正如您在脚本中看到的那样。
不过我认为您的存储过程中没有必要。
临时 table 不可用的原因是存储过程是:
CREATE PROCEDURE sp_MassInsertProcedure
@PassInVariable int
AS
CREATE TABLE #TempTable(OriginalID int IDENTITY(1,1), NewID int);
--These inserts are essentially repeated 70,000 times
INSERT INTO MyDBTable (something, something, something) VALUES (1, 2, @PassInVariable);
INSERT INTO #TempTable(NewID) SELECT SCOPE_IDENTITY();
GO
由于其他答案中解释的原因,定义在 GO
处停止。
现在,一些有用的建议:
- 将存储过程的主体放在
BEGIN
/END
块或BEGIN CATCH
/END TRY
块中。那么它的存在或结束就没有歧义了。 - 您不需要删除存储过程末尾的临时 table。它自动发生。不过,我承认,我个人更喜欢 table 变量,因为删除它们会更明显。
- 获取返回值的最佳方法是使用
OUTPUT
子句。SCOPE_IDENTITY()
很安全。但OUTPUT
更好,通常是推荐的方法。
感谢大家的回答...我最终得到以下解决方案:
我从调用我的存储过程的脚本创建了#TempTable
CREATE TABLE #TempTable (OriginalID int IDENTITY(1,1), NewID int);
EXEC sp_InsertMassRecords 777;
然后在我的存储过程中我有:
CREATE PROCEDURE sp_InsertMassRecords
@IncomingVariable int
AS
BEGIN
SET NOCOUNT ON;
INSERT MyDBTable (col1, col2, col3)
OUTPUT Inserted.ID INTO #TempTable(NewID)
SELECT 1, @IncomingVariable, 3 UNION ALL
SELECT 4, @IncomingVariable, 6 UNION ALL...
我在大约每 100 个 select 语句后重复了 INSERT/OUTPUT 行,整个过程 运行 非常快地成功了!