海鸥航迹

学习之笔记,好文之收集。

实现千万级数据的分页显示--整理资料并测试

原帖子如下:

/*
 名称:spAll_DeleteNoneUnique
 输入:要查询的表名和字段列表
 输出:
 调用:
 说明:实现千万级数据的分页显示!--可以在5秒内获取1448万条记录里的第1200页的100条记录,雄不?
 作者:铁拳
 邮件:
 网站:http://www.wellknow.net
 更新:20040610
 支持:http://bbs.wellknow.net
 版权:转述时请注明来源:用思维创造未来的Wellknow.net
*/


CREATE PROCEDURE GetRecordFromPage
    @tblName      
varchar(255),       -- 表名
    @fldName      varchar(255),       -- 字段名
    @PageSize     int = 10,           -- 页尺寸
    @PageIndex    int = 1,            -- 页码
    @IsCount      bit = 0,            -- 返回记录总数, 非 0 值则返回
    @OrderType    bit = 0,            -- 设置排序类型, 非 0 值则降序
    @strWhere     varchar(1000= ''  -- 查询条件 (注意: 不要加 where)
AS

declare @strSQL   varchar(6000)       -- 主语句
declare @strTmp   varchar(100)        -- 临时变量
declare @strOrder varchar(400)        -- 排序类型

if @OrderType != 0
begin
    
set @strTmp = "<(select min"
    
set @strOrder = " order by [" + @fldName +"] desc"
end
else
begin
    
set @strTmp = ">(select max"
    
set @strOrder = " order by [" + @fldName +"] asc"
end

set @strSQL = "select top " + str(@PageSize) + " * from ["
    + @tblName + "
] where [" + @fldName + "]+ @strTmp + "(["
    + @fldName + "
]from (select top " + str((@PageIndex-1)*@PageSize) + " ["
    + @fldName + "
] from [" + @tblName + "]+ @strOrder + ") as tblTmp)"
    
+ @strOrder

if @strWhere != ''
    
set @strSQL = "select top " + str(@PageSize) + " * from ["
        + @tblName + "
] where [" + @fldName + "]+ @strTmp + "(["
        + @fldName + "
]from (select top " + str((@PageIndex-1)*@PageSize) + " ["
        + @fldName + "
] from [" + @tblName + "] where " + @strWhere + " "
        
+ @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrder

if @PageIndex = 1
begin
    
set @strTmp = ""
    
if @strWhere != ''
        
set @strTmp = " where " + @strWhere

    
set @strSQL = "select top " + str(@PageSize) + " * from ["
        + @tblName + "
]+ @strTmp + " " + @strOrder
end

if @IsCount != 0
    
set @strSQL = "select count(*as Total from [" + @tblName + "]"

exec (@strSQL)

GO



修改后,便于使用的代码:


修改一点点便于其他人使用 
CREATE PROCEDURE GetRecordFromPage 
@tblName 
varchar(255), -- 表名 
@fldName varchar(255), -- 字段名 
@OrderfldName varchar(255), -- 排序字段名 
@StatfldName varchar(255), -- 统计字段名 
@PageSize int = 10-- 页尺寸 
@PageIndex int = 1-- 页码 
@IsCount bit = 0-- 返回记录总数, 非 0 值则返回 
@OrderType bit = 0-- 设置排序类型, 非 0 值则降序 
@strWhere varchar(1000= '' -- 查询条件 (注意: 不要加 where) 
AS 

declare @strSQL varchar(6000-- 主语句 
declare @strTmp varchar(100-- 临时变量 
declare @strOrder varchar(400-- 排序类型 

if @OrderType != 0 
begin 
set @strTmp = "<(select min
set @strOrder = " order by [" + @OrderfldName +"] desc
end 
else 
begin 
set @strTmp = ">(select max
set @strOrder = " order by [" + @OrderfldName +"] asc
end 

set @strSQL = "select top " + str(@PageSize) + " " + @fldName + " from [
+ @tblName + "
] where [" + @OrderfldName + "]+ @strTmp + "([
+ @OrderfldName + "
]from (select top " + str((@PageIndex-1)*@PageSize) + " [
+ @OrderfldName + "
] from [" + @tblName + "]+ @strOrder + ") as tblTmp)" 
+ @strOrder 

if @strWhere != '' 
set @strSQL = "select top " + str(@PageSize) + " " + @fldName + " from [
+ @tblName + "
] where [" + @OrderfldName + "]+ @strTmp + "([
+ @OrderfldName + "
]from (select top " + str((@PageIndex-1)*@PageSize) + " [
+ @OrderfldName + "
] from [" + @tblName + "] where " + @strWhere + " " 
+ @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrder 

if @PageIndex = 1 
begin 
set @strTmp = "" 
if @strWhere != '' 
set @strTmp = " where " + @strWhere 

set @strSQL = "select top " + str(@PageSize) + " " + @fldName + " from [
+ @tblName + "
]+ @strTmp + " " + @strOrder 
end 

if @IsCount != 0 
set @strSQL = "select count(" + @StatfldName + ") as Total from [" + @tblName + "]

exec (@strSQL) 

GO 


说明:
增加了下面两个部分,其他人拷贝去可根据自己需要进行设定。
@OrderfldName varchar(255), -- 排序字段名
@StatfldName varchar(255), -- 统计字段名

fldName作用由排序转变为控制需要打开的字段。
@fldName varchar(255), -- 字段名

----------------------------------------------
个人测试结果:
通过测试,性能并没有原帖子所说的好。但这可能是机器原因,要知道,在SQL Server中,2千万条空记录大约要占用3G左右的空间,而插入这2千万条记录,在我的测试平台上耗费了近10个小时,内存占用从125M增长到350M左右。我实际测试了一下,在建立索引的情况下,执行一次根据主键,返回一条记录的查询,在512M内存,2.2G CPU,20G单分区存储数据库文件,100M局域网的配置情况下,平均大约需要15s左右(这只是我的个人测试,不具有任何实质性意义。)。

另外我发现,对于上述配置的机器,运行SQL Server时,在百万级别以下的表中执行查询--(索引良好,没有坏点,或者最新索引),速度差别不大,但达到8位数以上,也就是千万以上时候,SQL Server表现就不太好了,当然,这与机器配置有很大关系了。但无可否认,SQL Server  2000还不是企业级数据库的最佳选择,在Oracle执行类似的查询,性能要好于SQL Server。插入执行完毕的时间是4个小时,内存占用一直没有变化:400M。执行相同查询,需要的时间是10s左右。

小弟最近忙于一个公积金项目,对于部门数据库、中央数据库的调度进行了一些评估,Oracle软件+SUN/HP的硬件仍然占了中国政府机构服务器的大部分市场,再就是IBM,占据了高端和甚高端的大部分业务。

微软在这方面打个胜仗,还有很长的路要走--个人认为,和硬件公司合作研发相配套硬件是不二法门。

posted on 2004-10-23 12:26 海天一鸥 阅读(5091) 评论(12) 编辑 收藏

评论

#1楼 2004-10-24 02:35 myxname      

INSERT INTO table1(Catagory, LockTop, name, age, sex, data1, updatetime)select top 5000000 1, 0, '测试数据', 99, 0, 'testtesttesttesttesttesttesttesttesttest', GETDATE() from syscolumns a,syscolumns b,syscolumns c

这样快速插入数据:)
 回复 引用 查看   

#2楼 2004-12-15 14:58 asdf

你的复杂而效率不高,SQL SERVER 对 [IN] 的支持很好,这样改一下的话可以在2秒以下。  回复 引用   

#3楼[楼主] 2005-01-21 15:35 海天一鸥      

/*
功能描述: 通用分页显示查询
如果有自增标识字段,在@strGetFields中不要加入此字段信息,
如果非要加入的话,要 (fldName + 0) AS fldName 这样处理;
输入参数:
@tblName: 表名
@strGetFields: 需要返回的列 '*':返回所以列信息
@PageSize: 页尺寸
@PageIndex: 页码
@doCount: 返回记录总数, 非 0 值则返回
@strOrderBy: 排序字段信息,(注意: 不要加 ORDER BY)
格式: Field1 DESC, Field2 ASC
@strWhere: 查询条件,(注意: 不要加 WHERE)
输出参数: @RecordCount: 记录总数
作 者: ningfeiyang
创建时间: 2005-01-21
更改纪录:
*/
ALTER PROCEDURE Pagination2
(
@tblName varchar(255),
@strGetFields varchar(1000) = '*',
@PageSize int = 10,
@PageIndex int = 1,
@doCount bit = 0,
@strOrderBy varchar(500) = '',
@strWhere varchar(1500) = '',
@RecordCount int output
)
AS
-- 主语句
DECLARE @strSQL varchar(5000) SET @strSQL = ''
-- 排序变量
DECLARE @strOrder varchar(400) SET @strOrder = ''

SET @RecordCount = 0
--如果@doCount传递过来的不是0,就执行总数统计
IF (@doCount != 0)
BEGIN
DECLARE @sWhere varchar(2000)

SET @sWhere = ''
IF (@strWhere != '')
SET @sWhere = ' WHERE ' + @strWhere

