【问题标题】:What is wrong with Cursors?游标有什么问题?
【发布时间】:2010-10-19 02:17:21
【问题描述】:

SQL Server 开发人员认为游标是一种不好的做法,除非在某些情况下。他们认为,Cursors 没有以最佳方式使用 SQL 引擎,因为它是一个过程构造,并且违背了 RDBMS 的基于集合的概念。

但是,Oracle 开发人员似乎不建议反对 Cursors。 Oracle 的 DML 语句本身就是隐式游标。

为什么会有这种不同的方法?是因为这两种产品的制造方式,还是该建议适用于两种产品?

【问题讨论】:

  • 首先,您需要定义您所指的CURSOR。 Oracle 将这个词用于大约 7 种不同的事物。

标签: sql-server oracle data-structures database-cursor


【解决方案1】:

我相信有人可以更详细地解释一下,但基本上归结为 SQL server 中的游标很慢。

【讨论】:

    【解决方案2】:

    来自MSDN:Cursor Implementations

    使用游标的效率低于 使用默认结果集。在一个 默认结果集唯一的数据包 从客户端发送到服务器的是 包含语句的数据包 执行。使用服务器游标时, 每个 FETCH 语句必须从 客户端到服务器,它在哪里 必须被解析并编译成 执行计划。

    如果 Transact-SQL 语句将 返回一个相对较小的结果集 可以缓存在内存中的 可用于客户端应用程序, 你知道在执行之前 您必须检索的声明 整个结果集,使用默认值 结果集。仅使用服务器游标 当需要游标操作时 支持的功能 应用程序,或者当只有一部分 结果集很可能被检索到。

    我不是 Oracle DBA,所以我无法真正说明实现有何不同。但是,从编程的角度来看,基于集合的操作几乎总是比在游标中处理结果要快。

    【讨论】:

      【解决方案3】:

      我一直被告知游标是邪恶的,但 MS SQL Server 专家总是告诉我,因为它的性能很差。关于Oracle的PL/SQLI found this saying when to use cursors

      不使用游标会导致重复解析。如果不使用绑定变量,则对所有 SQL 语句进行硬解析。这对性能有一个数量级的影响,而且是完全不可扩展的。将游标与打开游标并多次执行的绑定变量一起使用。警惕生成动态 SQL 的应用程序。

      作为cursors are implicitly createdon every operation,在需要时使用它们似乎并没有那么惩罚性能:)

      请记住,Oracle 的实现更接近 Postgres 而不是 Sybase(MS SQL Server 的起源),因此不同任务的性能会有所不同。如果可以,请避免在可以交换的系统上进行性能调整后端,如果您需要同时使用两者,请选择最小公分母。 /tangential_topic

      【讨论】:

        【解决方案4】:

        游标的问题在于它们经常被滥用,无论是在Oracle 还是MS SQL

        光标用于保持稳定的结果集,您可以逐行检索。它们在您的查询运行时隐式创建,并在完成时关闭。

        当然,保持这样的结果集需要一些资源:lockslatchesmemory,甚至是disk space

        这些资源释放得越快越好。

        保持光标打开就像保持冰箱门打开

        你不会在没有必要的情况下连续几个小时这样做,但这并不意味着你永远不应该打开你的冰箱。

        这意味着:

        • 您不会逐行获取结果并将它们相加:而是调用SQLSUM
        • 您不会执行整个查询并从游标中获取第一个结果:您将 rownum <= 10 条件附加到您的查询中

        对于Oracle,在过程中处理游标需要臭名昭著的SQL/PLSQL context switch,每次从游标中获得SQL 查询的结果时都会发生这种情况。

        它涉及在线程之间传递大量数据并同步线程。

        这是Oracle中最烦人的事情之一。

        该行为的一个不太明显的后果是应尽可能避免使用 Oracle 中的触发器。

        创建触发器并调用DML函数等于打开游标选择更新的行并为此游标的每一行调用触发器代码。

        仅存在触发器(甚至是空触发器)可能会减慢 DML 操作 10 times 或更多。

        10g 上的测试脚本:

        SQL> CREATE TABLE trigger_test (id INT NOT NULL)
          2  /
        
        Table created
        
        Executed in 0,031 seconds
        SQL> INSERT
          2  INTO   trigger_test
          3  SELECT level
          4  FROM   dual
          5  CONNECT BY
          6     level <= 1000000
          7  /
        
        1000000 rows inserted
        
        Executed in 1,469 seconds
        SQL> COMMIT
          2  /
        
        Commit complete
        
        Executed in 0 seconds
        SQL> TRUNCATE TABLE trigger_test
          2  /
        
        Table truncated
        
        Executed in 3 seconds
        SQL> CREATE TRIGGER trg_test_ai
          2  AFTER INSERT
          3  ON trigger_test
          4  FOR EACH ROW
          5  BEGIN
          6     NULL;
          7  END;
          8  /
        
        Trigger created
        
        Executed in 0,094 seconds
        SQL> INSERT
          2  INTO   trigger_test
          3  SELECT level
          4  FROM   dual
          5  CONNECT BY
          6     level <= 1000000
          7  /
        
        1000000 rows inserted
        
        Executed in 17,578 seconds
        

        1.47 秒没有触发器,17.57 秒有一个空触发器什么都不做。

        【讨论】:

        • 有“10倍”减速的辅助参考资料吗?
        • @nineside:在表上创建一个空触发器并在其中插入 1M 条记录。
        【解决方案5】:

        其他答案正确地指出了游标的性能问题,但他们没有提到 SQL 和关系数据库最擅长基于集合的操作,而游标基本上用于迭代操作。有一些操作(在更广泛的意义上)使用游标更容易执行,但是在使用 SQL 时,您应该始终考虑使用数据集。游标经常被滥用,因为编码人员没有掌握如何使用基于集合的操作来执行任务。

        【讨论】:

        • Cursor 在 Oracle 中有大约 7 个定义。游标也是解析后的 SQL。这不是一个迭代操作。
        • 你能提供参考吗?我熟悉 Oracle 引用游标和 for 循环中使用的游标,但不熟悉其他游标。谷歌搜索没有多大帮助。
        • 虽然我是 Oracle 新手,但例如 REF CURSOR 只是一个指针,用于传回结果集(或 TABLE 函数)。所以也许我们看到游标的一般方式和它在 Oracle 中的使用方式可能会有所不同。所以我认为我们不能概括游标。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多