SQL 调优,长时间运行的查询 + rownum

Posted

技术标签:

【中文标题】SQL 调优,长时间运行的查询 + rownum【英文标题】:SQL tuning, long running query + rownum 【发布时间】:2017-05-25 09:20:58 【问题描述】:

我在数据库表中有数百万条记录,其中包含帐号、地址和更多列。我想要 100 行以 desc 顺序排序,我为此使用了 rownum,但是查询需要很长时间才能执行,因为它首先扫描整个表,使其按排序顺序然后应用 rownum。

最小化查询执行时间的解决方案是什么?

例如:

select * 
from 
    (select 
         acc_no, address 
     from 
         customer 
     order by 
         acc_no desc) 
where 
    ROWNUM <= 100;   

【问题讨论】:

你在客户表上定义了索引吗? Sumit 说你应该有一个索引,我假设 acc_no 是唯一的?如果它不是你的主键,它至少应该包含在索引中 这适用于哪个 RDBMS?请添加标签以指定您使用的是mysqlpostgresqlsql-serveroracle 还是db2 - 或完全其他的东西。 是的,索引已定义,我有查询(连接到多个表)返回 30 万条记录,但在 UI 中我只想显示 100 条记录,但由于查询本身需要很长时间execute ,因此在将 rownum 应用于外部查询后,它需要相同的时间,对此的任何解决方案。 @marc_s : 是 oracle DBMS 【参考方案1】:

根据过去的经验,我发现 TOP 最适合这种情况。

此外,您应该始终选择您只需要的列,并避免使用全卡 (*)

SELECT TOP 100 [acc_no], [address] FROM [customer] ORDER BY [acc_no] DESC

关于 TOP、LIMIT 甚至 ROWNUM 的有用资源。

https://www.w3schools.com/sql/sql_top.asp

【讨论】:

【参考方案2】:

确保在 acc_no 列上使用索引。

    如果 acc_no 上已存在索引,请通过验证查询执行计划来检查该索引是否在查询执行期间使用。

要创建新索引(如果不存在),请使用以下查询:

Create index idx1 on customer(acc_no); -- If acc_no is not unique
Create unique index idx1 on customer(acc_no); -- If acc_no is unique. Note: Unique index is faster.
    如果在解释计划输出中看到“全表扫描”,则说明优化器未使用索引。

先尝试提示:

select /*+ index(idx1) */ * from 
(select 
     acc_no, address 
 from 
     customer 
 order by 
     acc_no desc) 
where 
ROWNUM <= 100;   
    如果上面有提示的查询很快返回结果,那么你需要检查优化器为什么故意忽略你的索引。造成这种情况的一个可能原因是过时的统计数据。刷新统计信息。

希望这会有所帮助。

【讨论】:

【参考方案3】:

考虑在内部查询/内联视图中获取您的主要帐号,以便您只对这 100 条客户记录执行联接。否则,您可能会在百万+行上执行所有联接,然后对百万+结果进行排序以获得前 100 名。这样的事情可能会奏效。

    select .....
    from customer
    where customer.acc_no in (select acc_no from
                        (select inner_cust.acc_no
                         from   customer inner_cust
                         order by inner_cust.acc_no desc
                        )
                    where rownum <= 100)
       and ...

或者,如果您使用的是 12C,则可以使用 FETCH FIRST 100 ROWS ONLY

    select .....
    from customer
    where customer.acc_no in (select inner_cust.acc_no
                              from   customer inner_cust
                              order by inner_cust.acc_no desc
                              fetch first 100 rows only
                            )
       and ...

【讨论】:

【参考方案4】:

这将在 100 毫秒内给出结果,但请确保 ACC_NO 列上有索引。 ACC_NO+其他列也可以组合索引,但ACC_NO必须在索引的第一个位置。您必须在执行计划中看到“范围扫描”。不是“全表扫描”,也不是“跳过扫描”。您可能会在执行计划中看到嵌套循环(将从表中获取地址)。您可以通过为 ACC_NO、ADDRESS(按此顺序)创建组合索引来进一步提高速度。在这种情况下,Oracle 引擎根本不需要读取表,因为所有信息都包含在索引中。您可以在执行计划中进行比较。

select top 100 acc_no, address 
    from customer 
    order by acc_no desc

【讨论】:

我不确定,在这种情况下您的 SQL 是否正确,只需注意正确设置索引

以上是关于SQL 调优,长时间运行的查询 + rownum的主要内容,如果未能解决你的问题,请参考以下文章

Spark SQL + Window + Streaming 问题 - 使用 Spark Streaming 运行时,Spark SQL 查询需要很长时间才能执行

MySQL管理长时间运行查询

SQL 查询运行了很长时间

sql 检查长时间运行的查询

优化删除... where 使用 rownum 查询

sql 在MySQL中查找长时间运行的查询