不使用游标为每行调用 SQL 存储过程
如何在不使用游标的情况下为表中的每一行调用存储过程,其中行的列是 sp 的输入参数?
How can one call a stored procedure for each row in a table, where the columns of a row are input parameters to the sp without using a Cursor?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(17)
一般来说,我总是寻找基于集合的方法(有时以更改架构为代价)。
然而,这个片段确实有它的位置..
Generally speaking I always look for a set based approach (sometimes at the expense of changing the schema).
However, this snippet does have its place..
您可以执行以下操作:按 CustomerID 等对表进行排序(使用 AdventureWorks
Sales.Customer
示例表),并使用 WHILE 循环迭代这些客户:只要满足以下条件,就应该适用于任何表:您可以在某些列上定义某种
ORDER BY
。You could do something like this: order your table by e.g. CustomerID (using the AdventureWorks
Sales.Customer
sample table), and iterate over those customers using a WHILE loop:That should work with any table as long as you can define some kind of an
ORDER BY
on some column.好吧,所以我永远不会将这样的代码投入生产,但它确实满足您的要求。
Ok, so I would never put such code into production, but it does satisfy your requirements.
我会使用已接受的答案,但另一种可能性是使用表变量来保存一组编号的值(在本例中只是表的 ID 字段),并按行号循环遍历这些值并与表连接以检索循环内操作所需的任何内容。
I'd use the accepted answer, but another possibility is to use a table variable to hold a numbered set of values (in this case just the ID field of a table) and loop through those by Row Number with a JOIN to the table to retrieve whatever you need for the action within the loop.
马克的回答很好(如果我能弄清楚如何做的话,我会对此发表评论!)
只是想我会指出,更改循环可能会更好,这样
SELECT
只存在一次(在我需要这样做的实际情况下,SELECT
相当复杂,并且编写两次是一个有风险的维护问题)。Marc's answer is good (I'd comment on it if I could work out how to!)
Just thought I'd point out that it may be better to change the loop so the
SELECT
only exists once (in a real case where I needed to do this, theSELECT
was quite complex, and writing it twice was a risky maintenance issue).如果可以将存储过程变成返回表的函数,那么就可以使用交叉应用。
例如,假设您有一个客户表,并且您想要计算其订单的总和,您将创建一个接受 CustomerID 并返回总和的函数。
您可以这样做:
函数如下所示:
显然,上面的示例可以在单个查询中无需用户定义的函数来完成。
缺点是函数非常有限——存储过程的许多功能在用户定义的函数中不可用,并且将存储过程转换为函数并不总是有效。
If you can turn the stored procedure into a function that returns a table, then you can use cross-apply.
For example, say you have a table of customers, and you want to compute the sum of their orders, you would create a function that took a CustomerID and returned the sum.
And you could do this:
Where the function would look like:
Obviously, the example above could be done without a user defined function in a single query.
The drawback is that functions are very limited - many of the features of a stored procedure are not available in a user-defined function, and converting a stored procedure to a function does not always work.
对于 SQL Server 2005 及以上版本,您可以使用 CROSS APPLY 执行此操作表值函数。
为了清楚起见,我指的是存储过程可以转换为表值函数的情况。
For SQL Server 2005 onwards, you can do this with CROSS APPLY and a table-valued function.
Just for clarity, I'm referring to those cases where the stored procedure can be converted into a table valued function.
这是已提供答案的变体,但性能应该更好,因为它不需要 ORDER BY、COUNT 或 MIN/MAX。这种方法的唯一缺点是您必须创建一个临时表来保存所有 Id(假设您的 CustomerID 列表中有间隙)。
也就是说,我同意@Mark Powell 的观点,尽管一般来说,基于集合的方法应该仍然更好。
This is a variation on the answers already provided, but should be better performing because it doesn't require ORDER BY, COUNT or MIN/MAX. The only disadvantage with this approach is that you have to create a temp table to hold all the Ids (the assumption is that you have gaps in your list of CustomerIDs).
That said, I agree with @Mark Powell though that, generally speaking, a set based approach should still be better.
这是上述 n3rds 解决方案的变体。不需要使用 ORDER BY 进行排序,因为使用了 MIN()。
请记住,CustomerID(或用于进度的任何其他数字列)必须具有唯一约束。此外,为了使其尽可能快,必须对 CustomerID 建立索引。
我对一些需要查看的 varchar 使用这种方法,首先将它们放入临时表中,为它们提供一个 ID。
This is a variation of n3rds solution above. No sorting by using ORDER BY is needed, as MIN() is used.
Remember that CustomerID (or whatever other numerical column you use for progress) must have a unique constraint. Furthermore, to make it as fast as possible CustomerID must be indexed on.
I use this approach on some varchars I need to look over, by putting them in a temporary table first, to give them an ID.
对此更好的解决方案是
这样您就可以获得干净的表格式输出。然而,如果对每一行运行 SP,每次迭代都会得到一个单独的查询结果,这很丑陋。
A better solution for this is to
This was you get a clean table-formatted output. While if you run SP for every row, you get a separate query result for each iteration which is ugly.
如果您不知道如何使用游标,我认为您必须在外部执行此操作(获取表,然后运行每个语句并每次调用 sp)
它与使用游标相同,但仅限于 SQL 之外。
为什么不使用光标?
If you don't what to use a cursor I think you'll have to do it externally (get the table, and then run for each statement and each time call the sp)
it Is the same as using a cursor, but only outside SQL.
Why won't you use a cursor ?
当行数相当多时,我通常这样做:
(不过,在较大的数据集上,我会使用上面提到的解决方案之一)。
I usually do it this way when it's a quite a few rows:
(On larger datasets i'd use one of the solutions mentioned above though).
分隔符 //
DELIMITER //
我遇到过需要对结果集(表)执行一系列操作的情况。这些操作都是集合操作,所以这不是问题,但是......
我需要在多个地方执行此操作。因此,将相关部分放入表类型中,然后使用每个结果集填充表变量,这样我就可以调用 sp 并在每次需要时重复操作。
虽然这没有解决他提出的确切问题,但它确实解决了如何在不使用游标的情况下对表的所有行执行操作。
@Johannes 没有深入了解他的动机,所以这可能对他有帮助,也可能没有帮助。
我的研究使我找到了这篇写得很好的文章,它作为我的解决方案的基础
https://codingsight.com/passing-data-table -as-parameter-to-stored-procedures/
这是设置,
这里是实现
I had a situation where I needed to perform a series of operations on a result set (table). The operations are all set operations, so its not an issue, but...
I needed to do this in multiple places. So putting the relevant pieces in a table type, then populating a table variable w/ each result set allows me to call the sp and repeat the operations each time i need to .
While this does not address the exact question he asks, it does address how to perform an operation on all rows of a table without using a cursor.
@Johannes offers no insight into his motivation , so this may or may not help him.
my research led me to this well written article which served as a basis for my solution
https://codingsight.com/passing-data-table-as-parameter-to-stored-procedures/
Here is the setup
here is the implementation
如果订单很重要
In case the order is important
我有一些生产代码,一次只能处理 20 名员工,下面是代码的框架。我刚刚复制了生产代码并删除了下面的内容。
I had some production code that could only handle 20 employees at a time, below is the framework for the code. I just copied the production code and removed stuff below.
我喜欢做与此类似的事情(尽管它仍然与使用光标非常相似)
[code]
[/code]
请注意,您不需要临时表上的标识或 isIterated 列/变量表,我只是更喜欢这样做,这样我就不必在迭代循环时从集合中删除顶部记录。
I like to do something similar to this (though it is still very similar to using a cursor)
[code]
[/code]
Note that you don't need the identity or the isIterated column on your temp/variable table, i just prefer to do it this way so i don't have to delete the top record from the collection as i iterate through the loop.