你好,游客 登录 注册 搜索
背景:
阅读新闻

Rownum分页故障解决一例

[日期:2014-05-18] 来源:Linux社区  作者:realkid4 [字体: ]

在目前很多系统中,界面数据分页(Page)已经成为一项通用功能。基本上,每种框架、结构和对应的数据库,都有一些成熟的现成解决方案。在Oracle中,rownum伪列是初学者非常容易用错的功能。本篇就介绍一个实际的分页和rownum结合使用时候的故障案例。

(Oracle)Rownum释疑  http://www.linuxidc.com/Linux/2013-05/84637.htm

Oracle数据库学习之Rownum  http://www.linuxidc.com/Linux/2012-09/70061.htm

Oracle 中Rownum用法总结,Rownum 与 ROWID 区别 http://www.linuxidc.com/Linux/2012-07/66121.htm

Oracle利用Rownum和rowid分页 http://www.linuxidc.com/Linux/2012-04/58301.htm

Oracle数据库中Rownum分页 http://www.linuxidc.com/Linux/2012-02/53707.htm

VMware+Linux+Oracle 10G RAC全程详细图解 http://www.linuxidc.com/Linux/2011-02/31976.htm

CentOS 6.4下安装Oracle 11gR2(x64) http://www.linuxidc.com/Linux/2014-02/97374.htm

Oracle 11gR2 在VMWare虚拟机中安装步骤 http://www.linuxidc.com/Linux/2013-09/89579p2.htm

Debian 下 安装 Oracle 11g XE R2 http://www.linuxidc.com/Linux/2014-03/98881.htm

Oracle 11gR2 在VMWare虚拟机中安装步骤 http://www.linuxidc.com/Linux/2013-09/89579.htm

1、问题简述

 

系统后台数据库采用Oracle 11gR2版本。

 

 

SQL> select * from v$version;

 

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

PL/SQL Release 11.2.0.3.0 - Production

CORE11.2.0.3.0Production

TNS for IBM/AIX RISC System/6000: Version 11.2.0.3.0 - Production

NLSRTL Version 11.2.0.3.0 - Production

 

 

数据表inputfile_log记录数据文件日志信息。在界面上提供数据查询、操作和排序。界面提供所有字段的查询排序功能。数据表主键为inputfile_log_seq,一个字段为ISO_CODE

 

在测试阶段,测试人员不时报出某些特定字段在排序过程中有一些问题,排序之后,跳转到下一页数据取值不变。但是,在架构层面,没有明显的故障和Bug存在,前端故障也大都是偶发性的。

 

一个偶然的机会,测试和开发人员定位到了问题的所在:对其他字段进行排序处理之后,分页功能一般不会有故障。只有在ISO_CODE进行排序,之后进行跳转分页的时候,才会有问题,而且问题也是偶发性,不具有必然性。

 

经过定位,发现页面前端输出的SQL语句有一些问题。页面前端采用Hibernate实体映射类。分页采用数据库“真分页”技术——数据库只把要显示的数据传递到前端。Hibernate负责SQL语句的生成执行。

 

定位的SQL语句为:

 

 

select *

from (select a1.inputfile_log_seq, a1.ISO_CODE,rownum rwn

from (select * from inputfile_log t order by t.ISO_CODE) a1

where rownum <= 50)

where rwn > 45;

 

 

Oracle中分页的语句方案很多,这种利用rownum取段的方法也是普遍接受的方法。问题在哪儿呢?下面我们执行语句看结果。

 

 

 

SQL> select *

2from (select a1.inputfile_log_seq, a1.ISO_CODE,rownum rwn

3from (select * from inputfile_log t order by t.ISO_CODE) a1

4where rownum <= 50)

5where rwn > 45;

 

INPUTFILE_LOG_SEQ ISO_CODERWN

----------------- -------- ----------

1901 AU46

1903 AU47

1906 AU48

1142 AU49

1157 AU50

 

 

SQL> select *

2from (select a1.inputfile_log_seq, a1.ISO_CODE, rownum rwn

3from (select * from inputfile_log t order by t.ISO_CODE) a1

4where rownum <= 55)

5where rwn > 50;

 

INPUTFILE_LOG_SEQ ISO_CODERWN

----------------- -------- ----------

1901 AU51

1903 AU52

1906 AU53

1142 AU54

1157 AU55

 

 

看出问题了,在SQL语句中,我们希望看到连续10条记录。但是虽然替换了数据段范围,但是结果集却是相同的。

 

反映到页面上,就是按照ISO_CODE进行排序之后,分页功能失效,点击跳到下一页,但是显示的数据却没有变化。

 

2、分析和实验

 

看似很诡异的问题,难道是Hibernate语句方案有问题?替换为其他的排序字段,问题似乎不存在。

 

 

SQL> select *

2from (select a1.inputfile_log_seq, a1.ISO_CODE,rownum rwn

3from (select * from inputfile_log t order by t.inputfile_log_seq) a1

4where rownum <= 55)

5where rwn > 50;

 

INPUTFILE_LOG_SEQ ISO_CODERWN

----------------- -------- ----------

997 AU51

998 AU52

999 AU53

1000 AU54

1001 AU55

 

SQL>

SQL> select *

2from (select a1.inputfile_log_seq, a1.ISO_CODE,rownum rwn

3from (select * from inputfile_log t order by t.inputfile_log_seq) a1

4where rownum <= 50)

5where rwn > 45;

 

INPUTFILE_LOG_SEQ ISO_CODERWN

----------------- -------- ----------

992 AU46

993 AU47

994 AU48

995 AU49

996 AU50

 

 

排序结果正常。看来问题还是出在ISO_CODE上。

 

条条大路通罗马,换一种方法,试试结果如何呢?Oracle中还有很多其他的分页方案。

 

 

SQL> select *

2from (select a1.inputfile_log_seq, a1.ISO_CODE,rownum rwn

3from (select * from inputfile_log t order by t.ISO_CODE) a1)

4where rwn > 45

5and rwn <= 50;

 

INPUTFILE_LOG_SEQ ISO_CODERWN

----------------- -------- ----------

1316 AU46

1317 AU47

1318 AU48

1319 AU49

1323 AU50

 

SQL>

SQL> select *

2from (select a1.inputfile_log_seq, a1.ISO_CODE, rownum rwn

3from (select * from inputfile_log t order by t.ISO_CODE) a1)

4where rwn > 50

5and rwn <= 55;

 

INPUTFILE_LOG_SEQ ISO_CODERWN

----------------- -------- ----------

1324 AU51

1865 AU52

1624 AU53

1163 AU54

1173 AU55

 

 

不采用问题SQL的两次截取方法,而是将所有的rownum都取出来实体化,再利用范围进行截取。这样做的结果也是正确的。

 

问题出在哪里呢?SQL本质上是一种描述性语句,只要我们正确描述,绝大多数情况是可以将正确的结果返回的。但是这个案例下,描述本身没有什么问题,而且错误出现是偶发性的,令人疑惑。

 

更多详情见请继续阅读下一页的精彩内容http://www.linuxidc.com/Linux/2014-05/101886p2.htm

linux
相关资讯       Rownum分页  Rownum 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款