【问题标题】:Keyset pagination with composite key使用复合键的键集分页
【发布时间】:2019-12-21 13:50:05
【问题描述】:

我正在使用 oracle 12c 数据库,并且我有一个具有以下结构的表:

Id       NUMBER
SeqNo    NUMBER
Val      NUMBER
Valid    VARCHAR2

使用字段 Id 和 SeqNo 创建复合主键。

我想使用 Valid = 'Y' 获取数据并应用页面大小为 3 的 ketset 分页。假设我有以下数据:

Id    SeqNo    Val    Valid
1     1        10     Y
1     2        20     N
1     3        30     Y
1     4        40     Y
1     5        50     Y
2     1        100    Y
2     2        200    Y

预期结果:

----------------------------
Page 1
----------------------------
Id    SeqNo    Val    Valid
1     1        10     Y
1     3        30     Y
1     4        40     Y
----------------------------
Page 2
----------------------------
Id    SeqNo    Val    Valid
1     5        50     Y
2     1        100    Y
2     2        200    Y

偏移分页可以这样完成:

SELECT * FROM table ORDER BY Id, SeqNo OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY;

然而,在实际的数据库中,它有超过 500 万条记录,使用 OFFSET 会大大降低查询速度。因此,我正在寻找一种 ketset 分页方法(使用一些唯一字段而不是 OFFSET 跳过记录)

由于使用了复合主键,我需要使用来自多个字段的信息来偏移页面。

这是一个应该在 PostgreSQL 中工作的示例 SQL(获取第二页):

SELECT * FROM table WHERE (Id, SeqNo) > (1, 4) AND Valid = 'Y' ORDER BY Id, SeqNo LIMIT 3;

我如何在 oracle 中实现相同的目标?

【问题讨论】:

    标签: sql oracle pagination


    【解决方案1】:

    使用row_number() 解析函数和ceil 算术函数。算术函数对性能没有负面影响,row_number() over (order by ...) 表达式自动对数据进行排序,无需考虑插入顺序,也无需为主查询添加额外的order by 子句。所以,考虑一下:

    select Id,SeqNo,
           ceil(row_number() over (order by Id,SeqNo)/3) as page
      from tab
     where Valid = 'Y';
    

    附:它也适用于Oracle 11g,而OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY 仅适用于Oracle 12c

    Demo

    【讨论】:

    • 利用rownum的智能解决方案!我检查了您的演示,发现您的查询选择了所有页面。假设我只想要第二页。由于我无法在 WHERE 子句中引用字段别名,因此我需要在使用 WHERE page = 2 之前将您的查询包装到子选择中(从您的演示更新:dbfiddle.uk/…)。如果我有很多页面,这会影响性能吗?
    • @wtnyayja 是的,您需要一个子查询来过滤所需的页面。我不知道性能情况,应该针对庞大的数据集进行测试。
    • @wtnyayja 我刚刚测试了一个人口众多(记录约 1.71 亿条)的表,如果其中一列(Id 或 SeqNo)具有主(或唯一)索引,它的性能非常好它。
    • 我刚刚使用大约 1500 万个样本数据和 1000 的页面大小对我的实际数据库进行了测试。为 Id 和 SeqNo 创建了一个复合索引(复合主键)。第一页用了 50 秒,最后一页用了 55 秒。一个简单的 OFFSET 方法花费了较短的时间,第一页为 28 秒,最后一页为 56 秒。我想知道为什么...
    • 无论如何将其标记为答案,因为您的解决方案确实有效。
    【解决方案2】:

    您可以使用order by,然后使用fetchoffset 获取行,如下所示:

    Select ID, SEQ, VAL, VALID FROM TABLE
    WHERE VALID = 'Y'
    ORDER BY ID, SEQ 
    --FETCH FIRST 3 ROWS ONLY -- first page
    --OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY -- second pages
    --OFFSET 6 ROWS FETCH NEXT 3 ROWS ONLY -- third page
    

    --更新--

    您可以使用row_number解析函数如下。

    Select id, seqNo, Val, valid from
    (Select t.*, 
    Row_number(order by id, seq) as rn from table t
    Where valid = 'Y')
    Where ceil(rn/3) = 2 -- for page no. 2
    

    干杯!!

    【讨论】:

    • 你的方式当然行得通。但我要求的是键集分页方法,而不是 OFFSET 方式。
    • 您能否编辑您的问题并分享预期结果?
    • 抱歉信息不足。我刚刚更新了问题。
    猜你喜欢
    • 2015-03-24
    • 2014-06-30
    • 2014-09-16
    • 1970-01-01
    • 2018-06-05
    • 1970-01-01
    • 2015-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多