在查询结束前开始查看查询结果

Posted

技术标签:

【中文标题】在查询结束前开始查看查询结果【英文标题】:Begin Viewing Query Results Before Query Ends 【发布时间】:2010-04-01 20:14:44 【问题描述】:

假设我查询了一个包含 500K 行的表。我想开始查看 fetch 缓冲区中的任何行,其中包含结果集,即使查询尚未完成。我想滚动获取缓冲区。如果我滚动得太远,我想显示如下消息:“REACHED LAST ROW IN FETCH BUFFER.. QUERY HAS NOT COMPLETED”。

这是否可以在查询继续构建结果集时使用 fgets() 读取提取缓冲区来完成?这样做意味着多线程*

在 Oracle、Informix、mysql 或其他 RDBMS 中是否可以提供除 FIRST ROWS 提示指令之外的类似功能?

整个想法是能够在长查询完成之前开始查看行,同时显示有多少行可供立即查看的计数器。

编辑:我的建议可能需要从根本上改变数据库服务器的架构,比如它们处理内部提取缓冲区的方式,例如锁定结果集直到查询完成,等等。像我建议的那样的功能将非常有用,特别是对于需要很长时间才能完成的查询。为什么必须等到整个查询完成后,您才能开始查看一些结果,同时查询继续收集更多结果!

【问题讨论】:

@Frank:什么平台?您现在如何访问数据库?你没有给我们太多的继续。 @Broam,我想这取决于你为什么在这里回答问题。 而且,+1,因为这是一个有趣的问题。 帮助别人的报酬对我来说已经足够了。尽管我将自己限制在 SO 上最不受欢迎的主题之一(即 MS-ACCESS),但我最终还是获得了很高的声誉。除了我自己的娱乐之外,我不理解任何动机,所以也许这就是我不明白的原因。 嗯,显然有办法做到这一点。大多数数据库客户端,例如。当您执行查询并在表格网格视图中查看数据时,Toad 和 SQL Developer 会使用此技术。当前视图仅获取少量行,并且每次滚动都从缓冲区中检索行。我很想看到所提问题的现实解决方案。 【参考方案1】:

释义:

我有一个有 500K 行的表。没有良好索引的即席查询需要全表扫描。我想在全表扫描继续时立即查看返回的第一行。然后我想滚动浏览下一个结果。

看来您想要的是某种可以有两个(或更多)线程在工作的系统。一个线程将忙于同步地从数据库中获取数据,并将其进度报告给程序的其余部分。另一个线程将处理显示。

同时,我想显示表扫描的进度,例如:“Searching...found 23 of 500,000 rows so far”。

不清楚您的查询将返回 500,000 行(实际上,我们希望它不会),尽管它可能必须扫描所有 500,000 行(并且很可能只找到 23 行匹配)。确定要返回的行数很难;确定要扫描的行数更容易;确定已扫描的行数非常困难。

如果我滚动得太远,我想显示如下消息:“已到达前瞻缓冲区中的最后一行...查询尚未完成”。

所以,用户已经滚动过了第 23 行,但是查询还没有完成。

这可以吗?可能像:spawn/exec、声明滚动光标、打开、获取等?

这里有几个问题。 DBMS(大多数数据库都是如此,当然 IDS 也是如此)在处理一个语句时保持与当前连接的关系。很难获得有关查询进展情况的反馈。您可以查看查询开始时返回的估计行(SQLCA 结构中的信息),但这些值很容易出错。当您到达第 23 行的第 200 行时,您必须决定要做什么,或者您只能到达 5,697 的第 23 行。有总比没有好,但它并不可靠。确定查询的进展程度非常困难。并且有些查询需要实际的排序操作,这意味着很难预测需要多长时间,因为在排序完成之前没有可用的数据(一旦完成排序,之间只有时间进行通信) DBMS 和应用程序来阻止数据的传递)。

Informix 4GL 有许多优点,但线程支持不是其中之一。该语言在设计时并未考虑线程安全,并且没有简单的方法将其改装到产品中。

我确实认为您所寻求的内容最容易得到两个线程的支持。在像 I4GL 程序这样的单线程程序中,没有一种简单的方法可以在等待用户输入更多输入的同时获取行(例如“向下滚动充满数据的下一页”)。

FIRST ROWS 优化是对 DBMS 的提示;它可能会或可能不会对感知的性能产生显着的好处。总体而言,这通常意味着从 DBMS 的角度来看,查询处理的优化程度较低,但快速将结果提供给用户可能比 DBMS 上的工作负载更重要。


在下面某处,在一个被否决的答案中,弗兰克喊道(但请不要喊):

这正是我想要做的,生成一个新进程以开始显示 first_rows 并滚动浏览它们,即使查询尚未完成。

