我正在寻找一种为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
上面这样的事情当然不起作用,但是有办法做那样的事吗?
答案 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
我可以使用x
和y
,就好像它们是从FROM
或JOIN
条款中提取的表格一样。如果我不用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或其他存储过程发送通知电子邮件。