Eri*_*ard 32 sql language-agnostic cursor
当在TSQL或PLSQL之类的东西中编写数据库查询时,我们通常可以选择使用游标迭代行来完成任务,或者制作一个同时执行相同作业的单个SQL语句.
此外,我们可以选择简单地将大量数据拉回到我们的应用程序中,然后使用C#或Java或PHP或其他任何方式逐行处理.
为什么使用基于集合的查询更好?这个选择背后的理论是什么?什么是基于游标的解决方案及其关系等效的一个很好的例子?
Mat*_*ton 18
我所知道的主要原因是引擎可以通过在多个线程上运行来优化基于集合的操作.例如,考虑一个快速排序 - 您可以将您要排序的列表分成多个"块",并在每个线程中单独排序.SQL引擎可以在一个基于集合的查询中使用大量数据执行类似的操作.
执行基于游标的操作时,引擎只能按顺序运行,操作必须是单线程的.
Mat*_*ish 16
除了上面的"让DBMS完成工作"(这是一个很好的解决方案)之外,还有其他一些很好的理由将查询留在DBMS中:
Mar*_*ett 15
基于集合的查询(通常)更快,因为:
然而,将数据拉出到中间层以处理它可能很有用,因为它消除了DB服务器的处理开销(这是最难扩展的事情,并且通常也在做其他事情).此外,您通常在中间层中没有相同的开销(或收益).诸如事务日志记录,内置锁定和阻塞等等 - 有时这些是必要且有用的,有时它们只是浪费资源.
一个简单的游标,带有程序逻辑和基于集合的示例(T-SQL),它将根据电话交换分配区号:
--Cursor
DECLARE @phoneNumber char(7)
DECLARE c CURSOR LOCAL FAST_FORWARD FOR
SELECT PhoneNumber FROM Customer WHERE AreaCode IS NULL
OPEN c
FETCH NEXT FROM c INTO @phoneNumber
WHILE @@FETCH_STATUS = 0 BEGIN
DECLARE @exchange char(3), @areaCode char(3)
SELECT @exchange = LEFT(@phoneNumber, 3)
SELECT @areaCode = AreaCode
FROM AreaCode_Exchange
WHERE Exchange = @exchange
IF @areaCode IS NOT NULL BEGIN
UPDATE Customer SET AreaCode = @areaCode
WHERE CURRENT OF c
END
FETCH NEXT FROM c INTO @phoneNumber
END
CLOSE c
DEALLOCATE c
END
--Set
UPDATE Customer SET
AreaCode = AreaCode_Exchange.AreaCode
FROM Customer
JOIN AreaCode_Exchange ON
LEFT(Customer.PhoneNumber, 3) = AreaCode_Exchange.Exchange
WHERE
Customer.AreaCode IS NULL
Run Code Online (Sandbox Code Playgroud)
你想要一些现实生活中的例子.我的公司有一个光标,花了40多分钟处理30,000条记录(有时我需要更新超过200,000条记录).没有光标需要45秒才能执行相同的任务.在另一种情况下,我删除了一个光标,并将处理时间从24小时发送到不到一分钟.一个是使用values子句而不是select的插入,另一个是使用变量而不是连接的更新.一个好的经验法则是,如果它是插入,更新或删除,您应该寻找一种基于集合的方式来执行任务.
游标有它们的用途(或者代码首先不是它们的代码),但是在查询关系数据库时它们应该是非常罕见的(除了经过优化以使用它们的Oracle).它们可以更快的一个地方是根据前面记录的值(运行总计)进行计算.甚至应该测试.
使用游标的另一个有限情况是进行一些批处理.如果您尝试以基于集合的方式一次性执行太多操作,则可以将表锁定到其他用户.如果你有一个真正大的集合,最好将它分解为较小的基于集合的插入,更新或删除,这些插入,更新或删除不会过长地保持锁定,然后使用游标运行集合.
游标的第三种用途是通过一组输入值运行系统存储过程.这仅限于一般的小集合,没有人应该搞乱系统过程,这对管理员来说是可以接受的.我建议不要对用户创建的存储过程执行相同的操作,以便处理大批量并重用代码.编写一个基于集合的版本会更好,因为在大多数情况下,性能应该胜过代码重用.