好的。这里的困难是在两个客户端进程之间组织 IPC。如果两者都连接到 DBMS,则它们具有单独的连接,因此一个会话的临时表和游标对另一个会话不可用。

执行查询时,会创建一个临时表来保存当前列表的查询结果。 IDS 引擎是否在此临时表上设置排他锁,直到查询完成?

并非所有查询都会生成临时表,尽管滚动游标的结果集通常确实具有与临时表大致等效的内容。 IDS 不需要锁定支持滚动游标的临时表,因为只有 IDS 可以访问该表。如果它是一个普通的临时表,那么仍然不需要锁定它,因为它不能被创建它的会话访问。

我的意思是 500k 行,是查询表中的 nrows,而不是返回多少预期结果。

也许更准确的状态消息是:

Searching 500,000 rows...found 23 matching rows so far

我了解可以在 sysmaster:sysactptnhdr.nrows 中获得准确的 nrow 计数?

可能;您还可以使用 'SELECT COUNT(*) FROM TheTable' 获得快速准确的计数;这不会扫描任何内容,而只是访问控制数据——实际上可能与 SMI 表 sysmaster:sysactptnhdr 的 nrows 列中的数据相同。

因此,产生一个新进程显然不是成功的秘诀;您必须将查询结果从衍生进程传输到原始进程。正如我所说,具有单独的显示和数据库访问线程的多线程解决方案将在一段时间后工作,但使用 I4GL 执行此操作存在问题,因为它不是线程感知的。您仍然需要决定客户端代码如何存储信息以供显示。

【讨论】:

【参考方案2】:

存在三个基本限制因素:

    查询的执行计划。如果执行计划最后有阻塞操作(例如排序或急切假脱机),则引擎无法在查询执行的早期返回行。它必须等到所有行都被完全处理后,才会尽快将数据返回给客户端。这个时间本身可能是可观的,所以这部分可能适用于你正在谈论的内容。但是,一般来说,您不能保证查询很快就会有很多可用的。

    数据库连接库。从数据库返回记录集时,驱动程序可以使用服务器端分页或客户端分页。使用哪个可以并且确实会影响将返回哪些行以及何时返回。客户端分页强制立即返回整个查询,从而减少在数据全部输入之前显示任何数据的机会。谨慎使用正确的分页方法对于在查询生命周期的早期显示数据的机会至关重要。

    客户端程序使用同步或异步方法。如果您只是复制和粘贴一些用于执行查询的 Web 示例代码,您将不太可能在查询仍在运行时处理早期结果——相反,该方法将阻塞,您将一无所获,直到它全部完成. 当然,服务器端分页(参见第 2 点)可以缓解这种情况,但是在任何情况下,如果您不专门使用异步方法,您的应用程序至少会被阻塞一小段时间。对于正在使用 .Net 阅读本文的任何人,您可能需要查看 Asynchronous Operations in .Net Framework。

如果你做对了所有这些,并使用 FAST FIRSTROW 技术,你也许可以做一些你正在寻找的事情。但不能保证。

【讨论】:

+1,因为您正在解决引擎如何处理查询的内部工作原理。那么提示指令FIRST ROWS(在 Informix 中,我不知道 Oracle 的等价物)使用什么方法来比没有提示的标准查询更快地返回结果?.. 也许查询处理方式的设计可以修改为以某种方式适应我的想法?我认为它可以准确地为用户提供一种有效的方式来消磨时间,即在查询不断消失的同时查看结果! 客户端是否有可能在同一个查询请求上运行多个线程?...即一个线程将查询提交给引擎,而另一个线程正在对获取缓冲区以查看是否已在其中存放任何内容,而第一个线程正在进行 IPC 以查看查询是否尚未完成等? 听起来您有一个特定的 DBMS,但我并不熟悉。很抱歉,我不能更具体。 我不认为它比我说的更可能实现。如果执行计划最后有一个 SORT,引擎将如何在发现所有行之前向客户端提供任何行?如果在大量连接中最终连接的最佳选择是 HASH MATCH,它会阻止任何行离开引擎,因为它无法探测左侧输入的哈希,直到该哈希完全构建?引擎可能仍然不知道哪些行将出现在最终结果中,因此它无法为您提供任何信息。我真的已经给了你系统固有的限制。 首先,使用 .Net 并不意味着 MS-SQL。它可以连接到许多数据库引擎。其次,您也可以使用异步读取来获取已提交查询的部分结果集!如果执行计划没有阻塞运算符(假设它正在执行循环连接、合并连接或惰性假脱机),它会在找到所有行之前将行返回给客户端。在引擎完成操作之前,您可以在收到这些找到的行时显示它们。【参考方案3】:

可以通过分析函数来完成,但是如果没有索引,无论您做什么,Oracle 都必须对表进行全扫描以确定计数。分析可以简化您的查询:

