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

问题描述 投票:0回答:9

我正在寻找一种方法来为 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

上面这样的事情当然是行不通的,但是有没有办法做到类似的事情呢?

sql sql-server t-sql
9个回答
79
投票

您需要使用光标。

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

29
投票

将 Id 放入临时表变量中,然后迭代每一行:(您不需要使用游标,这会慢得多)

   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 

EDIT 当与针对非常窄的唯一索引驱动的过滤谓词一起使用时,删除并不慢,就像这样。但它可以很容易地避免,只需按如下所示进行循环即可:

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    

21
投票

令人惊讶的是没有人给您最新的答案。游标是坏的。您想要的是将 SP 的逻辑移至 表值函数(TVF)中,然后使用

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

我可以使用

x
y
,就好像它们是从
FROM
JOIN
子句中拉入的表格一样。如果我必须在没有 TVF 的情况下编写此查询,它将跨越几百行。

注意:

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


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 

2
投票

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

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


1
投票

您需要使用游标:SQL Server 游标示例

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

0
投票

当可以进行集合处理时,真的需要逐行处理吗?

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


0
投票

标准光标解决方案是邪恶的。 两个相同的 FETCH NEXT 语句简直就是一场维护噩梦。

更好的是

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

邪恶有时也是正当的。 只需尝试设计一套基于方法来使用 sp_send_dbmail 或其他存储过程发送通知电子邮件。


0
投票
DECLARE @Query varchar(MAX);  
select @Query=STUFF((SELECT 'EXEC dbo.UpdateComputedFullText @SomeIds  =' +QUOTENAME(spro.SomeIds  ,'''')  + ';'    
        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
        FOR XML PATH('')),1,0,'');

EXEC(@Query);
© www.soinside.com 2019 - 2024. All rights reserved.