MS SQL Server - 游标什么时候好?

发布于 2024-08-05 19:13:01 字数 178 浏览 5 评论 0原文

很多时候,当我编写存储过程等时,我首先使用游标,后来发现我的过程存在一些性能问题。

我读到的每一篇文章都说游标很糟糕,会导致不必要的锁定等,性能测试也证明了这一点。

我的问题是你什么时候使用游标以及它们在什么情况下有用或好的?

如果没有用,为什么他们要为 SQL 制定如此糟糕的控制结构/类型?

Many times when I've written stored procedures, etc. I use a CURSOR at first and later find some performance issue with my procedure.

Every thing I read says CURSORS are awful, cause unnecessary locking, etc. and performance testing proves the same.

My question is when do you use a CURSOR and in what situations are they useful or good?

If there is no use, why would they make such a bad control structure/type for SQL?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

分分钟 2024-08-12 19:13:01

通常情况下应该避免使用它们,但该功能的存在是有原因的,并且有时会使用它们。我想说,我见过的 90% 以上的光标都是不需要的。如果您将它们用于 CRUD 操作,那么几乎总是可以以基于集合的方式重做。我经常看到人们为此使用游标,因为他们不知道如何在更新或删除中使用联接,也不知道他们可以在插入中使用 select 语句而不是值子句。当人们认为他们需要它们进行稍微复杂的处理时,另一个不必要的用途实际上可以通过 case 语句轻松处理。

有时,游标对于计算诸如运行总计之类的内容更快。

游标对于多次执行存储过程也很方便,该存储过程被设置为一次仅处理一个输入值。我不使用此功能来运行用户存储过程(除非我知道我将访问非常小的数据集),但当需要针对多个表运行系统过程时,它对于数据库管理员来说非常方便。

如果您在 SQl 中创建电子邮件(不是最好的位置,但在某些系统中就是这样做的)并且不希望电子邮件的整个受众看到列表中的其他人,或者您想要对每个人进行个性化包含有关收件人信息的电子邮件,光标是最佳选择。

如果整个基于集的插入/更新/删除将花费太长时间并锁定表,则游标或循环也可用于处理批量记录。这是游标和基于集的解决方案之间的一种混合,通常是生产系统上发生较大变化的最佳解决方案。

Normally they are to be avoided, but the feature is there for a reason and there are times to use them. I'd say 90+% of the cursors I've seen are not needed. If you are using them for CRUD operations, that can almost always be redone in a set-based fashion. I've often seen people use cursors for this because they don't know how to use joins in an update or delete or that they can use a select statment instead of a values clause in an insert. Another unnecessary use when people think they need them for slightly more complex processing that actually could easily be handled with a case statement.

Cursors are sometimes faster for calculating something like a running total.

Cursors are also handy for multiple executions of a stored proc that is set up to handle only one input value at a time. I do not use this feature for running user stored procs (unless I know I will be hitting a very small set of data) but it is very handy for database admins when needing to run system procs against multiple tables.

If you are creating emails in SQl (not the best place to do it, but in some systems that's where they do it) and do not want the entire audience of the email to see the other people on the list or you want to personalize each email with information about the addressee, cursors are the way to go.

Cursors or loops can be used also to process batches of records if the entire set-based insert/update/delete will take too long and lock up the tables. This is a sort of a hybrid between the cursors and the set-based solution and is often the best one for large changes on production systems.

临走之时 2024-08-12 19:13:01

有一次我问 SQL Server 团队的一个人,如果你可以添加一项功能来让产品对每个人都更好,那会是什么?

他的回答是“添加?”呵呵,我想带走一只。如果你摆脱游标,你就会迫使世界各地的程序员开始以基于 SET 的方式思考问题,这将是你所见过的全球范围内数据库性能的最大提升。

然而,就我而言,我倾向于看到一种模式,似乎有很多程序编码人员使用游标,因为他们需要能够一次执行一个元素的操作,并且错过了旧时尚的 WHILE 循环概念。基本思想相同,没有光标开销。仍然不如基于 SET 的方法那么快/有效,但 90% 的情况下,当有人声称“我不能基于此集合进行操作,我必须使用游标”时,我可以让他们使用 while 循环来完成此操作。

I asked a guy on the SQL Server team one time, if you could add one feature that would make the product better for everyone what would it be?

His response was 'Add? Huh, I would take one away. If you get rid of cursors you force programmers all over the world to start thinking about things in a SET based way and that will be the biggest world wide increase in DB performance you will ever see.'

For my part however I tend to see a pattern, there seems to be a lot of procedural coders who use cursors because they need to be able to do an operation one element at a time and miss the old fashion WHILE loop concept. Same basic idea without the cursor overhead. Still not near as fast/effective as something SET based but 90% of the time when someone claims 'I cant do this set based, I have to use cursors' I can get them to do it with a while loop.

夕色琉璃 2024-08-12 19:13:01

这是一篇由一位相当固执己见的人撰写的文章,他给出了不使用游标的理由以及关于它们是如何形成的一些答案:一定有 15 种丢失光标的方法

Here's an article by a rather opinionated fellow, who gives reasoning for not using Cursors and some answers as to how they came to be: There Must be 15 Ways to Lose Your Cursors.

翻了热茶 2024-08-12 19:13:01

我正在研究的 SQL Server 2008 MCTS 准备手册建议在 T-SQL 中需要 CURSOR 的任何地方使用外部 CLR 代码,特别是现在 SQL Server 2008 支持自定义聚合函数。

5 年前,我与他们合作开发了广泛的报告功能,但我认为我现在无法为它们想出一个好的用例。 CLR 聚合和函数的执行方式与内置聚合函数类似。

The MCTS prep manual for SQL Server 2008 that I'm studying recommends using external CLR code anywhere that a CURSOR would be required in T-SQL, especially now that SQL Server 2008 supports custom aggregate functions.

5 years ago, I worked with them for extensive reporting features, but I don't think I could come up with a good use case for them now. CLR aggregates and functions perform similarly to built-in aggregate functions.

梦醒灬来后我 2024-08-12 19:13:01

只有当光标内所做的任何事情都必须一次完成一项,并且光标内所做的任何事情都需要很长时间以至于光标的开销变得微不足道时,我才会使用它们。

例如数据库备份、完整性检查、索引重建。简而言之,管理任务。

Only time I'll use them is when whatever is been done inside the cursor absolutely has to be done one item at a time and where whatever is been done inside the cursor takes so long that the overhead of the cursor fades into insignificance.

Eg database backups, integrity checks, index rebuilds. In short, admin tasks.

橘味果▽酱 2024-08-12 19:13:01

天哪,我怎么忘记了 Group By?我采用了您在下面看到的基于游标的查询,并将其替换为后面的查询。现在我得到一个结果集,因此在 php 中使用 sqlsrv_next_result() 没有问题。

DECLARE @thisday datetime;

DECLARE daycursor CURSOR FOR
SELECT DISTINCT DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as thisday
FROM computerusedata

OPEN daycursor;
FETCH NEXT FROM daycursor
INTO @thisday;
WHILE @@FETCH_STATUS = 0
    BEGIN
    select distinct left(ComputerName,5) as CompGroup,DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as day
    FROM computerusedata
    where DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) = @thisday
    order by CompGroup;
    FETCH NEXT FROM daycursor;
    END;
