thought persistence
简单,再简单一点
博客园
首页
新随笔
联系
订阅
管理
15 Posts :: 0 Stories :: 142 Comments :: 1 Trackbacks
与我联系
发短消息
搜索
常用链接
我的随笔
我的空间
我的短信
我的评论
更多链接
我的参与
我的新闻
最新评论
我的标签
留言簿
(27)
给我留言
查看留言
我管理的小组
SaaS
数据访问
我参加的小组
机器人的世界
创业交流
博客园上海俱乐部
图灵俱乐部
随笔档案
(15)
2007年9月 (1)
2007年8月 (1)
2007年7月 (3)
2005年1月 (3)
2004年12月 (2)
2004年11月 (1)
2004年9月 (1)
2004年7月 (1)
2004年6月 (2)
blog
VB的一些老东西
VB的一些老东西 editgrid 源码在这里找
那个谁谁谁
伟大的HeyBrain
伟大的HeyBrain 记录生活的点滴
积分与排名
积分 - 45231
排名 - 985
最新评论
阅读排行榜
1. 我们需要什么样的字段类型?(3665)
2. SQLite 3 (alpha) 的新特性(3140)
3. .Net 代码混淆的一些注意事项(2954)
4. 自动代码生成和VBA脚本(2885)
5. 关于分页这点事 -- 如何才能分得准(2663)
6. 我所认识的O/R Mapping 之 XPO(2513)
7. 关于ORM性能测试的一点意见(2375)
8. VB到底可以做什么? (Viking compiler)(2292)
9. 再续:ORM性能测试用例 (2133)
10. Comega -- O/R Mapper的终结者?(2133)
评论排行榜
1. 功夫 、Persistore 及其它(24)
2. 关于ORM性能测试的一点意见(19)
3. 关于分页这点事 -- 如何才能分得准(17)
4. VB到底可以做什么? (Viking compiler)(15)
5. SQLite 3 (alpha) 的新特性(15)
6. Comega -- O/R Mapper的终结者?(14)
7. 我所认识的O/R Mapping 之 XPO(11)
8. 我们需要什么样的字段类型?(9)
9. 对象化查询之OCL vs. OPath(4)
10. 关于索引,我们可以多知道一些 -- 全表扫描和索引扫描(3)
关于分页这点事 -- 如何才能分得准
一直以来, 大家热衷于存储过程分页谁分得快, 但是我们搜索一下"通用分页存储过程", 就会发现这个有问题的方法流传得颇广
对于通用查询的解决方案, 两层TOP
SELECT * FROM
(
SELECT TOP(PageSize) * FROM
(
SELECT TOP (PageSize * (PageIndex + 1)) *
FROM table
ORDER BY field DESC
)
ORDER BY field ASC
)
ORDER BY field DESC
这个号称"掐头去尾"的分页方法, 其实有很多问题
首先就是, 在最后一页时, 如果符合此页的数据不足PageSize时, 会取到前一页的部分数据, 因为你总是取到了PageSize条记录呢, 解决办法呢当初是先计算ItemCount, 再决定外面这层Top的数量.
其次, 当orderby field不是unique的时候(这种情况很常见, 因为web上很多grid的都有点击列头排序的功能), 第二次的Top未必就是子查询里的最后那些数据.
为什么会是这样呢, 我们知道, 对于任何两次查询, sql server返回的数据顺序是一样的, 但是对一个子查询做倒排序, 得到的结果集未必就是原始结果集的倒置
举个例子:
create table test (a varchar(10), b varchar(10))
insert into test values ('a','1')
insert into test values ('b','1')
insert into test values ('c','3')
insert into test values ('d','4')
insert into test values ('e','4')
insert into test values ('f','4')
select top 6 * from test order by b asc
a b
--------------------
b 1
a 1
c 3
f 4
e 4
d 4
我们想着倒着再来一次top 2应该是e d两条吧, 那么来吧
select top 2 * from
(select top 6 * from test
order by b asc)
as a order by b desc
a b
--------------------
f 4
e 4
很不幸, 实际结果是f, e
也就是每次它排序的结果是确定的, 但倒排序的时候未必是按正排序的反序来排列数据的, 而上述分页方法恰恰是建立在这个不成立的假设上的, 所以对于order by field不唯一的查询, 这种分页方法一定不对
那么有没有解决方案呢? 答案也是肯定的, 把主键或其它唯一性字段加到order by中去
select * from test
select top 2 * from
(select top 6 * from test
order by b asc,a desc)
as a order by b desc,a asc
这样就暂时OK了, 但是, 我们知道有时候查询结果确实会因为join的存在, 而出现根本无法给出唯一性字段(当然这种情况非常少), 这时这个分页方案就彻底没戏了
再来看一个查询方案, not in
SELECT TOP (PageSize) *
FROM table
WHERE (ID NOT IN
(SELECT TOP (PageSize * PageIndex) id
FROM table
ORDER BY field))
ORDER BY field
这个没问题了, 因为始终依据的是同一种排序方法, 对于最后一页的查询, 也同样能够给出正确的记录数, 但是我们要看到这种分页方法的前提是需要给出唯一性字段ID来做not in操作的, 这是它的局限性
不妨多看一个类似的, > max
SELECT TOP (PageSize) *
FROM table
WHERE (ID >
(SELECT MAX(ID)
FROM (SELECT TOP (PageSize * PageIndex) id
FROM table
ORDER BY field) AS T))
ORDER BY field
同样, 需要ID的存在, 如果只是用order by field来代替ID, 那么结果就错得很离谱了, 因为可能大量的field = max(field)的值还没有被显示, 就被无情的>掉了
顺便看一下sql server2005的CTE(common table expression)的方法, 其它说是CTE方法不准确, 应该说row_number函数好一些
with t as(
select row_number() over(order by field) as rownum, * from table
) select * from t where rownum .....
CTE改进的方法同样需要有ID这样的唯一字段的存在
with t as(
select row_number() over(order by field) as rownum, id from table
) select a.* from table a inner join t on a.id = b.id where t.rownum .....
我们这里不研究效率, 因为效率这里有人研究:
分页实现方法的性能比较
, 这种方法就不会出现分不准的问题了, 对于无唯一字段的查询也可以解决, 为什么我说这种不能叫CTE分页法呢, 因为这样写也是可以的:
select *from( select row_number() over(order by field) as rownum, * from table ) as a where a.rownum ....
顺便我比对了一下, 和CTE的执行计划是一样的
那么在sql server2000中, 没有唯一性字段下如何要分得准? 临时表, identity字段, 这种方法网上一搜也是遍地都是
posted on 2007-07-03 21:44
progame
阅读(2663)
评论(17)
编辑
收藏
Feedback
#1楼
2007-07-03 22:30
aspnet2007 [未注册用户]
一直用这个方法,感觉还不错:SELECT TOP (PageSize) *
FROM table
WHERE (ID NOT IN
(SELECT TOP (PageSize * PageIndex) id
FROM table
ORDER BY field))
ORDER BY field
回复
引用
#2楼
2007-07-03 22:44
Artech
大不了再Stored Procedure中加上一个通过获得总页数判断是否是最后一页的好了。在数据量不是非常大的话,性能也不会有太大的影响。这是最直接的方式。
在这方面Oracle提供Rownum关键字就很好地解决了这个问题。
回复
引用
查看
#3楼
2007-07-03 22:56
YAO.NET(三千)℡
临时表吧.
回复
引用
查看
#4楼
2007-07-03 23:32
冬冬
学习了
回复
引用
查看
#5楼
2007-07-04 01:01
vboy
大侠呀,好久不见你在这里发文章了,www.progame.org也没了
回复
引用
查看
#6楼
2007-07-04 01:11
维生素C.NET
学习
回复
引用
查看
#7楼
2007-07-04 08:48
JerryChou
这点事还有必要说吗
回复
引用
查看
#8楼
2007-07-04 09:13
非我
很有必要,虽然一般情况下看不出来分的对不对。
回复
引用
查看
#9楼
2007-07-04 09:45
曲滨*銘龘鶽
sql server2000
本来就不是一个对 "Web 应用特性" 有任何优化的数据库。所以有很多地方很郁闷的如分页,因为 sql server2000 出来的时候Web还不是很火,多半的应用还是 win32 的天下哪
不过即使到了 SQL2005 row_number(),也没oracle 的
number 好用,哎......
期待 SQL2008 有所加强......
回复
引用
查看
#10楼
2007-07-04 09:54
Anders Liu
原来的两层top其实已经足够了。应该避免在非unique列上做分页。
回复
引用
查看
#11楼
2007-07-04 09:58
laifangsong
分析的很全面,:)
回复
引用
查看
#12楼
2007-07-04 10:07
henry
又是分页...
正常来说分页不准已经是逻辑上的错误。
其实对于少量的数据分页其实根据没有必要计较用什么方法(小量数据体现不了差距)。可能有些人说那我有上千万的数据怎办,如果你把上千W的分页体现在应用上,那就容易产生数据库服务器安全问题;假设google做上千万或更多的分页结果页,那它可能面对一个非常严重的问题。
回复
引用
查看
#13楼
2007-07-04 14:28
Robert [未注册用户]
这样的分页是用问题的,MS SQL有时会让你多几条记录
,比如每页20条,你可能会有时得到25条
回复
引用
#14楼
2007-07-04 15:39
阿毅 [未注册用户]
个人认为“通用分页存储过程”是很不明智的“偷懒”
考虑数据量,查询复杂度,索引配合状况,结果集的散列程度,存储方式,等等,根本没有一劳永逸的方法。
回复
引用
#15楼
2007-07-04 20:40
chy710 [未注册用户]
mysql中的limit a,b 更加灵活...
回复
引用
#16楼
2007-07-05 07:58
在线代理 [未注册用户]
我就就用两层top,容易理解。
回复
引用
#17楼
2007-07-05 09:08
路人甲 [未注册用户]
还有一种方案是这样的,用Groupby,不过效率不高,能保证每条记录唯一性字段的情况下,也很准确
select * from
(
select * from (select top 20*4 唯一ID,其他字段 from 表集 where 条件 order by 排序) as a
union all
select * from (select top 20*5 唯一ID,其他字段 from 表集 where 条件 order by 排序) as b
)
a
group by 唯一ID,其他字段 having count(唯一ID)=1 order by 排序
回复
引用
新用户注册
刷新评论列表
标题
姓名
主页
Email
(博主才能看到)
验证码
*
看不清,换一张
[
登录
][
注册
]
内容(请不要发表任何与政治相关的内容)
网站首页
新闻频道
社区
小组
博问
网摘
闪存
找找看
Remember Me?
登录
使用高级评论
新用户注册
返回页首
恢复上次提交
[使用Ctrl+Enter键可以直接提交]
该文被作者在 2007-07-03 21:49 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索
相关链接:
最新IT新闻:
Silverlight对Flash 微软打垮Adobe
IBM扩大研究规模 在上海成立研究院
eWeek评Google八大“20%项目”
微软终于发布Silverlight 2正式版
微软首次打破先例 Windows 7不再改名
Powered by:
博客园
Copyright © progame