我正在寻找一种方法来为 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
上面这样的事情当然是行不通的,但是有没有办法做到类似的事情呢?
您需要使用光标。
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
将 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
令人惊讶的是没有人给您最新的答案。游标是坏的。您想要的是将 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 服务器构造有警告——所以除非有人另有说法,否则我认为情况就是这样。
尝试一下没有光标的这个
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
RE 光标上面的两个答案都是正确的。但是,根据游标内运行的代码的复杂性,您最好将其放入您选择的语言中,并在将结果放入数据库之前在代码中执行计算。
我发现自己回过头来检查了很多游标操作,并且在许多情况下,出于性能原因将这些操作转换为代码。
您需要使用游标: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
当可以进行集合处理时,真的需要逐行处理吗?
您可以将 SELECT 的结果放入临时表中,然后调用 proc 对临时表的内容执行批量 SQL。临时表将可用于基于 T-SQL 作用域规则的被调用过程。
标准光标解决方案是邪恶的。 两个相同的 FETCH NEXT 语句简直就是一场维护噩梦。
更好的是
...declare cursor etc.
While 1=1
Fetch ...
if @@FETCH_STATUS <> 0 BREAK
...
End -- While
..Close cursor etc.
邪恶有时也是正当的。 只需尝试设计一套基于方法来使用 sp_send_dbmail 或其他存储过程发送通知电子邮件。
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);