通用分页存储过程
1
Community_Common_Count_USP
2
/*
3
作者: SLIGHTBOY
4
建立日期: 2005-8-17
5
修改日期:
6
存储作用: 通用记录统计
7
作用数据库: 自定义
8
输入变量:
9
@KeyColumn 主键字段名
10
@TableName 表格名称
11
@SearchQuery 查询条件 默认 ''
12
输出变量:
13
中间变量:
14
*/
15
CREATE PROCEDURE dbo.Community_Common_Count_USP
16
(
17
@KeyColumn varchar(20),
18
@TableName varchar(20),
19
@SearchQuery varchar(400) = ''
20
)
21
AS
22
SET NOCOUNT ON
23
DECLARE @Timer datetime
24
SET @Timer = getdate()
25![]()
26
IF ( @SearchQuery IS Not NULL ) AND ( @SearchQuery <> '' )
27
exec('SELECT Count('+ @KeyColumn +') FROM ['+ @TableName +'] Where '+ @SearchQuery)
28
Else
29
exec('SELECT Count('+ @KeyColumn +') FROM ['+ @TableName +']')
30![]()
31
print(DATEDIFF(millisecond, @Timer, getdate()))
32
GO
33![]()
34![]()
35
Community_Common_Page_USP
36
/*
37
作者: SLIGHTBOY
38
建立日期: 2005-8-12
39
修改日期:
40
存储作用: 通用分页存储过程
41
作用数据库: 自定义
42
版本: 1.0
43
输入变量:
44
@AbsolutePage 请求页 默认 1
45
@PageSize 每页记录数 默认 20
46
@RecordCount 总记录
47
@KeyColumn 主键字段名
48
@SelectColumn 查询字段
49
@TableName 表格名称
50
@Sort 排序方式 DESC
51
@SearchQuery 查询条件 默认 ''
52
@ExpandQuery 扩展内容(FOR XML EXPLICIT) 默认 ''
53
输出变量:
54
过程变量
55
@SqlQuery 查询语句
56
@AbsolutePosition 当前位置
57
*/
58
CREATE PROCEDURE dbo.Community_Common_Page_USP
59
(
60
@AbsolutePage int = 1,
61
@PageSize int = 20,
62
@RecordCount int,
63
@KeyColumn varchar(20),
64
@SelectColumn varchar(1000),
65
@TableName varchar(20),
66
@Sort varchar(4) = '',
67
@SearchQuery varchar(400) = '',
68
@ExpandQuery varchar(50) = ''
69
)
70
AS
71
SET NOCOUNT ON
72
DECLARE @Timer datetime
73
SET @Timer = getdate()
74![]()
75
DECLARE @SqlQuery varchar(3000)
76
DECLARE @AbsolutePosition int
77
SET @AbsolutePosition = @AbsolutePage * @PageSize
78![]()
79
IF ( @AbsolutePosition = @PageSize )
80
IF ( @Sort = 1 )
81
SET @SqlQuery = '
82
SELECT TOP '+ LTRIM( STR( @PageSize ) ) +'
83
'+ @SelectColumn +'
84
FROM ['+ @TableName +']
85
'+ dbo.iif(@SearchQuery, 'Where '+ @SearchQuery, '' ) +'
86
ORDER BY '+ @KeyColumn +' ASC
87
'+ @ExpandQuery
88
Else
89
90
SET @SqlQuery = '
91
SELECT TOP '+ LTRIM( STR( @PageSize ) ) +'
92
'+ @SelectColumn +'
93
FROM ['+ @TableName +']
94
'+ dbo.iif(@SearchQuery, 'Where '+ @SearchQuery, '' ) +'
95
ORDER BY '+ @KeyColumn +' DESC
96
'+ @ExpandQuery
97
-- 大于 首页 小于 中间页
98
Else IF ( @AbsolutePosition <= @RecordCount/2 )
99
IF ( @Sort = 1 )
100
SET @SqlQuery = '
101
SELECT TOP '+ LTRIM( STR( @PageSize ) ) +' '+ @SelectColumn +'
102
FROM ['+ @TableName +']
103
WHERE '+ @KeyColumn +' >
104
(
105
SELECT MAX('+ @KeyColumn +')
106
FROM
107
(
108
SELECT TOP '+ LTRIM( STR( ( @AbsolutePage - 1)*@PageSize) ) +'
109
'+ @KeyColumn +'
110
FROM ['+ @TableName +']
111
'+ dbo.iif(@SearchQuery, 'Where '+ @SearchQuery, '' ) +'
112
ORDER BY '+ @KeyColumn +' ASC
113
) As Child
114
)
115
'+ dbo.iif(@SearchQuery, ' AND '+ @SearchQuery, '' ) +'
116
ORDER BY '+ @KeyColumn +' ASC
117
'+ @ExpandQuery
118
Else
119
SET @SqlQuery = '
120
SELECT TOP '+ LTRIM( STR( @PageSize ) ) +' '+ @SelectColumn +'
121
FROM ['+ @TableName +']
122
WHERE '+ @KeyColumn +' <=
123
(
124
SELECT Min('+ @KeyColumn +')
125
FROM
126
(
127
SELECT TOP '+ LTRIM( STR( ( @AbsolutePage - 1)*@PageSize + 1 ) ) +'
128
'+ @KeyColumn +'
129
FROM ['+ @TableName +']
130
'+ dbo.iif(@SearchQuery, 'Where '+ @SearchQuery, '' ) +'
131
ORDER BY '+ @KeyColumn +' DESC
132
) As Child
133
)
134
'+ dbo.iif(@SearchQuery, ' AND '+ @SearchQuery, '' ) +'
135
ORDER BY '+ @KeyColumn +' DESC
136
'+ @ExpandQuery
137
Else IF (@AbsolutePosition > @RecordCount/2 )
138
IF ( @Sort = 1 )
139
SET @SqlQuery = '
140
SELECT TOP '+ LTRIM( STR( @PageSize ) ) +' '+ @SelectColumn +'
141
FROM ['+ @TableName +']
142
WHERE '+ @KeyColumn +' >
143
(
144
SELECT Min('+ @KeyColumn +')
145
FROM
146
(
147
SELECT TOP '+ LTRIM( STR( @RecordCount - ((@AbsolutePage - 1) * @PageSize ) + 1) ) +'
148
'+ @KeyColumn +'
149
FROM ['+ @TableName +']
150
'+ dbo.iif(@SearchQuery, 'Where '+ @SearchQuery, '' ) +'
151
ORDER BY '+ @KeyColumn +' DESC
152
) As Child
153
)
154
'+ dbo.iif(@SearchQuery, ' AND '+ @SearchQuery, '' ) +'
155
ORDER BY '+ @KeyColumn +' ASC
156
'+ @ExpandQuery
157
Else
158
SET @SqlQuery = '
159
SELECT TOP '+ LTRIM( STR( @PageSize ) ) +' '+ @SelectColumn +'
160
FROM ['+ @TableName +']
161
WHERE '+ @KeyColumn +' <
162
(
163
SELECT MAX('+ @KeyColumn +')
164
FROM
165
(
166
SELECT TOP '+ LTRIM( STR( @RecordCount - ((@AbsolutePage - 1) * @PageSize ) + 1) ) +'
167
'+ @KeyColumn +'
168
FROM ['+ @TableName +']
169
'+ dbo.iif(@SearchQuery, 'Where '+ @SearchQuery, '' ) +'
170
ORDER BY '+ @KeyColumn +' ASC
171
) As Child
172
)
173
'+ dbo.iif(@SearchQuery, ' AND '+ @SearchQuery, '' ) +'
174
ORDER BY '+ @KeyColumn +' DESC
175
'+ @ExpandQuery
176![]()
177
exec(@SqlQuery)
178
print(@SqlQuery)
179
print(DATEDIFF(millisecond, @Timer, getdate()))
180![]()
181
GO

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

