【问题标题】:Oracle paging on large table using list of primary keys使用主键列表对大表进行 Oracle 分页
【发布时间】:2017-09-29 08:11:27
【问题描述】:

我正在使用 Oracle 11.2 并尝试在包含数百万行的表上编写分页查询。阅读其他文章,我认为我找到了最好的方法,但页码越高,它的速度就越慢。

这是我的解决方案。首先,我得到该数据页的主键值 (ID) 列表。然后我得到与这些 id 匹配的所有其他表数据。但是,这仍然运行缓慢。

SELECT *
FROM mytable
WHERE ID IN (
    SELECT ID
    FROM (
        SELECT ID, ROWNUM rnum
        FROM (
            SELECT ID
            FROM mytable
            ORDER BY ID
        ) results
        WHERE ROWNUM <= 1000010
    )
    WHERE rnum >= 1000001
 )

执行时间:30+ 秒。

如果我单独执行内部查询并手动将 ids 传递给外部查询,它会更快:

SELECT ID
FROM (
    SELECT ID, ROWNUM rnum
    FROM (
        SELECT ID
        FROM mytable
        ORDER BY ID
    ) results
    WHERE ROWNUM <= 1000010
)
WHERE rnum >= 1000001

Execution Time: 0.2 seconds.

Results: 
2134696,
2134697,
2134692,
2134693,
2134694,
2134695,
2134698,
2134699,
2134700,
2134701

SELECT *
FROM mytable
WHERE ID IN (
    2134696,
    2134697,
    2134692,
    2134693,
    2134694,
    2134695,
    2134698,
    2134699,
    2134700,
    2134701
)

Execution Time: 0.03 seconds.

第一个查询应该和其他两个查询一样快,但速度要慢得多。

谁能解释为什么会这样并提出更好的解决方案?

【问题讨论】:

    标签: sql oracle paging


    【解决方案1】:

    您的第一个查询是执行两个表(或索引)扫描(数百万行)并将它们连接在一起以过滤行。

    您的第二个和第三个查询分别执行单个表(或索引)扫描,但它们没有将它们连接在一起。

    你应该使用类似的东西:

    SELECT *
    FROM (
        SELECT r.*, ROWNUM rnum
        FROM (
            SELECT *
            FROM   mytable
            ORDER BY ID
        ) r
        WHERE ROWNUM <= 1000010
    )
    WHERE rnum >= 1000001
    

    只进行一次表扫描。

    Oracle 12c你可以使用:

    SELECT   *
    FROM     MYTABLE
    ORDER BY id 
    OFFSET 1000000 ROWS FETCH NEXT 10 ROWS ONLY
    

    【讨论】:

    • 感谢您的回复。我从您的查询开始,但再次返回结果需要 30 多秒。我的解决方案目前最快的方法是首先获取 id 列表,然后触发单独的查询以获取相关数据。对我来说似乎很疯狂,但它有效。我认为 Oracle 12 可以更好地处理这个问题。
    • 是的,它是主键列,并且可以非常快地自行获取。获取该表中的其他数据时会出现问题。
    • @markvpc 这可能是执行full table scan and an index scan 之间的区别 - 您需要查看各种查询的解释计划,以了解使用索引和不使用索引的区别,然后查看为什么你可能不使用索引。
    【解决方案2】:

    获取总记录数和记录的最佳方法。

    更改 [您的姓名] 更改 2 和 100 ( 2 "PAGENO", 100 "PERPAGE" )

    with 
    XPARAMS as (select 2 "PAGENO", 100 "PERPAGE" from dual)
    ,P2 as (select  (XPARAMS.PAGENO-1)*XPARAMS.PERPAGE "STARTNO",   XPARAMS.PAGENO*XPARAMS.PERPAGE "ENDNO" from XPARAMS)
    ,D1 as (select  * from [YOURTABLENAME] t order by ID)
    ,DCOUNT as (select count(*) "TOTALCOUNT" from D1)
    ,D2 as (select  rownum "RN" , D1.* from D1 )
    ,D3 as (select D2.* from D2,P2 where D2.RN between P2.STARTNO and p2.ENDNO )
    select D3.RN, DCOUNT.TOTALCOUNT, XPARAMS.PAGENO, XPARAMS.PERPAGE, D3.* from D3,DCOUNT, XPARAMS
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-03
      • 2018-11-18
      相关资源
      最近更新 更多