我有一个以某种方式改变用户数据的存储过程。我传递user_id给它,它做它的事情。我想在一个表上运行一个查询,然后对于我找到的每个user_id运行存储过程一次对该user_id
我怎么写查询这个?
我有一个以某种方式改变用户数据的存储过程。我传递user_id给它,它做它的事情。我想在一个表上运行一个查询,然后对于我找到的每个user_id运行存储过程一次对该user_id
我怎么写查询这个?
使用游标
补遗:[MS SQL游标示例]
declare @field1 int
declare @field2 int
declare cur CURSOR LOCAL for
select field1, field2 from sometable where someotherfield is null
open cur
fetch next from cur into @field1, @field2
while @@FETCH_STATUS = 0 BEGIN
--execute your sproc on each row
exec uspYourSproc @field1, @field2
fetch next from cur into @field1, @field2
END
close cur
deallocate cur
在MS SQL中,这是一个示例文章
注意,游标比基于集的操作慢,但比手动while循环快;更多关于这个SO问题的细节
附录2:如果你要处理不止几条记录,首先把它们拉到临时表中,然后在临时表上运行光标;这将防止SQL升级为表锁并加快操作
附录3:当然,如果您可以内联存储过程对每个用户ID所做的事情,并将整个过程作为单个SQL更新语句运行,那将是最佳的
您的表和字段名将需要类似这样的替换。
Declare @TableUsers Table (User_ID, MyRowCount Int Identity(1,1)
Declare @i Int, @MaxI Int, @UserID nVarchar(50)
Insert into @TableUser
Select User_ID
From Users
Where (My Criteria)
Select @MaxI = @@RowCount, @i = 1
While @i <= @MaxI
Begin
Select @UserID = UserID from @TableUsers Where MyRowCount = @i
Exec prMyStoredProc @UserID
Select
@i = @i + 1, @UserID = null
End
如果你需要循环,试着改变你的方法!
在父存储过程中,创建一个包含需要处理的数据的#temp表。调用子存储过程,#temp表将是可见的,您可以处理它,希望能够处理整个数据集,而不需要游标或循环。
这实际上取决于这个子存储过程在做什么。如果您正在进行update -ing,则可以“更新”加入#temp表,并在一条语句中完成所有工作,而不需要循环。INSERT和delete也可以这样做。如果你需要使用If进行多次更新,你可以使用#temp表和CASE语句或WHERE条件将它们转换为多个UPDATE FROM。
当在数据库中工作时试图失去循环的心态,这是一个真正的性能消耗,会导致锁定/阻塞和减慢处理速度。如果你到处循环,你的系统将不能很好地扩展,并且当用户开始抱怨缓慢的刷新时将很难加速。
把你想调用的这个过程的内容放到一个循环中,我打赌十有八九,你可以把它写在一组行上。
这难道不能用用户定义的函数来复制存储过程所做的事情吗?
SELECT udfMyFunction(user_id), someOtherField, etc FROM MyTable WHERE WhateverCondition
其中,udfMyFunction是一个函数,它接受用户ID,并对它做任何你需要做的事情。
更多背景信息请参见http://www.sqlteam.com/article/user-defined-functions
我同意在可能的情况下应该避免使用游标。这通常是可能的!
(当然,我的回答假设您只对从SP获得输出感兴趣,并且不改变实际数据。我发现“以某种方式改变用户数据”与最初的问题有点模糊,所以我认为我可以提供这个作为可能的解决方案。完全取决于你在做什么!)
您可以使用动态查询来实现这一点。
declare @cadena varchar(max) = ''
select @cadena = @cadena + 'exec spAPI ' + ltrim(id) + ';'
from sysobjects;
exec(@cadena);
我喜欢Dave Rincon的动态查询方式,因为它不使用游标,而且小巧方便。谢谢Dave的分享。
但是对于我在Azure SQL上的需求,在查询中有一个“distinct”,我不得不像这样修改代码:
Declare @SQL nvarchar(max);
-- Set SQL Variable
-- Prepare exec command for each distinctive tenantid found in Machines
SELECT @SQL = (Select distinct 'exec dbo.sp_S2_Laser_to_cache ' +
convert(varchar(8),tenantid) + ';'
from Dim_Machine
where iscurrent = 1
FOR XML PATH(''))
--for debugging print the sql
print @SQL;
--execute the generated sql script
exec sp_executesql @SQL;
我希望这能帮助到一些人……
使用表变量或临时表。
如前所述,游标是最后的手段。主要是因为它使用了大量的资源,产生了锁问题,并且可能是你不理解如何正确使用SQL的标志。
旁注:我曾经遇到过一个使用游标进行更新的解决方案 表中的行。经过一番仔细检查,发现整件事 可以用一个UPDATE命令替换。然而,在这种情况下, 存储过程应该在哪里执行,单个sql命令 不能工作。
创建一个像这样的表变量(如果你处理大量数据或内存不足,可以使用临时表):
DECLARE @menus AS TABLE (
id INT IDENTITY(1,1),
parent NVARCHAR(128),
child NVARCHAR(128));
本我很重要。
用一些好的数据替换父节点和子节点,例如相关的标识符或整个要操作的数据集。
在表中插入数据,例如:
INSERT INTO @menus (parent, child)
VALUES ('Some name', 'Child name');
...
INSERT INTO @menus (parent,child)
VALUES ('Some other name', 'Some other child name');
声明一些变量:
DECLARE @id INT = 1;
DECLARE @parentName NVARCHAR(128);
DECLARE @childName NVARCHAR(128);
最后,在表中的数据上创建一个while循环:
WHILE @id IS NOT NULL
BEGIN
SELECT @parentName = parent,
@childName = child
FROM @menus WHERE id = @id;
EXEC myProcedure @parent=@parentName, @child=@childName;
SELECT @id = MIN(id) FROM @menus WHERE id > @id;
END
第一个选择从临时表中获取数据。第二个选择更新@id。如果没有选择行,MIN返回null。
另一种方法是在表有行时循环,SELECT TOP 1并从临时表中删除所选的行:
WHILE EXISTS(SELECT 1 FROM @menuIDs)
BEGIN
SELECT TOP 1 @menuID = menuID FROM @menuIDs;
EXEC myProcedure @menuID=@menuID;
DELETE FROM @menuIDs WHERE menuID = @menuID;
END;