在没有游标的情况下,如何从另一个存储过程中迭代存储过程的结果?
我不确定这是否应该在T-SQL中执行,而且我很确定在这种情况下使用" iterate"一词是错误的,因为我们永远都不应在sql中进行任何迭代。应该是基于集合的运算,对吗?无论如何,这是场景:
我有一个存储的proc,它返回许多uniqueidentifiers(单列结果)。这些ID是另一个表中记录的主键。我需要在该表中的所有相应记录上设置一个标志。
在不使用游标的情况下该如何做?对我们来说,sql专家应该很容易!
解决方案
这可能不是最有效的方法,但是我将创建一个临时表来保存存储的proc的结果,然后在针对目标表的联接中使用它。例如:
CREATE TABLE #t (uniqueid int) INSERT INTO #t EXEC p_YourStoredProc UPDATE TargetTable SET a.FlagColumn = 1 FROM TargetTable a JOIN #t b ON a.uniqueid = b.uniqueid DROP TABLE #t
一个丑陋的解决方案是让过程在每次调用时都通过使用另一个表(或者现有表上的某些标志)返回"下一个" ID来过滤出它已经返回的行
将存储的proc的结果插入到临时表中,并将其加入到我们要更新的表中:
INSERT INTO #WorkTable EXEC usp_WorkResults UPDATE DataTable SET Flag = Whatever FROM DataTable INNER JOIN #WorkTable ON DataTable.Ket = #WorkTable.Key
使用临时表或者表变量(我们正在使用SS2005)。
虽然,如果存储的proc使用该方法是不可嵌套的,则我们不能将输出转储到临时表中。
我们可以将临时表或者表变量与其他列一起使用:
DECLARE @MyTable TABLE ( Column1 uniqueidentifer, ..., Checked bit ) INSERT INTO @MyTable SELECT [...], 0 FROM MyTable WHERE [...] DECLARE @Continue bit SET @Continue = 1 WHILE (@Continue) BEGIN SELECT @var1 = Column1, @var2 = Column2, ... FROM @MyTable WHERE Checked = 1 IF @var1 IS NULL SET @Continue = 0 ELSE BEGIN ... UPDATE @MyTable SET Checked = 1 WHERE Column1 = @var1 END END
编辑:实际上,在情况下,联接会更好;上面的代码是无游标的迭代,这对于情况而言是过大的选择。
如果我们升级到SQL 2008,则可以传递表参数。否则,我们将无法使用全局临时表或者创建一个永久表,该表包含用于某种进程ID的列,以标识对存储过程的哪个调用是相关的。
在更改生成ID的存储过程时,我们有多少空间?我们可以在其中添加代码来处理它,或者使用一个参数让我们在调用它时有选择地标记行。
我们还可以将存储的proc更改为用户定义的函数,该函数返回带有uniqueidentifiers的表。我们可以直接加入UDF并将其像表一样对待,从而避免了显式创建额外的临时表。另外,我们可以在调用函数时将参数传递给函数,这使它成为非常灵活的解决方案。
CREATE FUNCTION dbo.udfGetUniqueIDs () RETURNS TABLE AS RETURN ( SELECT uniqueid FROM dbo.SomeWhere ) GO UPDATE dbo.TargetTable SET a.FlagColumn = 1 FROM dbo.TargetTable a INNER JOIN dbo.udfGetUniqueIDs() b ON a.uniqueid = b.uniqueid
编辑:
这将适用于SQL Server 2000及更高版本。