【问题标题】:Oracle OCI - How to obtain the number of rows in the select set without fetchingOracle OCI - 如何在不获取的情况下获取选择集中的行数
【发布时间】:2015-07-31 18:05:16
【问题描述】:

这里是新手(关于 Oracle 和 OCI)。

我正在编写一个程序来为我的公司完成一些任务。在这些任务中,我必须更新一个 oracle 数据库以使其与其他操作保持同步。

我想有效地预先分配内存以保存在选择集上获取的结果。为此,我需要知道选择集中有多少行。

我可以用 2 个单独的语句来做到这一点;首先是 SELECT COUNT 语句,然后是 SELECT 语句,但从服务器的角度来看,这是低效的。我也可以只执行我的 SELECT 语句并在从选择集中获取行时动态分配内存,但从客户端的角度来看这是低效的。

我想简单地执行 SELECT 语句,并在获取任何行之前检索选择集中匹配的行数。

但我找不到包含此信息的属性。
有 OCI_ATTR_ROW_COUNT 但这是 FETCHED 的行数,而不是选择集中的总行数。 然后是 OCI_ATTR_PARAM_COUNT,它告诉你 COLUMNS 的数量(似乎没用,因为你已经知道你在 SQL 中请求了多少列)。

有人知道如何在获取之前获取选择集的行数吗?

谢谢 乔什

【问题讨论】:

  • 我们在谈论多少数据/行?宿主语言是什么?
  • 几个答案说没有办法查询选择集中的行数。谢谢您的回复。我可以使用可滚动光标解决此问题吗?换句话说,我知道选择集中的最大可能行数(表中的行数)。我可以从最大值开始乱序获取行并以二进制搜索方式向下工作,直到确定有多少行?使用二分搜索算法,这不会超过 25 或 30 次获取...
  • 我们最小的表有 1 行(实际上有一些是 0,但它们不是很有趣),而我们最大的表有 180 万行。我们的选择集预计通常会在一个典型的表上生成 20 到 50,000 个结果。

标签: oracle select count row oracle-call-interface


【解决方案1】:

您可以使用可滚动的语句句柄并获取最后一行,然后询问当前位置:

OCIStmtExecute(pOCIServiceHnd, pOCIStmtHnd, pOCIErrorHnd, 0, 0, NULL, NULL,   OCI_STMT_SCROLLABLE_READONLY);

int rowCount = 0;
OCIStmtFetch2(pOCIStmtHnd, pOCIErrorHnd, 1, OCI_FETCH_LAST, 0, OCI_DEFAULT);
OCIAttrGet(pOCIStmtHnd, OCI_HTYPE_STMT, &rowCount, NULL, OCI_ATTR_CURRENT_POSITION, pOCIErrorHnd);

如果您要重复使用它,请记住倒带您的语句句柄:

OCIStmtFetch2(pOCIStmtHnd, pOCIErrorHnd, 1, OCI_FETCH_FIRST, 0, OCI_DEFAULT);

【讨论】:

    【解决方案2】:

    正如其他人所说,Oracle 使用生产者-消费者设计模式。因此,结果数据不会保存在任何“缓冲区”中,而是在执行 OCIStmtFetch 时即时生成。这也与 SQL 优化器目标(ALL_ROWS 和 FIRST_ROWS)有关。例如在 Oracle Forms(或其他 4GL 工具)中 大多数复杂的查询永远不会结束。用户将简单地获得显示的第一页数据,并且当且仅当他们向下滚动时,从数据库返回的行数越多。这在概念上与 Java/Web 应用程序非常不同,Java/Web 应用程序必须首先从数据库中获取所有行,然后应用程序将整个数据块发送到客户端。

    例如 PostgreSQL 的工作方式不同。它使用客户端的 RAM 作为缓冲区。因此,对语句的“执行”调用将等到所有结果行都存储在客户端的 RAM 中。调用 fetch 只会遍历客户端 RAM 中的缓冲区。 (除非您使用显式游标)。

    【讨论】:

      【解决方案3】:

      根据定义,Oracle 不知道结果集中将有多少行,直到完成全部获取。根据用于查询的执行计划,它可能实际上是在您获取结果集时逐行构建结果集。

      我会说您根本不需要担心这一点 - 只需动态分配内存即可。但我想如果它是一个非常大的结果集,您可能会遇到一些性能问题。

      我能想到几个选项:

      1) 如您所说,执行初始查询以计算匹配行并使用它来分配内存以保存完整结果。

      2) 使用某种技术来执行查询并将其结果存储在服务器端,返回计数。这可能类似于将结果存储在内存中的打包过程,或者插入到工作表中。除非您确定了需要解决的真正问题,否则这可能比其价值更多。

      3) 根据您对可能返回的行数的期望,以合理大小的块分配内存。在某些情况下,您最终可能会过度分配,但如果您的块大小不是太大,这可能不是一个大问题。

      【讨论】:

        【解决方案4】:

        你不能。 Oracle 数据库在获取最后一行之前通常不知道查询将返回多少行。

        通常,客户端会设计为从客户端获取特定大小的数据批次,处理这些结果,然后获取下一批数据。例如,大多数 PL/SQL GUI 应用程序(TOAD、SQL Developer 等)将执行查询,获取第一批 50 或 100 行,然后等待用户。当用户翻阅数据时,客户端将获取下一批,处理它并迭代直到最后一批被消耗。您是否可以从内存中丢弃之前的批次,或者是否需要保留它们(以支持用户向后滚动的能力,例如,无需重新执行查询)是您的应用程序需要确定的事情。

        当您担心在客户端上动态分配内存时,您担心什么效率低下?这通常是最有效的做事方式。假设您选择了合理的批次大小,您将在客户端请求合理大小的内存块(每个批次一次或一个,取决于您是否需要在本地缓存数据),这非常有效。

        【讨论】:

        • 我明白了。好的,让我问一个后续问题;在 OCIStatementExecute 函数期间服务器端发生了什么?我在(显然是错误的)假设下,在此调用期间,服务器端评估了主题表中的所有行的选择条件,并在服务器端的内存中构建了匹配行的结果集,以便稍后在期间传输给客户端随后调用 OCIStatementFetch。所以,如果这不是它在做什么,它在做什么?
        • @Josh - OCIStatementExecute 将查询发送到对其进行优化的数据库。生成的游标句柄允许应用程序请求数据。我已经有一段时间没有深入了解了,但我希望作为一种优化,第一批数据实际上也会被返回,以节省等待第一个 fetch 调用的网络往返。数据库通常不会在内存中实现整个结果集,除非它绝对必须对整个结果集进行排序。
        • @Josh - 有关更多信息,您可以阅读处理 SQL 语句的 parse-execute-fetch 步骤。参见例如asktom.oracle.com/pls/asktom/…。该帖子的相关引用:“执行 - 我们打开语句。对于更新,删除,插入 - 就是这样,当您打开语句时,我们执行它。所有工作都在这里发生。对于选择它更复杂。大多数选择在执行过程中会做零工作。我们所做的就是打开光标"
        猜你喜欢
        • 1970-01-01
        • 2017-01-31
        • 1970-01-01
        • 1970-01-01
        • 2022-06-10
        • 2011-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多