是否可以在不使用游标的情况下在集合上执行存储过程?

时间:2009-01-25 02:24:38

标签: sql sql-server tsql stored-procedures

我想在一个集合中的每一行上执行一个存储过程而不使用这样的游标:

SELECT EXEC dbo.Sproc @Param1 = Table1.id
FROM Table1


我在SQL Server 2005中使用T-SQL。我认为这可能是使用函数,但我想尽可能使用存储过程(公司标准)

7 个答案:

答案 0 :(得分:17)

是。如果您有一个列,您可以在表中使用以标记已处理的列,您可以使用WHILE EXISTS:

DECLARE @Id int
WHILE EXISTS(SELECT * FROM Table1 WHERE Processed='N')
BEGIN
 SELECT Top 1 @Id = id from Table1 WHERE Procesed='N'
 EXEC dbo.Sproc @Id
 UPDATE Table1 SET Processed = 'Y' WHERE Id = @Id
END

或者,将id转储到临时表或表变量中,并在完成后删除:

DECLARE @HoldTable table (Id int PRIMARY KEY)
DECLARE @Id int
INSERT INTO @HoldTable SELECT Id FROM Table1
WHILE EXISTS(SELECT * FROM @HoldTable)
BEGIN
 SELECT @Id = id from @HoldTable
 EXEC dbo.Sproc @Id
 DELETE FROM @HoldTable where Id = @Id
END

答案 1 :(得分:14)

如果您只使用SQL Server 2005或更新版本,请不要关心向后兼容性,并且可以将代码转换为用户定义函数(而不是存储过程)然后您可以使用新的“CROSS” APPLY“运算符,它使用与您想要的语法非常相似的语法。我发现here是一个简短的介绍(当然,你也可以阅读BOL和MSDN)

假设您的SP返回一个名为 out_int 的值,您的示例可能会被重写为:

SELECT T.id, UDF.out_int
FROM 
    Table1 T
CROSS APPLY
    dbo.fn_My_UDF(T.id) AS UDF

这将从Table1中检索每个“id”并使用它来调用fn_My_UDF,其结果将出现在原始参数之外的最终结果集中。

“CROSS APPLY”的变量是“外部应用”。它们等同于“INNER JOIN”和“LEFT JOIN”,但是可以加入表和UDF(同时调用第二个)。

如果你必须(通过尖头发老板的明确顺序)使用SPs insead,那么 - 运气不好!您必须使用游标,或尝试作弊:将代码更改为UDF,并创建包装SP:D。

答案 2 :(得分:3)

坦率地说,如果我需要为一组数据执行存储过程,而不是如果为其编写了一个记录,我会编写基于集合的代码而不是使用存储过程。如果要对大型数据集运行,这是执行此操作的唯一有效方法。您可以从几个小时开始执行存储过程执行的插入操作,达到几秒甚至几毫秒。当您需要处理一组数据时,请不要使用基于记录的处理,尤其是因为重用代码等愚蠢的原因。性能胜过代码重用。

答案 3 :(得分:1)

如果可能的话,我会写一个从临时表中读取的存储过程的第二个版本。

如果那不可能,那么你可能运气不好。

答案 4 :(得分:1)

使用动态SQL来看看这个answer。我将重写查询以适合您的需求。

-fx-text-inner-color: red;

如果您DECLARE @TSQL NVARCHAR(MAX) = '' SELECT @TSQL = @TSQL + N'EXEC dbo.Sproc ' + id + '; ' -- To insert a line break FROM Table1 EXEC sp_executesql @TSQL ,则等于以下内容。注意how the line break is inserted

PRINT @TSQL

答案 5 :(得分:0)

在没有光标或while循环的情况下,你可以做你想要的10次中的9次。但是,如果你必须使用一个,我发现while循环往往更快。

此外,如果您不想删除或更新表格,可以使用以下内容:

DECLARE @id [type]
SELECT @id = MIN([id]) FROM [table]
WHILE @id IS NOT NULL
BEGIN
    EXEC [sproc] @id
    SELECT @id = MIN([id]) FROM [table] WHERE [id] > @id
END

答案 6 :(得分:0)

这个循环有效,但我的SP速度很慢。我认为正确的答案是HLGEM,你最好的办法就是写一个更好的批量查询。

DECLARE @id INT
SET @id = 0

DECLARE @max INT
SELECT TOP 1 @max = TableID
FROM dbo.Table
ORDER BY TableID desc

-- loop until BREAK
-- this is how you can perform a DO-WHILE loop in TSQL
WHILE (1 = 1) 
BEGIN
    SELECT      
        TOP 1 @id = TableID
        FROM dbo.Table
        WHERE TableID > @id 

    IF @id IS NOT NULL
    BEGIN        
        -- call you SP here
        PRINT @id        
    END

    -- break out of the loop once the max id has been reached
    IF @id = @max BREAK 
END