DB2 的 LIMIT 等效项

Posted

技术标签:

【中文标题】DB2 的 LIMIT 等效项【英文标题】:Equivalent of LIMIT for DB2 【发布时间】:2011-04-22 13:28:12 【问题描述】:

您如何在 DB2 for iSeries 中使用LIMIT

我有一个超过 50,000 条记录的表,我想返回 0 到 10,000 条记录,以及 10,000 到 20,000 条记录。

我知道在 SQL 中,您在查询末尾写 LIMIT 0,10000 表示 0 到 10,000 和 LIMIT 10000,10000 在查询末尾表示 10000 到 20,000

那么,这在 DB2 中是如何完成的呢?代码和语法是什么? (感谢完整的查询示例)

【问题讨论】:

ROW_NUMBER() 仅在 iSeries DB2 V5R4 中实现。对于以前的版本,请尝试使用类似的 RRN()。 RRN() 与 row_number() 完全不同。 对我不起作用。语法错误。 尝试 RRN(filename) 它将给出行的物理相对记录号。如果行已被删除,RRN 不会是连续的并且可以跳过数字。 RRN 也不会按键顺序排列,但如果没有发生删除,则将基于添加顺序排列。在任何情况下,RRN 对于一行都是唯一的,可用于选择表的子集。 DB2 根据programmingzen.com/2010/06/02/…从 DB2 9.7.2 提供限制关键字支持 【参考方案1】:

使用FETCH FIRST [n] ROWS ONLY

http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.perf/db2z_fetchfirstnrows.htm

SELECT LASTNAME, FIRSTNAME, EMPNO, SALARY
  FROM EMP
  ORDER BY SALARY DESC
  FETCH FIRST 20 ROWS ONLY;