第一个存储过程 计算总记录
第二个存储过程 为选择记录部分
根据传入页码判断位置 实现三个逻辑
请求页 = 第一页
请求页 <= 总页/2
请求页 > 总页/2
并且针对排序方式做了优化
总记录统计部分消耗资源比较大 尽可能做冗余
具体参数作用 存储过程说明里已经做了说明 这里就不重复了
呵呵,一般
1、统计的:如果多表关联统计呢?
2、如果不按照主键排序呢?
结论:对于您的项目可能比较适合,通用? 做不到!
回子扬:
在大容量表里做联合查询 本来就极不划算 所以这种情况 我不考虑
即使要做 也宁愿做冗余
不按主键排序是可以的 希望你看清楚发话
另外我的通用只是 一般意义的话
不是 All in one.
回tubo:
简单是简单了
但
第一 排序没有优化
第二 记录状态选择没有优化
在大容量表里做联合查询 本来就极不划算 所以这种情况 我不考虑
即使要做 也宁愿做冗余
不按主键排序是可以的 希望你看清楚发话
另外我的通用只是 一般意义的话
不是 All in one.
回tubo:
简单是简单了
但
第一 排序没有优化
第二 记录状态选择没有优化
一般
给你个好一些的
CREATE PROCEDURE P_GetPagedOrders2005
@startIndex INT, // 第几页
@pageSize INT //一页显示几条数据
AS
with orderlist as(
select row_number() over(order by freight) as rownumber, orderid,customerid from orders)
select orderid,customerid from orderlist
where rownumber between @startindex and @startindex+@pagesize-1
go
给你个好一些的
CREATE PROCEDURE P_GetPagedOrders2005
@startIndex INT, // 第几页
@pageSize INT //一页显示几条数据
AS
with orderlist as(
select row_number() over(order by freight) as rownumber, orderid,customerid from orders)
select orderid,customerid from orderlist
where rownumber between @startindex and @startindex+@pagesize-1
go
MSSQL 分页方式说明:
目前我所知的有以下几种方式
第一种方式和第二种方实际上是比较类似的.
优点: 排序方式比较随意
缺点:
第一种方式 有大量的 IO 开销.
第二种方式则会开销内存, 但当表数据量比较大的时候性能会直线下降.
所以这两种方式都不适合做大数据量的分页.
第三种方式: 性能次之, 可操作较差
优点: 排序方式比较随意
缺点: 资源开销比较大, 数据库会承担不小的运算压力, 所以也不适合做大表分页.
第四种方式: 性能平均, 可操作性尚可
优点: 排序相对比较随意, 各分页情况下速度平均, 属于不是最快也不是最慢.
缺点: 没有明显缺点.
第五种方式: 性能较好, 可操作性良好
优点: 排序相对比较随意, 代码简洁, 适用面广.
缺点: 尾页速度比较慢(需针对优化).
第六种方式: 性能最好, 可操作性比较差
优点: 速度快.
缺点: 尾页速度比较慢(需针对优化), 对排序键有要求.
PS: 以上内容居于以前测试结果说得.
测试用库 DB_PagingTest, 测试用表: Paing_New
主键: ID Desc
总记录 @RecordCount: 10000331
分页尺寸 @PageSize: 30
总页数 @PageCount: 333345
请求页 @AbsolutePage
分页情况分析:
请求页等于第一页, 这种情况是最简单的.
请求页小于总页数/2
请求页大于等于总页数/2
理论上 请求页等于总页数/2的时候应该也有优化方法.
请求页等于总页数
第 30 条, 即 1 页, CPU 时间 = 0 毫秒,占用时间 = 1 毫秒, 实际执行时间 = 0 毫秒;
第 1W 条, 即 334 页, CPU 时间 = 0 毫秒,占用时间 = 3 毫秒, 实际执行时间 = 0 毫秒;
第 10W 条, 即 3334 页, CPU 时间 = 31 毫秒,占用时间 = 26~28 毫秒, 实际执行时间 = 16~33 毫秒;
第 100W 条, 即 3334 页, CPU 时间 = 250~260 毫秒,占用时间 = 250~260 毫秒, 实际执行时间 = 250~260 毫秒;
第 5000130 条(中间页), 即 166671 页, CPU 时间 = 1200~1300 毫秒,占用时间 = 1200~1300 毫秒, 实际执行时间 = 1200~1300 毫秒;
第 5000160 条(中间页), 即 166672 页, CPU 时间 = 3400~3600 毫秒,占用时间 = 3400~3600 毫秒, 实际执行时间 = 3400~3600 毫秒;
第 9000331 条, 即 300012 页, CPU 时间 = 266~281 毫秒,占用时间 = 273~285 毫秒, 实际执行时间 = 266~296 毫秒;
第 9900331 条, 即 330012 页, CPU 时间 = 31~32 毫秒,占用时间 = 29~30 毫秒, 实际执行时间 = 30~33 毫秒;
第 9999331 条, 即 333312 页, CPU 时间 = 0 毫秒,占用时间 = 2~3 毫秒, 实际执行时间 = 0 毫秒;
第 10000331 条(尾页), 即 333345 页, CPU 时间 = 0 毫秒,占用时间 = 1 毫秒, 实际执行时间 = 0 毫秒;
PS: 关于时间的说明, CPU 时间和占用时间为 MSSQL 的统计结果, 实行时间是人为技术所得;
分页方案优点:
对分页多数情况进行了针对优化, 并且可以对非主键和顺序编号等情况进行分页.
开始和结尾速度都非常快, 因为选择的记录集相对较少.
分页方案缺点:
请求页在总页数中间的时候速度比较慢.
结论:
对于使用 ID 为主键索引的分页, 还是使用传统的 ID 大于或小于这种方式最好.
对于分页主键不明确的, 使用 CTE 的方式比较好.
目前我所知的有以下几种方式
- 临时表
- 表变量
- in, not in
- SET ROWCOUNT
- CTE
- id >, id <
第一种方式和第二种方实际上是比较类似的.
优点: 排序方式比较随意
缺点:
第一种方式 有大量的 IO 开销.
第二种方式则会开销内存, 但当表数据量比较大的时候性能会直线下降.
所以这两种方式都不适合做大数据量的分页.
第三种方式: 性能次之, 可操作较差
优点: 排序方式比较随意
缺点: 资源开销比较大, 数据库会承担不小的运算压力, 所以也不适合做大表分页.
第四种方式: 性能平均, 可操作性尚可
优点: 排序相对比较随意, 各分页情况下速度平均, 属于不是最快也不是最慢.
缺点: 没有明显缺点.
第五种方式: 性能较好, 可操作性良好
优点: 排序相对比较随意, 代码简洁, 适用面广.
缺点: 尾页速度比较慢(需针对优化).
第六种方式: 性能最好, 可操作性比较差
优点: 速度快.
缺点: 尾页速度比较慢(需针对优化), 对排序键有要求.
PS: 以上内容居于以前测试结果说得.
测试用库 DB_PagingTest, 测试用表: Paing_New
主键: ID Desc
总记录 @RecordCount: 10000331
分页尺寸 @PageSize: 30
总页数 @PageCount: 333345
请求页 @AbsolutePage
分页情况分析:
- @AbsolutePage == 1
- @AbsolutePage < @PageCount/2
- @AbsolutePage >= @PageCount/2
- @AbsolutePage == @PageCount
请求页等于第一页, 这种情况是最简单的.
复制内容到剪贴板
情况 2:代码:
Select TOP @PageSize * From [Paing_New] Order BY ID Desc
请求页小于总页数/2
复制内容到剪贴板
情况 3:代码:
WITH CTE AS
(
SELECT TOP @AbsolutePage * @PageSize
*
ROW_NUMBER() Over (Order By ID Desc) as _RowNumber
FROM [Paing_New]
)
SELECT
*
FROM CTE
WHERE _RowNumber > (@AbsolutePage - 1) * @PageSize);
请求页大于等于总页数/2
理论上 请求页等于总页数/2的时候应该也有优化方法.
复制内容到剪贴板
情况 4:代码:
WITH CTE AS
(
SELECT TOP @RecordCount - (@AbsolutePage - 1) * @PageSize
*,
ROW_NUMBER() Over (Order BY ID Asc) as _RowNumber
FROM [Paing_New]
)
SELECT
*
FROM CTE
WHERE _RowNumber > (@RecordCount - @AbsolutePage * @PageSize) Order BY ID Desc;
请求页等于总页数
复制内容到剪贴板
数据测试结果:代码:
WITH CTE AS
(
SELECT TOP @RecordCount - (@AbsolutePage - 1) * @PageSize
*,
ROW_NUMBER() Over (Order BY ID Asc) as _RowNumber
FROM [Paing_New]
)
SELECT
*
FROM CTE Order BY ID Desc;
第 30 条, 即 1 页, CPU 时间 = 0 毫秒,占用时间 = 1 毫秒, 实际执行时间 = 0 毫秒;
第 1W 条, 即 334 页, CPU 时间 = 0 毫秒,占用时间 = 3 毫秒, 实际执行时间 = 0 毫秒;
第 10W 条, 即 3334 页, CPU 时间 = 31 毫秒,占用时间 = 26~28 毫秒, 实际执行时间 = 16~33 毫秒;
第 100W 条, 即 3334 页, CPU 时间 = 250~260 毫秒,占用时间 = 250~260 毫秒, 实际执行时间 = 250~260 毫秒;
第 5000130 条(中间页), 即 166671 页, CPU 时间 = 1200~1300 毫秒,占用时间 = 1200~1300 毫秒, 实际执行时间 = 1200~1300 毫秒;
第 5000160 条(中间页), 即 166672 页, CPU 时间 = 3400~3600 毫秒,占用时间 = 3400~3600 毫秒, 实际执行时间 = 3400~3600 毫秒;
第 9000331 条, 即 300012 页, CPU 时间 = 266~281 毫秒,占用时间 = 273~285 毫秒, 实际执行时间 = 266~296 毫秒;
第 9900331 条, 即 330012 页, CPU 时间 = 31~32 毫秒,占用时间 = 29~30 毫秒, 实际执行时间 = 30~33 毫秒;
第 9999331 条, 即 333312 页, CPU 时间 = 0 毫秒,占用时间 = 2~3 毫秒, 实际执行时间 = 0 毫秒;
第 10000331 条(尾页), 即 333345 页, CPU 时间 = 0 毫秒,占用时间 = 1 毫秒, 实际执行时间 = 0 毫秒;
PS: 关于时间的说明, CPU 时间和占用时间为 MSSQL 的统计结果, 实行时间是人为技术所得;
分页方案优点:
对分页多数情况进行了针对优化, 并且可以对非主键和顺序编号等情况进行分页.
开始和结尾速度都非常快, 因为选择的记录集相对较少.
分页方案缺点:
请求页在总页数中间的时候速度比较慢.
结论:
对于使用 ID 为主键索引的分页, 还是使用传统的 ID 大于或小于这种方式最好.
对于分页主键不明确的, 使用 CTE 的方式比较好.
Create Procedure ComListPaged
@Sql nvarchar(800), --构造好的sql语句
@PKey nvarchar(50), --主键
@PageSize int, --每页大小
@Page int, --页码
@Order nvarchar(50), --排序表达式,比如"pub_date desc"
@TotalCount int output --总记录数
As
Declare @Sql1 nvarchar(1000)
Declare @Sql2 nvarchar(1000)
Select @Sql1 = 'Select @Count = count(ta.' + @PKey + ') From (' + @Sql + ') ta '
Exec sp_executesql @Sql1, N'@Count int output', @TotalCount output
Select @Sql2 = 'Select Top ' + Cast(@PageSize As varchar) + ' * From (' + @Sql + ') ta '
Select @Sql2 = @Sql2 + 'Where ta.' + @PKey + ' Not In(Select Top ' + Cast((@Page - 1)* @PageSize As varchar) + ' ' + @PKey + ' From (' + @Sql + ') tb Order By ' + @Order + ') '
Select @Sql2 = @Sql2 + ' Order By ' + @Order
Exec(@Sql2)
Go