Answers:
尝试从存储过程链中“冒泡”数据时,这是一个常见问题。SQL Server中的一个限制是您一次只能激活一个INSERT-EXEC。我建议阅读“ 如何在存储过程之间共享数据”,这是一篇非常详尽的文章,介绍了解决此类问题的模式。
例如,一种变通方法是将Sp3转换为表值函数。
sp_help_jobactivity
这是在SQL Server中执行此操作的唯一“简单”方法,而无需一些复杂的复杂创建函数或已执行的sql字符串调用,这两种方法都是很糟糕的解决方案:
例:
INSERT INTO #YOUR_TEMP_TABLE
SELECT * FROM OPENROWSET ('SQLOLEDB','Server=(local);TRUSTED_CONNECTION=YES;','set fmtonly off EXEC [ServerName].dbo.[StoredProcedureName] 1,2,3')
注意:必须使用“ set fmtonly off”,并且不能在包含存储过程参数的字符串或表名的openrowset调用中向其中添加动态sql。这就是为什么您必须使用临时表而不是表变量的原因,这会更好,因为它在大多数情况下会执行临时表。
好的,在吉姆哈克(Jimhark)的鼓励下,这是旧的单个哈希表方法的示例:-
CREATE PROCEDURE SP3 as
BEGIN
SELECT 1, 'Data1'
UNION ALL
SELECT 2, 'Data2'
END
go
CREATE PROCEDURE SP2 as
BEGIN
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
INSERT INTO #tmp1
EXEC SP3
else
EXEC SP3
END
go
CREATE PROCEDURE SP1 as
BEGIN
EXEC SP2
END
GO
/*
--I want some data back from SP3
-- Just run the SP1
EXEC SP1
*/
/*
--I want some data back from SP3 into a table to do something useful
--Try run this - get an error - can't nest Execs
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
DROP TABLE #tmp1
CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))
INSERT INTO #tmp1
EXEC SP1
*/
/*
--I want some data back from SP3 into a table to do something useful
--However, if we run this single hash temp table it is in scope anyway so
--no need for the exec insert
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
DROP TABLE #tmp1
CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))
EXEC SP1
SELECT * FROM #tmp1
*/
这个技巧对我有用。
您在远程服务器上没有此问题,因为在远程服务器上,最后一个插入命令等待上一个命令的执行结果。在同一台服务器上不是这种情况。
解决这种情况,从中获利。
如果您具有创建链接服务器的权限,请执行此操作。创建与链接服务器相同的服务器。
现在您在SP1中的Sql命令是
insert into @myTempTable
exec THISSERVER.MY_DATABASE_NAME.MY_SCHEMA.SP2
相信我,即使您在SP2中具有动态插入功能,它也能正常工作
我发现一种解决方法是将其中一个产品转换为表值函数。我意识到这并不总是可能的,并介绍了其自身的局限性。但是,我始终能够找到至少一个程序来解决此问题。我喜欢这个解决方案,因为它不会给解决方案带来任何“麻烦”。
尝试将存储的Proc的结果导入到临时表中时,该存储的Proc作为其自身操作的一部分插入到临时表中时,遇到了此问题。问题在于SQL Server不允许同一进程同时写入两个不同的临时表。
接受的OPENROWSET答案工作正常,但是我需要避免在过程中使用任何Dynamic SQL或外部OLE提供程序,因此我采取了另一种方法。
我发现一个简单的解决方法是将存储过程中的临时表更改为表变量。它的工作原理与使用临时表完全相同,但不再与我的其他临时表插入冲突。
只是为了避免发表评论,我知道你们中的一些人将要写信,警告我不要将Table Variables视为性能杀手...我只能对您说的是,到2020年,它会带来红利,不要害怕Table Variables。如果是2008年,而我的数据库托管在具有16GB RAM并运行5400RPM HDD的服务器上,那么我可能会同意您的看法。但是到了2020年,我有了一个SSD阵列作为我的主要存储设备,并拥有数百个 RAM。我可以将整个公司的数据库加载到表变量中,但仍有大量RAM可用。
表变量又回到了菜单上!
只将输出存储到静态表怎么办?喜欢
-- SubProcedure: subProcedureName
---------------------------------
-- Save the value
DELETE lastValue_subProcedureName
INSERT INTO lastValue_subProcedureName (Value)
SELECT @Value
-- Return the value
SELECT @Value
-- Procedure
--------------------------------------------
-- get last value of subProcedureName
SELECT Value FROM lastValue_subProcedureName
它并不理想,但是它是如此简单,您无需重写所有内容。
更新:以前的解决方案不适用于并行查询(异步和多用户访问),因此现在使用临时表进行IAM
-- A local temporary table created in a stored procedure is dropped automatically when the stored procedure is finished.
-- The table can be referenced by any nested stored procedures executed by the stored procedure that created the table.
-- The table cannot be referenced by the process that called the stored procedure that created the table.
IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NULL
CREATE TABLE #lastValue_spGetData (Value INT)
-- trigger stored procedure with special silent parameter
EXEC dbo.spGetData 1 --silent mode parameter
嵌套的spGetData
存储过程内容
-- Save the output if temporary table exists.
IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NOT NULL
BEGIN
DELETE #lastValue_spGetData
INSERT INTO #lastValue_spGetData(Value)
SELECT Col1 FROM dbo.Table1
END
-- stored procedure return
IF @silentMode = 0
SELECT Col1 FROM dbo.Table1
向内部sp声明输出游标变量:
@c CURSOR VARYING OUTPUT
然后将游标c声明为要返回的选择。然后打开光标。然后设置参考:
DECLARE c CURSOR LOCAL FAST_FORWARD READ_ONLY FOR
SELECT ...
OPEN c
SET @c = c
请勿关闭或重新分配。
现在从外部提供一个光标参数的内部sp调用:
exec sp_abc a,b,c,, @cOUT OUTPUT
内部sp一旦执行, @cOUT
就可以获取了。循环,然后关闭并取消分配。
如果您能够使用其他关联技术(例如C#),建议您将内置SQL命令与Transaction参数一起使用。
var sqlCommand = new SqlCommand(commandText, null, transaction);
我创建了一个简单的控制台应用程序,该应用程序演示了此功能,可以在这里找到:https : //github.com/hecked12/SQL-Transaction-Using-C-Sharp
简而言之,C#允许您克服此限制,您可以检查每个存储过程的输出并根据需要使用该输出,例如,可以将其提供给另一个存储过程。如果输出正常,则可以提交事务,否则,可以使用回滚来还原更改。
在SQL Server 2008 R2上,表列不匹配,导致回滚错误。当我修复由insert-exec语句填充的sqlcmd表变量以匹配存储的proc返回的变量时,它消失了。它缺少org_code。在Windows cmd文件中,它将加载存储过程的结果并选择它。
set SQLTXT= declare @resets as table (org_id nvarchar(9), org_code char(4), ^
tin(char9), old_strt_dt char(10), strt_dt char(10)); ^
insert @resets exec rsp_reset; ^
select * from @resets;
sqlcmd -U user -P pass -d database -S server -Q "%SQLTXT%" -o "OrgReport.txt"