【问题标题】:where rownum=1 query taking time in Oracle其中 rownum=1 查询在 Oracle 中花费时间
【发布时间】:2011-07-15 11:54:43
【问题描述】:

我正在尝试执行类似

的查询
select * from tableName where rownum=1

这个查询基本上是为了获取表的列名。表中有超过一百万条记录。当我提出上述条件时,它需要花费很多时间来获取第一行。是否有任何替代方法来获取第一行。

【问题讨论】:

    标签: oracle optimization where rownum


    【解决方案1】:

    这个问题已经回答了,我将解释一下为什么有时过滤器 ROWNUM=1 或 ROWNUM

    当遇到 ROWNUM 过滤器(在单个表上)时,优化器将生成带有 COUNT STOPKEY 的 FULL SCAN。这意味着 Oracle 将开始读取行,直到遇到前 N 行(这里 N=1)。完整扫描从第一个范围读取块到高水位标记。 Oracle 无法预先确定哪些块包含行,哪些不包含行,因此将读取所有块,直到找到 N 行。如果第一个块是空的,可能会导致多次读取。

    考虑以下几点:

    SQL> /* rows will take a lot of space because of the CHAR column */
    SQL> create table example (id number, fill char(2000));
    
    Table created
    
    SQL> insert into example 
      2     select rownum, 'x' from all_objects where rownum <= 100000;
    
    100000 rows inserted
    
    SQL> commit;
    
    Commit complete
    
    SQL> delete from example where id <= 99000;
    
    99000 rows deleted
    
    SQL> set timing on
    SQL> set autotrace traceonly
    SQL> select * from example where rownum = 1;
    
    Elapsed: 00:00:05.01
    
    Execution Plan
    ----------------------------------------------------------
       0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=7 Card=1 Bytes=2015)    
       1    0   COUNT (STOPKEY)
       2    1     TABLE ACCESS (FULL) OF 'EXAMPLE' (TABLE) (Cost=7 Card=1588 [..])
    
    Statistics
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
          33211  consistent gets
          25901  physical reads
              0  redo size
           2237  bytes sent via SQL*Net to client
            278  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
              0  sorts (memory)
              0  sorts (disk)
              1  rows processed
    

    如您所见,一致获取的数量非常高(对于单行)。在某些情况下可能会遇到这种情况,例如,您插入带有/*+APPEND*/ 提示的行(因此高于高水位线),并且您还定期删除最旧的行,导致在开头有很多空白空间段。

    【讨论】:

      【解决方案2】:

      试试这个:

      select * from tableName where rownum<=1
      

      有一些奇怪的 ROWNUM 错误,有时稍微更改查询即可修复它。我以前见过这种情况,但我无法重现。

      这里有一些类似问题的讨论:http://jonathanlewis.wordpress.com/2008/03/09/cursor_sharing/http://forums.oracle.com/forums/thread.jspa?threadID=946740&tstart=1

      【讨论】:

        【解决方案3】:

        Oracle 肯定有可以用来获取列名的元数据表,比如 DB2 中的 sysibm.syscolumns 表?

        而且,在quick web search 之后,情况似乎就是这样:见ALL_TAB_COLUMNS

        我会使用这些而不是去实际的桌子,比如(未经测试):

        SELECT   COLUMN_NAME
        FROM     ALL_TAB_COLUMNS
        WHERE    TABLE_NAME = "MYTABLE"
        ORDER BY COLUMN_NAME;
        

        如果您一心想找出查询缓慢的原因,您应该恢复到标准方法:让您的 DBMS 为您解释查询的执行计划。对于 Oracle,请参阅this document 的第 9 节。

        Ask Tom - Oracle 上的对话似乎表明行号是在选择阶段之后创建的,这可能意味着查询无论如何都会检索所有行。 explain 可能有助于确定这一点。如果它包含FULL 而没有COUNT STOPKEY,那么这可以解释性能。

        除此之外,我对 Oracle 细节的了解会减少,您将不得不进一步分析 explain

        【讨论】:

        • 取列名不是唯一的问题,我还想知道为什么我们使用 rownum=1 需要时间。另外我的查询是在 Oracle 中
        • @Harish,您的 actual 要求似乎是获取列名。我提供了一种回避性能问题的方法。我知道您要求获得第一行的方法,但这对于您尝试做的事情来说不是必需的(例如,如果它包含六个 2G BLOB 列,那将是一种可怕的浪费)。如果您仍然想知道为什么 where 子句会导致问题,那么很遗憾,我无法帮助您。我宁愿务实。查询 is 是针对 Oracle 的,它是我提到的 DB2 的 Oracle 等价物(这是我的专长所在)。
        【解决方案4】:

        您的查询正在执行全表扫描,然后返回第一行。

        试试

        SELECT * FROM table WHERE primary_key = primary_key_value;
        

        第一行,尤其是与 ROWNUM 相关的行,是由 Oracle 任意决定的。除非您提供 ORDER BY 子句,否则查询之间可能不一样。

        因此,选择一个主键值作为过滤依据是获取单行的最佳方法。

        【讨论】:

        • 我也想要第一行的值。
        • 定义“第一行”。如果您不使用 order by,则“第一行”未定义。根据定义,它可以是表中的任何行。也许你应该告诉我们真正的问题是什么。我看到了各种有价值的建议,但你一直在移动目标。
        【解决方案5】:

        我认为您稍微错过了 ROWNUM 的概念 - 根据 Oracle 文档:“ROWNUM 是一个伪列,它返回一行在结果集中的位置。在从数据库中选择记录之后评估 ROWNUM以及在执行 ORDER BY 子句之前。” 因此,它会返回它认为结果集中 #1 的任何行,在您的情况下将包含 1M 行。

        您可能需要查看 ROWID 伪列:http://psoug.org/reference/pseudocols.html

        【讨论】:

        • 是的,我对此表示怀疑,感谢您的确认。那么解决方案是什么?
        • 只有在有 ORDER BY 时才如此。如果没有 ORDER BY,您通常会在说明计划中看到“COUNT STOPKEY”,这意味着 Oracle 可能会在第一行之后停止。
        【解决方案6】:

        我最近遇到了与您描述的相同的问题:我希望从非常大的表中取出一行作为快速、肮脏、简单的内省,并且“where rownum=1”单独的行为非常糟糕。以下是对我有用的补救措施。

        选择某个索引的第一项的 max(),然后用它来选择“rownum=1”的所有行的一小部分。假设我的表在数字“group-id”上有一些索引,然后比较一下:

         select * from my_table where rownum = 1;
         -- Elapsed: 00:00:23.69
        

        用这个:

         select * from my_table where rownum = 1
            and group_id = (select max(group_id) from my_table);
         -- Elapsed: 00:00:00.01
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-10-22
          相关资源
          最近更新 更多