SQL - 为每条记录调用存储过程

时间:2010-01-16 15:55:39

标签: sql sql-server tsql

我正在寻找一种为select语句的每条记录调用存储过程的方法。

SELECT @SomeIds = (
    SELECT spro.Id 
    FROM SomeTable as spro
    INNER JOIN [Address] addr ON addr.Id = spro.Id 
    INNER JOIN City cty ON cty.CityId = addr.CityId
    WHERE cty.CityId = @CityId
)


WHILE @SomeIds  IS NOT NULL
BEGIN
    EXEC UpdateComputedFullText @SomeIds
END

上面这样的事情当然不起作用,但是有办法做那样的事吗?

8 个答案:

答案 0 :(得分:65)

你需要使用光标。

DECLARE @oneid int -- or the appropriate type

DECLARE the_cursor CURSOR FAST_FORWARD
FOR SELECT spro.Id  
    FROM SomeTable as spro 
        INNER JOIN [Address] addr ON addr.Id = spro.Id  
        INNER JOIN City cty ON cty.CityId = addr.CityId 
    WHERE cty.CityId = @CityId

OPEN the_cursor
FETCH NEXT FROM the_cursor INTO @oneid

WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC UpdateComputedFullText @oneid

    FETCH NEXT FROM the_cursor INTO @oneid
END

CLOSE the_cursor
DEALLOCATE the_cursor

答案 1 :(得分:21)

惊讶没有人给你一个最新的答案。游标很糟糕。你想要的是将SP的逻辑移动到table-valued-function(TVF) and then use CROSS APPLY

这是我昨天写的一个查询(不要详述细节,只看CROSS APPLY)。 CROSS APPLY创建表的联合。此联合的每个元素都是从TVF生成的,TVF在select语句的行条目上进行参数化。

SELECT supt.hostname,supt.scriptname, COUNT(*)
FROM Event_Pagehit eph
    INNER JOIN Symboltable_urlpair supf
    ON eph.fromPagePair=supf.id
    INNER JOIN Symboltable_urlpair supt
    ON supt.id=eph.toPagePair
CROSS APPLY dbo.TDFCompanyFormationsUrlClassification(supf.hostname,supf.scriptname) as x
CROSS APPLY dbo.TDFCompanyFormationsUrlClassification(supt.hostname,supt.scriptname) as y
WHERE x.isCompanyFormations=1
AND y.isCompanyFormations=0
GROUP BY supt.hostname,supt.scriptname
ORDER BY COUNT(*) desc

我可以使用xy,就好像它们是从FROMJOIN条款中提取的表格一样。如果我不用TVF写这个查询,它将跨越几百行。

注意:

如果无法重写SP:您应该能够从表值函数将结果表中插入存储过程的结果。我从来没有这样做,有时不同的SQL服务器构造有警告 - 所以除非有人说不然我认为是这种情况。

答案 2 :(得分:18)

将Ids放入Temporary表变量,然后遍历每一行:(您不需要使用速度相当慢的游标)

   Declare @Keys Table (key integer Primary Key Not Null)
   Insert @Keys(key)
   SELECT spro.Id  
   FROM SomeTable as spro 
       JOIN [Address] addr ON addr.Id = spro.Id  
       JOIN City cty ON cty.CityId = addr.CityId 
   WHERE cty.CityId = @CityId
   -- -------------------------------------------
   Declare @Key Integer
   While Exists (Select * From @Keys)
     Begin
         Select @Key = Max(Key) From @Keys
         EXEC UpdateComputedFullText @Key
         Delete @Keys Where Key = @Key
     End 

编辑删除在与针对非常窄的唯一索引驱动的过滤谓词一起使用时并不慢,因为这是。但它可以很容易地避免,只需循环如下:

Declare @Key Integer = 0
While Exists (Select * From @Keys
              Where key > @Key)
 Begin
     Select @Key = Min(Key) From @Keys
                   Where key > @Key
     EXEC UpdateComputedFullText @Key
     -- Delete @Keys Where Key = @Key No Longer necessary 
 End    

答案 3 :(得分:5)

尝试没有光标的

DECLARE @id int 

SELECT top 1 @id = spro.Id   
    FROM SomeTable as spro  
        INNER JOIN [Address] addr ON addr.Id = spro.Id   
        INNER JOIN City cty ON cty.CityId = addr.CityId  
    WHERE cty.CityId = @CityId
    ORDER BY spro.id

WHILE @@ROWCOUNT > 0 
BEGIN 
    EXEC UpdateComputedFullText @id 

    SELECT top 1 @id = spro.Id   
    FROM SomeTable as spro  
        INNER JOIN [Address] addr ON addr.Id = spro.Id   
        INNER JOIN City cty ON cty.CityId = addr.CityId  
    WHERE cty.CityId = @CityId 
    and spro.id > @id
    ORDER BY spro.id
END 

答案 4 :(得分:2)

RE游标上面的两个答案都是正确的。但是,根据游标内部运行的代码的复杂性,在将结果放入数据库之前,最好将其放入您选择的语言并在代码中执行计算。

我发现自己回过头来审查很多游标操作,并且在很多情况下,出于性能原因将这些操作转换为代码。

答案 5 :(得分:1)

您需要使用光标:SQL Server Cursor Examples

DECLARE @id int
DECLARE cursor_sample CURSOR FOR  
SELECT spro.Id 
FROM SomeTable as spro
    INNER JOIN [Address] addr ON addr.Id = spro.Id 
    INNER JOIN City cty ON cty.CityId = addr.CityId
WHERE cty.CityId = @CityId

OPEN cursor_sample
FETCH NEXT FROM cursor_sample INTO @id 
WHILE @@FETCH_STATUS = 0   
BEGIN  
    EXEC UpdateComputedFullText @id
    FETCH NEXT FROM cursor_sample INTO @id
END   

CLOSE cursor_sample
DEALLOCATE cursor_sample

答案 6 :(得分:0)

当设置处理可用时,您是否真的需要逐行处理?

您可以将SELECT的结果放入临时表中,然后调用proc以对临时表的内容执行批量SQL。临时表将基于T-SQL作用域规则可用于被调用的proc。

答案 7 :(得分:0)

标准的游标解决方案对邪恶是邪恶的。 两个相同的FETCH NEXT语句只是维护的噩梦。

更好的是

...declare cursor etc.
While 1=1
 Fetch ...
 if @@FETCH_STATUS <> 0  BREAK
...
End -- While 
..Close cursor etc.

邪恶有时是有道理的。 只是尝试设计一种基于集合的方法来使用sp_send_dbmail或其他存储过程发送通知电子邮件。