CLOSE daycursor;
DEALLOCATE daycursor;";


select DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as day,left(ComputerName,5) as CompGroup
from ComputerUseData
group by DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)),left(ComputerName,5)
order by day,CompGroup

OMG, how did I forget about Group By? I took the cursor based query you see below and replaced it with the one after it. Now I get a single result set so there are no issues with using sqlsrv_next_result() in php.

DECLARE @thisday datetime;

DECLARE daycursor CURSOR FOR
SELECT DISTINCT DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as thisday
FROM computerusedata

OPEN daycursor;
FETCH NEXT FROM daycursor
INTO @thisday;
WHILE @@FETCH_STATUS = 0
    BEGIN
    select distinct left(ComputerName,5) as CompGroup,DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as day
    FROM computerusedata
    where DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) = @thisday
    order by CompGroup;
    FETCH NEXT FROM daycursor;
    END;
CLOSE daycursor;
DEALLOCATE daycursor;";


select DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as day,left(ComputerName,5) as CompGroup
from ComputerUseData
group by DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)),left(ComputerName,5)
order by day,CompGroup
请别遗忘我 2024-08-12 19:13:01

我通常不使用游标,但当我使用游标时,它必须是我在本地运行的“一次性”查询或日常工作。您希望避免让生产代码调用像响应 Web 请求那样频繁调用的游标。

I don't typically use cursors but when I do, it must be a "one-off" query that I'm running locally or a daily job. You want to refrain from having production code call a cursor that would be invoked frequently like in response to a web request.

羁绊已千年 2024-08-12 19:13:01

当 1) 您需要做一些集合操作无法完成的事情,或者 2) 通过从应用程序层进行迭代调用来完成相同的工作没有意义时,游标很有用。或者有时您有一个过程必须保留在数据库层上,并且您根本无法中途返回到应用程序层来迭代某些结果集。

不过,我提出的一项建议是,人们使用游标变量而不是普通游标,因为这样可以避免围绕普通游标的游标分配/释放问题。对于普通游标,如果不释放它们,它们将持续存在,这可能是内存泄漏的根源。对于基于变量的游标(即 DECLARE @cursor CURSOR),情况并非如此。

最重要的是,如果可以的话,请避免使用它们;如果不能,请尽量少地、明智地使用它们。

Cursors are useful when 1) you need to do something that you cannot do with a set operation, or 2) it doesn't make sense to do the same work by making iterative calls from the application layer. Or sometimes you have a procedure that must remain on the database layer, and you simply can't break back out to the app layer midstream to iterate over some result set.

One recommendation I would make though, is that people use cursor variables rather than normal cursors, because you avoid the cursor allocation/deallocation issues that surround normal cursors. With a normal cursor, if you don't deallocate them they persist, which can be a source of memory leaks. Not so with variable-based cursors (i.e. DECLARE @cursor CURSOR).

The bottom line is, avoid them if you possibly can, and if you can't, use them minimally and wisely.

泡沫很甜 2024-08-12 19:13:01

您可以使用游标单独重建或重新组织表索引
除非有办法运行 ALTER INDEX... 作为基于集合的操作。

You use cursor for rebuilding or reorganizing table indexes individually
unless there is a way of running ALTER INDEX... as a set-based operation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文