要获取范围,您必须使用 ROW_NUMBER()(自 v5r4 起)并在 WHERE 子句中使用它:(从此处窃取:http://www.justskins.com/forums/db2-select-how-to-123209.html

SELECT code, name, address
FROM ( 
  SELECT row_number() OVER ( ORDER BY code ) AS rid, code, name, address
  FROM contacts
  WHERE name LIKE '%Bob%' 
  ) AS t
WHERE t.rid BETWEEN 20 AND 25;

【讨论】:

是的,我也找到了,呵呵。我同时在编辑问题以表明我也想要中间行。 你必须用 ROW_NUMBER 做这样的事情:justskins.com/forums/db2-select-how-to-123209.html ROW_NUMBER 不是有效的关键字。但是谢谢这个链接,它给了我一个想法并且它有效。【参考方案2】:

开发了这个方法:

您需要一个可以排序的具有唯一值的表。

如果您想要 10,000 到 25,000 行并且您的表格有 40,000 行,首先您需要获取起点和总行数:

int start = 40000 - 10000;

int total = 25000 - 10000;

然后通过代码将这些传递给查询:

SELECT * FROM 
(SELECT * FROM schema.mytable 
ORDER BY userId DESC fetch first start rows only ) AS mini 
ORDER BY mini.userId ASC fetch first total rows only

【讨论】:

请注意,第 10000 行从结果集中排除,第一行是第 10001 行。 有趣的解决方案。我打算用它来兼容 H2 测试数据库......但是,遗憾的是,它的工作速度比 SELECT row_number() OVER (ORDER BY code) 方法慢了约 30 倍。【参考方案3】:

最近在 DB2 for i 7.1 和 7.2 中添加了对 OFFSET 和 LIMIT 的支持。您需要以下 DB PTF 组级别才能获得此支持:

SF99702 9 级,适用于 IBM i 7.2 SF99701 级别 38,适用于 IBM i 7.1

请参阅此处了解更多信息:OFFSET 和 LIMIT documentation,DB2 for i 增强 Wiki

【讨论】:

【参考方案4】:

这是我想出的解决方案:

select FIELD from TABLE where FIELD > LASTVAL order by FIELD fetch first N rows only;

通过将 LASTVAL 初始化为 0(或 '' 用于文本字段),然后将其设置为最新记录集中的最后一个值,这将以 N 条记录为单位逐步遍历表。

【讨论】:

(我最初以为您是在表中设置值,这在并发系统上会壮观出现问题)是的,这应该适用于您正在做的情况顺序读取表格,尽管在 N 小于列中相同值的数量的情况下您需要某种决胜列(尽管在使用 ROW_NUMBER() 时也是如此)。初始值也必须谨慎选择 - 如果列包含 negative 值,0 显然会出现问题。空值需要小心。如果页面跳过,将无法正常工作。 感谢您的评论。我认为有一个隐含的假设,即我们用来控制查询的字段是唯一的并且单调递增。我同意,如果这些假设不成立,则无法访问表中的所有记录。而且,当然,您必须从有意义的 LASTVAL 开始是正确的。一般来说,我认为您希望从“从 TABLE 中选择 MINIMUM(FIELD)”返回的任何内容开始。如果该字段被索引,大多数数据库引擎会比顺序读取整个表做得更好。【参考方案5】:

@elcool's solution 是一个聪明的主意,但您需要知道总行数(甚至可以在执行查询时更改!)。所以我提出了一个修改版本,不幸的是需要3个子查询而不是2个:

select * from (
    select * from (
        select * from MYLIB.MYTABLE
        order by MYID asc 
        fetch first last rows only 
        ) I 
    order by MYID desc
    fetch first length rows only
    ) II
order by MYID asc

其中last 应替换为我需要的最后一条记录的行号,length 应替换为我需要的行数,计算为last row - first row + 1

例如如果我想要从 10 到 25 的行(总共 16 行),last 将是 25,length 将是 25-10+1=16。

【讨论】:

我鄙视那些在别人花时间回答他们的问题时投反对票的人。【参考方案6】:

试试这个

SELECT * FROM
    (
        SELECT T.*, ROW_NUMBER() OVER() R FROM TABLE T
    )
    WHERE R BETWEEN 10000 AND 20000

【讨论】:

【参考方案7】:

LIMIT 子句允许您限制查询返回的行数。 LIMIT 子句是 SELECT 语句的扩展,具有以下语法:

SELECT select_list
FROM table_name
ORDER BY sort_expression
LIMIT n [OFFSET m];

在这种语法中:

n 是要返回的行数。 m 是在返回 n 行之前要跳过的行数。

LIMIT 子句的另一个较短版本如下:

LIMIT m, n;

此语法意味着跳过m 行并从结果集中返回下一个n 行。

表可能以未指定的顺序存储行。如果您不将ORDER BY 子句与LIMIT 子句一起使用,则返回的行也未指定。因此,最好始终将ORDER BY 子句与LIMIT 子句一起使用。

更多详情请见Db2 LIMIT。

【讨论】:

【参考方案8】:

您还应该考虑 OPTIMIZE FOR n ROWS 子句。在Guidelines for restricting SELECT statements 主题中的 DB2 LUW 文档中了解所有这些的更多详细信息:

OPTIMIZE FOR 子句声明了仅检索结果子集或优先检索仅前几行的意图。然后,优化器可以选择能够最大限度缩短检索前几行的响应时间的访问计划。

【讨论】:

【参考方案9】:

有两种解决方案可以有效地对 DB2 表进行分页:

1 - 使用函数 row_number() 和已在另一篇文章中介绍的子句 OVER 的技术(“SELECT row_number() OVER (ORDER BY ...)”)。在一些大桌子上,我注意到有时性能会下降。

2 - 使用可滚动光标的技术。实现取决于所使用的语言。这种技术在大桌子上似乎更强大。

我在明年的一次研讨会上介绍了在 php 中实现的 2 种技术。该幻灯片可在此链接上找到: http://gregphplab.com/serendipity/uploads/slides/DB2_PHP_Best_practices.pdf

抱歉,此文档仅提供法语版本。

【讨论】:

【参考方案10】:

有以下可用选项:-

DB2 has several strategies to cope with this problem.
You can use the "scrollable cursor" in feature.
In this case you can open a cursor and, instead of re-issuing a query you can FETCH forward and backward.
This works great if your application can hold state since it doesn't require DB2 to rerun the query every time.
You can use the ROW_NUMBER() OLAP function to number rows and then return the subset you want.
This is ANSI SQL 
You can use the ROWNUM pseudo columns which does the same as ROW_NUMBER() but is suitable if you have Oracle skills.
You can use LIMIT and OFFSET if you are more leaning to a mysql or PostgreSQL dialect.  

【讨论】:

以上是关于DB2 的 LIMIT 等效项的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 限制的 SQL Server 等效项 [重复]

MySQL FOUND_ROWS() 方法的 SQL 等效项是啥?

IGNORE_DUP_KEY 的 DB2 等效项

ClickHouse数据仓库使用之limit实践

AVX2 等效于 std::clamp

怎么在数据库查询中使用LIMIT 参数