ROWNUM
在 Oracle 中不像LIMIT
在 MySQL 中。
在MySQL中,
该LIMIT
子句可用于约束SELECT
语句返回的行数... 有两个参数,第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数。初始行的偏移量为 0(不是 1):
SELECT * FROM tbl LIMIT 5,10; # Retrieve rows 6-15
在甲骨文中,
对于查询返回的每一行,ROWNUM
伪列返回一个数字,指示 Oracle 从表或连接行集中选择行的顺序。选择的第一行的 aROWNUM
为 1,第二行的 a 为 2,依此类推。
SELECT *
FROM (SELECT * FROM employees ORDER BY employee_id)
WHERE ROWNUM < 11;
在前面的示例中,这些ROWNUM
值是顶级SELECT
语句的值,因此它们是在子查询中已经按employee_id 对行进行排序之后生成的。
所以你的代码的问题是:
WHERE ROWNUM BETWEEN :startrow AND :perpage
逻辑上不正确,因为ROWNUM
是行索引指示符,而不是“行数”指示符。ROWNUM BETWEEN a AND b
表示返回从 a 到 b 排序的行,例如第 1 行到第 3 行,而不是第 1 行和第 3 行。
因此,即使ROWNUM BETWEEN a AND b
有效(实际上没有),从逻辑上讲,要获取查询的“第 2 页” ROWNUM BETWEEN :startrow AND :endrow
,例如ROWNUM BETWEEN 4 AND 6
;
ROWNUM BETWEEN :startrow AND :endrow
实际上不适用于大于 1 的起始行,因为它是一个伪列,表示所选集中ROWNUM
行的顺序,
所以当你执行例如SELECT...WHERE ROWNUM BETWEEN 4 AND 6
,
- 第一场比赛,Oracle 想,“rownum 会在 4 到 6 之间吗?” 因为还没有选中行,所以选中
ROWNUM
后为1 ,所以Oracle放弃;
- 第二场比赛,“这会是 4 比 6 吗?” 否,因为没有选择任何行,所以这仍然是 1;
- 第三场比赛,相同;
- ...
根本不会有结果。
要获得所需的输出,您必须将查询包装在子查询中,并检查外部的行号,如下所示:
SELECT * FROM
(SELECT ROWNUM rn, t.*
FROM
(SELECT ...
FROM ...
WHERE ...
ORDER BY ...) t
)
WHERE rn>=:startrow AND rn<=:endrow
以保证在检查行顺序之前确定行顺序。
- 关于编码风格,由于 OCI 支持预处理语句和变量绑定,你应该使用它:
绑定允许数据库重用语句上下文和先前执行语句的缓存,...绑定减少了 SQL 注入问题,因为与绑定变量关联的数据永远不会被视为 SQL 语句的一部分。
$statement=oci_parse("... WHERE rn>=:start AND rn<=:end");
oci_bind_by_name($statement,":start",$start,-1,SQLT_INT);
oci_bind_by_name($statement,":end",intval($start+$per_page-1),-1,SQLT_INT);
oci_execute($statement);