SELECT x,y,z, count(*) over () the_count
  FROM your_table
 WHERE ...

返回的每一行将在 the_count 中包含查询返回的总行数。然而,正如我所说,Oracle 必须在返回任何内容之前完成查询以确定计数。

根据您处理查询的方式(例如,表单中的 PL/SQL 块),您可以使用上述查询打开游标,然后循环游标并显示记录集并为用户提供取消的机会。

【讨论】:

将优化器目标更改为FIRST_ROWS 有帮助吗? 如果您也想要计数,则不需要,因为 oracle 必须计算所有行;-) 我只是想知道是否有任何方法可以访问由查询创建的临时文件,该文件开始将结果放入结果集中。如果服务器可以做到这一点,为什么即使查询尚未完成,我们也不能访问它?【参考方案4】:

我不确定您将如何完成此操作,因为查询必须在结果已知之前完成。没有任何 RDBMS(据我所知)提供任何方法来确定在查询完成之前已找到多少个查询结果。

事实上,我无法说出这样一个特性在 Oracle 中的成本有多高,因为我从未见过源代码。但是,从外到内,我认为这将相当昂贵,并且可能会使查询完成的时间增加一倍(如果不是更多的话)。这意味着在每个结果之后更新一个原子计数器,当您谈论数百万可能的行时,这并不便宜。

【讨论】:

【参考方案5】:

所以我将我的 cmets 放入这个答案 - 就甲骨文而言。

Oracle 在系统全局区域 (SGA) 中为每个实例维护自己的缓冲区缓存。缓冲区高速缓存的命中率取决于大小,大多数情况下会达到 90%,这意味着 10 次命中中有 9 次会在没有到达磁盘的情况下得到满足。

考虑到上述情况,即使有一种“方式”(可以这么说)来访问您运行的查询的缓冲区缓存,结果也很大程度上取决于缓存大小因素。如果缓冲区缓存太小,则缓存命中率会变小,并且会导致更多的物理磁盘 I/O,这将导致缓冲区缓存在临时数据内容方面不可靠。如果缓冲区缓存太大,则缓冲区缓存的部分将未被充分利用并且内存资源将被浪费,这会导致过多的不必要的处理试图访问缓冲区缓存,同时为了窥视它为您想要的数据。

还取决于您的缓存大小和 SGA 内存,由 ODBC 驱动程序/优化器决定何时以及使用多少(缓存缓冲或直接磁盘 I/O)。

在尝试访问“缓冲区缓存”以查找您正在寻找的“行”方面,可能有一种方法(或在不久的将来)可以做到这一点,但没有办法知道是什么您正在寻找的(“行”)到底是否存在。

另外,大表的全表扫描通常会导致物理磁盘读取和较低的缓冲区缓存命中率。您可以通过查询v$filestat 并加入@987654322 来了解数据文件级别的全表扫描活动@。以下是您可以使用的查询和示例结果:

 SELECT   A.file_name, B.phyrds, B.phyblkrd
 FROM     SYS.dba_data_files A, v$filestat B
 WHERE    B.file# = A.file_id
 ORDER BY A.file_id;

由于这整个考验高度基于多个参数和统计数据,您正在寻找的结果可能仍然是这些因素驱动的概率。

【讨论】:

任何行,即使只是满足查询条件的一行,都需要查看,同时查询继续搜索。如果存储了此类数据,这就是我要查找的内容在提取缓冲区或当前列表(结果集)存储的任何地方,通常在查询完成时释放以供查看!..我不完全了解服务器如何处理此问题的内部工作原理,也不知道发生了什么当执行全表扫描或排序时,由于执行 ORDER BY 子句,此区域。 但是没有办法知道你要找的东西是否在那里!缓冲区缓存主要是引擎自己的暂存器,可供所有并发事务和进程访问。有时就像大海捞针一样,而针可能根本不存在。 我认为引擎选择临时存储特定查询的结果集的任何地方都应该可以在一个特定的地方访问,而不是与其他东西混在一起? 那越来越动态,我希望它在新版本的 Oracle 中更加灵活。 “引擎选择的地方”基于服务器/机器的硬件级别和系统级别的许多参数,并且由于不同的服务器可能具有不同的配置,因此不。对于 CPU、内存、磁盘容量等,优化器最好即时确定“选择什么”。您所说的听起来更像是“缓冲区缓存和磁盘 IO”的“缓冲区缓存”,因此所有规则也适用于该缓冲区缓存!

以上是关于在查询结束前开始查看查询结果的主要内容,如果未能解决你的问题,请参考以下文章

sqlalchemy 怎么查看某时间段的内容

sqlalchemy 怎么查看某时间段的内容

SQL 限制查询结果

LINQ 查询结果中的奇怪值

SQL如何显示查询结果的前100条?

如何将SQL server 2008 里的查询结果导出到 Excel 表内?