<!--新Google 468x15 横链接单元开始--> <script type="text/javascript"><!-- google_ad_client = "pub-7343546549496470"; /* 468x15 横链接单元 */ google_ad_slot = "5785741422"; google_ad_width = 468; google_ad_height = 15; //--> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script><!--新Google 468x15 横链接单元结束-->
<!-- Feedsky FEED发布代码开始 --> 欢迎使用RSS订阅本Blog <!-- FEED自动发现标记开始 --> <link title="RSS 2.0" type="application/rss+xml" href="http://feed.feedsky.com/softwave" rel="alternate"> <!-- FEED自动发现标记结束 -->
上面的SQL表示从table1中查出记录,并返回从第2条开始的20条记录(第1条记录从0开始)。
对于其他的数据库,恐怕就没MySQL那么容易查询出记录段了。在SQL Server2005中也提供了类似MySQL的处理方法(可以使用ROW_NUMBER()函数来实现这个功能),SQL语句如下:
虽然上面的SQL语句虽然也可以实现和MySQL一样的功能,但却比MySQL的limit复杂一些。
如果在数据库中提供了实现Web分页的机制,就算复杂一些,也是可以解决的。但有效数据库可能并未提供这种机制。这就得使用更复杂的方法来实现Web分页,如在SQL Server2000中未提供ROW_NUMBER()函数,就有很多开发人员通过编写分页的存储过程来处理。这样做既复杂,又不通用。假设要移植到Oracle上,还得费一番功夫。
在本文给出一种直接使用Web中的Session对象来方式来实现分页的功能,Session是在Web系统中保存当前分话数据的。我们可以想象。分页的难点在哪里,就象MySQL中的limit语句一样,只需要有两个值:起使记录数和要获得的记录总数就可以了。要获得的记录总数这个我们很容易知道,一般就是分一页的记录数。但是起使记录数却很难获得。
如果使用自增键当然可以,但这要建立在表只增不删,而且id从1或一个已知的起始位置开始的情况。如果删除了表中的一些数据,自增键就不再是从1到n,依次递增了。也就是中间可能有空档。如自增键从20至100,中间可能只有10条记录。因此,单纯使用自增键并不能很好地解决分页问题。
但却可以将Session和自增键组合来解决分页问题。大家可以设想,在用户第一次查询时,如select * from table1 where field1 like '%abc%',这时将记录全部查出。假设每页显示50条记录,这时可以从头开始取出50条记录。这不会有任何问题。然后,当用户要查看第2页时,最普通的做是再执行一次上面的SQL语句,然后从第51第记录开始,再取出50条记录。如果这样做,将大大浪费服务器的资源。
为了解决这个问题,可以在每一次执行完上面的SQL语句后,除了取出前50条记录外,再通过记录的定位,将其他页面的起始id值保存在Session中(可以放在List对象中)。然后在用户要查看第2页或后面的页时,直接从Session中取出该页起始id的值,如果使用的是SQL Servlet数据库,可以使用top n,其中n表示每页记录数,来查询当前页的记录。
先拿Java为例来说明一下。下面的代码在Session中记录了第一页到最后一页的起始id:
从上面的代码可以看出,使用ResultSet的absolute来定位记录,并取出当前记录的id值(一个自增字段),并将其保存在Session中。
假设共查询出500条记录,那么Session中保存的id值有可能是下面的样子:
1 51 123 179 229 290 367 567 699
从上面的id值可以看出,中间有断档。但这9个id值之间的记录数都是50个。如下面的SQL语句将查询出50个记录:
假设用户要查看第3页的话,就会取出123和179,并将其加入select 语句的where条件,类似上面的SQL语句。这样用户除了第一次查询外,查看其他页都会只返回当前页面的记录了。
上面的方法还有一些问题,如当第一次返回的记录很多的话,使用absolute方法进行循环所有的记录可能有些慢,那可以在程序中做个约定,只循环41次,也就是保存前40页的记录,当用户要查看第41页的话,再取出第40页的开始记录的id值,将再次查询从该id值往后的所有记录,再记录40页的id值,也就是这时已经有80页的id记录被保存在Session中的。以此类推,
当然,这种方法也不可避免地遇到删除记录的情况,如果用户正在查看页面,这时某一页的记录被删除了,当用户再次要查看这页时,根据Session中保存的id区间,就会得到少于50的记录。在这种情况下,如果使用的是SQL Servlet,就好办一些,可以在where条件中只加id的上限,不加下限,然后使用top关键字来限制查询出的记录数,SQL语句如下:
如果使用的是其他数据库,没有类型top的关键字,可以在查询时多加一个区间,如用户要查询第2页的数据,可以将第2页和第3页的都查出来,这样一般就可以获得超过50条的记录。但如果记录数还不够(这个表的记录被删除的太多了),笔者建议重新查询所有的记录,重新更新一下Session对象中的id值。
总之,本算法就是在第一次查询时预先将后面页面的起始记录的id值事先保存起来,然后等待以后查看其他页面时使用。如果这时某个页面的记录被删除(如果当前页面记录数不足页面记录总数,被示为有记录删除),可以重新更新一下Session中的id值,然后根据新的id值再查一遍。但要注意的是这个id值最好使用数据库的自增型字段(一般的数据库,甚至桌面数据库都会有自增型字段类型)。为了尽量避免总更新Session中的id值,可以在查询一个页面时查询出两个页面的记录,这样在一般情况下,会保证记录数超过页面记录总数。但这样做一个缺点,就是可能两个相邻页面的记录有一定的重复。不过并没有太大影响。我们在网上看某些论坛的贴子时,有时可能也会发现两个相邻页面的记录有重复。
本分页方法适合于所有的数据库,无论是网络数据库(Oracle、SQL Servlet、DB2等),以及桌面数据库(access、paradox、pdf等)。并且不需要在数据库中建立额外的资源,如存储过程等。(当然,每个表需要有一个自增类型字段,这一点很关键)。
哪位读者有更好,更通用的分页方法(最好不要在数据库中建立象存储过程一样的资源,尽量不要使用与数据库相关的语句,如SQL Server中的top),请跟贴。
摘自【http://www.blogjava.net/nokiaguy/archive/2008/08/23/223881.html】
<!--Google 468*60横幅广告开始--><script type="text/javascript"><!-- google_ad_client = "pub-7343546549496470"; google_ad_width = 468; google_ad_height = 60; google_ad_format = "468x60_as"; google_ad_type = "image"; //2007-07-26: CSDN google_ad_channel = "6063905817"; google_color_border = "6699CC"; google_color_bg = "E6E6E6"; google_color_link = "FFFFFF"; google_color_text = "333333"; google_color_url = "AECCEB"; google_ui_features = "rc:6"; //--> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script><!--Google 468*60横幅广告结束--><!--新Google 468*60横幅广告开始--><script type="text/javascript"><!-- google_ad_client = "pub-7343546549496470"; /* 468x60, 创建于 08-8-6 */ google_ad_slot = "7368701459"; google_ad_width = 468; google_ad_height = 60; //--> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script><!--新Google 468*60横幅广告结束-->
<!-- Google Reader shared发布代码开始 --><script type="text/javascript" src="http://www.google.com/reader/ui/publisher.js"></script><script type="text/javascript" src="http://www.google.com/reader/public/javascript/user/00697638153916680411/state/com.google/broadcast?n=5&callback=GRC_p(%7Bc%3A%22green%22%2Ct%3A%22%5Cu8FD9%5Cu4E9B%5Cu6587%5Cu7AE0%5Cu4E5F%5Cu503C%5Cu5F97%5Cu4E00%5Cu770B%22%2Cs%3A%22false%22%7D)%3Bnew%20GRC"></script><!-- Google Reader shared发布代码结束 -->
<!-- Feedsky FEED发布代码开始 --> 欢迎使用RSS订阅本Blog <!-- FEED自动发现标记开始 --> <link title="RSS 2.0" type="application/rss+xml" href="http://feed.feedsky.com/softwave" rel="alternate"> <!-- FEED自动发现标记结束 -->
用Session和自增键实现通用Web分页功能
Web系统虽然现在很流行,但是分页问题一直长期困扰着Web系统的开发人员。对于不同的数据库,可能开发人员对分页的处理分有很大差别。个人认为,使用MySQL开发Web系统的程序员是感到最舒服的,因为,在MySQL中提供了limit语句,可以获得查询结果的一段数据。如下面的SQL语句所示:
- select*fromtable1limit1,20
上面的SQL表示从table1中查出记录,并返回从第2条开始的20条记录(第1条记录从0开始)。
对于其他的数据库,恐怕就没MySQL那么容易查询出记录段了。在SQL Server2005中也提供了类似MySQL的处理方法(可以使用ROW_NUMBER()函数来实现这个功能),SQL语句如下:
- WithtAS
- (
- SELECTcontactid,namestyle,lastname,
- ROW_NUMBER()over(orderbynamestyle) as RowNumber
- FROMPerson.Contact
- )
- select*fromt
- WhereRowNumberBetween20and30
如果在数据库中提供了实现Web分页的机制,就算复杂一些,也是可以解决的。但有效数据库可能并未提供这种机制。这就得使用更复杂的方法来实现Web分页,如在SQL Server2000中未提供ROW_NUMBER()函数,就有很多开发人员通过编写分页的存储过程来处理。这样做既复杂,又不通用。假设要移植到Oracle上,还得费一番功夫。
在本文给出一种直接使用Web中的Session对象来方式来实现分页的功能,Session是在Web系统中保存当前分话数据的。我们可以想象。分页的难点在哪里,就象MySQL中的limit语句一样,只需要有两个值:起使记录数和要获得的记录总数就可以了。要获得的记录总数这个我们很容易知道,一般就是分一页的记录数。但是起使记录数却很难获得。
如果使用自增键当然可以,但这要建立在表只增不删,而且id从1或一个已知的起始位置开始的情况。如果删除了表中的一些数据,自增键就不再是从1到n,依次递增了。也就是中间可能有空档。如自增键从20至100,中间可能只有10条记录。因此,单纯使用自增键并不能很好地解决分页问题。
但却可以将Session和自增键组合来解决分页问题。大家可以设想,在用户第一次查询时,如select * from table1 where field1 like '%abc%',这时将记录全部查出。假设每页显示50条记录,这时可以从头开始取出50条记录。这不会有任何问题。然后,当用户要查看第2页时,最普通的做是再执行一次上面的SQL语句,然后从第51第记录开始,再取出50条记录。如果这样做,将大大浪费服务器的资源。
为了解决这个问题,可以在每一次执行完上面的SQL语句后,除了取出前50条记录外,再通过记录的定位,将其他页面的起始id值保存在Session中(可以放在List对象中)。然后在用户要查看第2页或后面的页时,直接从Session中取出该页起始id的值,如果使用的是SQL Servlet数据库,可以使用top n,其中n表示每页记录数,来查询当前页的记录。
先拿Java为例来说明一下。下面的代码在Session中记录了第一页到最后一页的起始id:
- //rs为记录集,其他语言的操作类似
- ResultSetrs=stmt.executeQuery( "select*fromtable1wherefield1like'%abc%'" );
- int n=1;
- while (rs.absolute(n))
- {
- int id=rs.getInt(id)
- //将id保存在Session中
- n+=50;
- }
假设共查询出500条记录,那么Session中保存的id值有可能是下面的样子:
1 51 123 179 229 290 367 567 699
从上面的id值可以看出,中间有断档。但这9个id值之间的记录数都是50个。如下面的SQL语句将查询出50个记录:
- select*fromtable1wherefield1like '%abc%' and(id>=290andid<367)
上面的方法还有一些问题,如当第一次返回的记录很多的话,使用absolute方法进行循环所有的记录可能有些慢,那可以在程序中做个约定,只循环41次,也就是保存前40页的记录,当用户要查看第41页的话,再取出第40页的开始记录的id值,将再次查询从该id值往后的所有记录,再记录40页的id值,也就是这时已经有80页的id记录被保存在Session中的。以此类推,
当然,这种方法也不可避免地遇到删除记录的情况,如果用户正在查看页面,这时某一页的记录被删除了,当用户再次要查看这页时,根据Session中保存的id区间,就会得到少于50的记录。在这种情况下,如果使用的是SQL Servlet,就好办一些,可以在where条件中只加id的上限,不加下限,然后使用top关键字来限制查询出的记录数,SQL语句如下:
- selecttop50*fromtable1wherefield1like '%abc%' andid>=290
总之,本算法就是在第一次查询时预先将后面页面的起始记录的id值事先保存起来,然后等待以后查看其他页面时使用。如果这时某个页面的记录被删除(如果当前页面记录数不足页面记录总数,被示为有记录删除),可以重新更新一下Session中的id值,然后根据新的id值再查一遍。但要注意的是这个id值最好使用数据库的自增型字段(一般的数据库,甚至桌面数据库都会有自增型字段类型)。为了尽量避免总更新Session中的id值,可以在查询一个页面时查询出两个页面的记录,这样在一般情况下,会保证记录数超过页面记录总数。但这样做一个缺点,就是可能两个相邻页面的记录有一定的重复。不过并没有太大影响。我们在网上看某些论坛的贴子时,有时可能也会发现两个相邻页面的记录有重复。
本分页方法适合于所有的数据库,无论是网络数据库(Oracle、SQL Servlet、DB2等),以及桌面数据库(access、paradox、pdf等)。并且不需要在数据库中建立额外的资源,如存储过程等。(当然,每个表需要有一个自增类型字段,这一点很关键)。
哪位读者有更好,更通用的分页方法(最好不要在数据库中建立象存储过程一样的资源,尽量不要使用与数据库相关的语句,如SQL Server中的top),请跟贴。
摘自【http://www.blogjava.net/nokiaguy/archive/2008/08/23/223881.html】
<!--Google 468*60横幅广告开始--><script type="text/javascript"><!-- google_ad_client = "pub-7343546549496470"; google_ad_width = 468; google_ad_height = 60; google_ad_format = "468x60_as"; google_ad_type = "image"; //2007-07-26: CSDN google_ad_channel = "6063905817"; google_color_border = "6699CC"; google_color_bg = "E6E6E6"; google_color_link = "FFFFFF"; google_color_text = "333333"; google_color_url = "AECCEB"; google_ui_features = "rc:6"; //--> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script><!--Google 468*60横幅广告结束--><!--新Google 468*60横幅广告开始--><script type="text/javascript"><!-- google_ad_client = "pub-7343546549496470"; /* 468x60, 创建于 08-8-6 */ google_ad_slot = "7368701459"; google_ad_width = 468; google_ad_height = 60; //--> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script><!--新Google 468*60横幅广告结束-->
<!-- Google Reader shared发布代码开始 --><script type="text/javascript" src="http://www.google.com/reader/ui/publisher.js"></script><script type="text/javascript" src="http://www.google.com/reader/public/javascript/user/00697638153916680411/state/com.google/broadcast?n=5&callback=GRC_p(%7Bc%3A%22green%22%2Ct%3A%22%5Cu8FD9%5Cu4E9B%5Cu6587%5Cu7AE0%5Cu4E5F%5Cu503C%5Cu5F97%5Cu4E00%5Cu770B%22%2Cs%3A%22false%22%7D)%3Bnew%20GRC"></script><!-- Google Reader shared发布代码结束 -->