Sql 为什么基于关系集的查询比游标更好?

Sql 为什么基于关系集的查询比游标更好?,sql,language-agnostic,cursor,Sql,Language Agnostic,Cursor,在用TSQL或PLSQL之类的语言编写数据库查询时,我们通常可以选择使用游标对行进行迭代以完成任务,或者编写一条SQL语句同时执行相同的任务 此外,我们还可以选择简单地将大量数据拉回到应用程序中,然后使用C#或Java或PHP或其他工具逐行处理 为什么使用基于集合的查询更好?这一选择背后的理论是什么?什么是基于光标的解决方案及其对应关系的好例子?我知道的主要原因是,引擎可以通过跨多个线程运行基于集合的操作来优化它们。例如,设想一个快速排序-您可以将正在排序的列表分成多个“块”,并在各自的线程中分

在用TSQL或PLSQL之类的语言编写数据库查询时,我们通常可以选择使用游标对行进行迭代以完成任务,或者编写一条SQL语句同时执行相同的任务

此外,我们还可以选择简单地将大量数据拉回到应用程序中,然后使用C#或Java或PHP或其他工具逐行处理


为什么使用基于集合的查询更好?这一选择背后的理论是什么?什么是基于光标的解决方案及其对应关系的好例子?

我知道的主要原因是,引擎可以通过跨多个线程运行基于集合的操作来优化它们。例如,设想一个快速排序-您可以将正在排序的列表分成多个“块”,并在各自的线程中分别对每个块进行排序。SQL引擎可以在一个基于集合的查询中对大量数据执行类似的操作


当您执行基于光标的操作时,引擎只能按顺序运行,并且操作必须是单线程的。

首选在查询中执行工作的想法是,数据库引擎可以通过重新格式化来进行优化。这也是为什么您希望在查询中运行EXPLAIN,以查看数据库实际上在做什么。(例如,利用索引、表格大小,有时甚至利用关于列中值分布的知识。)

这就是说,要在实际的具体案例中取得良好的表现,您可能必须遵守或违反规则


哦,另一个原因可能是约束:如果在所有更新之后都检查了约束,则将唯一列的值增加一个可能没问题,但如果逐个执行,则会产生冲突。

基于集合的操作只需一次操作即可完成 游标与基于游标集的查询的行集一样多的操作(通常)更快,因为:

  • 它们有更多信息供查询优化器优化
  • 它们可以从磁盘进行批读取
  • 回滚、事务日志等所涉及的日志记录较少
  • 使用更少的锁,从而减少开销
  • 基于集合的逻辑是RDBMS的重点,因此它们已经为此进行了大量优化(通常以牺牲过程性能为代价)
  • 不过,将数据拉到中间层进行处理可能会很有用,因为它消除了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
    

    我认为真正的答案是,就像编程中的所有方法一样,这取决于哪种方法更好。一般来说,基于集合的语言将更加高效,因为这就是它设计的目的。光标有两个优势位置:

  • 您正在更新数据库中的一个大数据集,其中不允许锁定行(可能在生产时间内)。基于集合的更新可能会将表锁定几秒钟(或几分钟),而光标(如果写入正确)则无法锁定。光标可以在一行中来回移动,一次更新一行,您不必担心影响其他任何内容

  • 使用SQL的优点是,在大多数情况下,大部分优化工作由数据库引擎处理。有了企业级数据库引擎,设计师们付出了艰辛的努力来确保系统在处理数据方面是高效的。缺点是SQL是一种基于集合的语言。您必须能够定义一组数据才能使用它。虽然这听起来很容易,但在某些情况下并非如此。查询可能非常复杂,引擎中的内部优化器无法有效地创建执行路径,猜猜会发生什么。。。具有32个处理器的超级强大的box使用一个线程执行查询,因为它不知道如何执行其他操作,因此在数据库服务器上浪费处理器时间,而数据库服务器通常只有一个,而不是多个应用服务器(回到原因1,您遇到了与需要在数据库服务器上运行的其他东西的资源冲突)。使用基于行的语言(C#、PHP、JAVA等),您可以更好地控制发生了什么。您可以检索数据集,并强制它按您希望的方式执行。(将数据集分离,以便在多个线程上运行等)。在大多数情况下,在数据库引擎上运行它的效率仍然不高,因为它仍然需要访问引擎来更新行,但是当您必须执行1000多次计算来更新行(假设您有一百万行)时,数据库服务器可能会开始出现问题

  • 除了上面提到的“让DBMS完成工作”(这是一个很好的解决方案)之外,还有两个很好的理由将查询留在DBMS中:

    • 它(主观上)更易于阅读。在以后查看代码时,您是希望尝试用循环和其他东西解析复杂的存储过程(或客户端代码),还是希望查看简洁的SQL语句
    • 它避免了网络往返。为什么要将所有数据推送到客户端,然后再推回更多数据?如果不需要,为什么要重击网络
    • 这是浪费。您的DBMS和应用程序服务器需要缓冲部分/全部数据才能使用。如果您没有无限内存,您可能会调出其他数据;为什么要将可能重要的内容从内存中调出,以缓冲一个几乎无用的结果集
    • 你为什么不呢?