SET @strSQL = 'if exists (select * from dbo.sysobjects where id = object_id(''[dbo].[tmpTable]'') and OBJECTPROPERTY(id, ''IsUserTable'') = 1) '
SET @strSQL = @strSQL + ' UPDATE tmpTable SET Total = (SELECT COUNT(*) FROM [' + @tblName + '] ' + @sWhere + ') '
SET @strSQL = @strSQL + ' ELSE SELECT COUNT(*) AS Total INTO tmpTable FROM [' + @tblName + '] ' + @sWhere

EXEC (@strSQL)

SELECT @RecordCount=Total FROM tmpTable

--删除总数统计临时表
EXEC ('DROP TABLE tmpTable')
END

PRINT @RecordCount

--排序字段信息
IF (@strOrderBy != '')
SET @strOrder = ' ORDER BY ' + @strOrderBy
--如果是第一页就执行以上代码,这样会加快执行速度
IF (@PageIndex = 1)
BEGIN
IF (@strWhere != '')
SET @strSQL = 'SELECT TOP ' + str(@PageSize) + ' ' + @strGetFields + ' FROM [' + @tblName + '] WHERE ' + @strWhere + @strOrder
ELSE
SET @strSQL = 'SELECT TOP ' + str(@PageSize) + ' ' + @strGetFields + ' FROM ['+ @tblName + '] '+ @strOrder
END
ELSE
BEGIN
--为搜索表建立自动编号 保存到临时表中
SET @strSQL = 'SELECT TOP ' + str(@PageIndex*@PageSize) + ' IDENTITY(int,1,1) AS IID, ' + @strGetFields + ' INTO #tmpTable FROM [' + @tblName + ']'
IF (@strWhere != '')
SET @strSQL = @strSQL + ' WHERE ' + @strWhere + @strOrder
ELSE
SET @strSQL = @strSQL + @strOrder

--以下代码赋予了@strSQL以真正执行的SQL代码
SET @strSQL = @strSQL + ' SELECT ' + @strGetFields + ' FROM #tmpTable WHERE IID > ' + str((@PageIndex-1)*@PageSize) + ' DROP TABLE #tmpTable'
END

PRINT @strSQL

--执行分页查询
EXEC (@strSQL)
 回复 引用 查看   

#4楼 2005-08-03 16:22 路人[未注册用户]

if @IsCount != 0
set @strSQL = "select count(" + @StatfldName + ") as Total from [" + @tblName + "]"
 回复 引用   

#5楼 2005-08-03 16:29 路人[未注册用户]

if @IsCount != 0
set @strSQL = "select count(" + @StatfldName + ") as Total from [" + @tblName + "]"
有点小问题:就是在给定where语句下,统计的记录数不是经过选择后的,仍然是给定表所有记录数。
可修改为:
if @IsCount != 0
set @strSQL = "select count(" + @StatfldName + ") as Total from [" + @tblName + "]" + ' WHERE ' + @strWhere

该存储过程还有一个问题就是: 当想对两个或两个以上的表联合查询获得的结果集,进行分页是无能为力的。
 回复 引用   

#6楼 2005-09-14 16:55 天道酬勤[未注册用户]

我觉得应该修改为:
if @IsReCount != 0
set @strSQL = @strSQL+' select count(' + @StatfldName + ') as Total from [' + @tblName + ']'

if @strWhere!=''
set @strSQL = @strSQL+' where ' + @strWhere

--1句可以同时获得数据和记录总数
--2句就是可以防止条件为空的情况
 回复 引用   

#7楼 2005-09-14 17:10 天道酬勤 [未注册用户]

还有一个问题,
就是排序的话,如果不是主键的时候,在最后几页得到的记录不全,也就是说如果设定一页是10条的话,怎么成了8条了,只是最后几页,不知道为什么?
 回复 引用   

#8楼 2007-12-23 21:24 蛙蛙池塘      

你好,刚看到你这个帖子,你正文里和你评论里的两个存储过程到底哪个效率更好一些呢。  回复 引用 查看   

#9楼 2007-12-23 21:46 蛙蛙池塘      


(1 行受影响)
100
SELECT TOP 20 IDENTITY(int,1,1) AS IID, SubJectId, Title, PostTime,PostId INTO #tmpTable FROM [D_Posts] ORDER BY PostTime Desc SELECT SubJectId, Title, PostTime,PostId FROM #tmpTable WHERE IID > 10 DROP TABLE #tmpTable
消息 8108,级别 16,状态 1,第 1 行
无法使用 SELECT INTO 语句将标识列添加到表 '#tmpTable',该表的列 'PostId' 已继承了标识属性。

(1 行受影响)

出错了
 回复 引用 查看