在没有游标的情况下,如何从另一个存储过程中迭代存储过程的结果?

时间:2020-03-06 14:52:47  来源:igfitidea点击:

我不确定这是否应该在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及更高版本。