如何在 SQL Server 2008 中进行分页
Posted
技术标签:
【中文标题】如何在 SQL Server 2008 中进行分页【英文标题】:How to do pagination in SQL Server 2008 【发布时间】:2011-01-15 16:54:10 【问题描述】:?
【问题讨论】:
没有 MSSQL 2008 这样的东西。你的意思是“SQL Server 2008” 【参考方案1】:你可以使用ROW_NUMBER():
返回结果集分区内行的序号,每个分区的第一行从 1 开始。
例子:
WITH CTEResults AS
(
SELECT IDColumn, SomeField, DateField, ROW_NUMBER() OVER (ORDER BY DateField) AS RowNum
FROM MyTable
)
SELECT *
FROM CTEResults
WHERE RowNum BETWEEN 10 AND 20;
【讨论】:
【参考方案2】:你可以试试
DECLARE @Table TABLE(
Val VARCHAR(50)
)
DECLARE @PageSize INT,
@Page INT
SELECT @PageSize = 10,
@Page = 2
;WITH PageNumbers AS(
SELECT Val,
ROW_NUMBER() OVER(ORDER BY Val) ID
FROM @Table
)
SELECT *
FROM PageNumbers
WHERE ID BETWEEN ((@Page - 1) * @PageSize + 1)
AND (@Page * @PageSize)
【讨论】:
我认为这段代码不起作用。你测试过这段代码吗? (它应该用“@Table”替换“PageNumbers”表。 @zeitgeist,它正在使用一个名为 PageNumbers 的 CTE。您是否按原样测试了代码? 这个分页算法是错误的。对于第 1 页和第 2 页,它可以工作,但从第 3 页开始,它将开始跳过条目。第2页将从11
转到20
,但第3页将从22
转到30
跳过条目21
。【参考方案3】:
SQL Server 2012 提供分页功能(请参阅http://www.codeproject.com/Articles/442503/New-features-for-database-developers-in-SQL-Server)
在SQL2008中你可以这样做:
declare @rowsPerPage as bigint;
declare @pageNum as bigint;
set @rowsPerPage=25;
set @pageNum=10;
With SQLPaging As (
Select Top(@rowsPerPage * @pageNum) ROW_NUMBER() OVER (ORDER BY ID asc)
as resultNum, *
FROM Employee )
select * from SQLPaging with (nolock) where resultNum > ((@pageNum - 1) * @rowsPerPage)
久经考验!它始终如一地工作和扩展。
【讨论】:
如果去掉顶部,结果可能会有所不同。如果您删除顶部,此方法不保证性能相同的结果,除非表已按 id 排序。【参考方案4】:1) 创建虚拟数据
CREATE TABLE #employee (EMPID INT IDENTITY, NAME VARCHAR(20))
DECLARE @id INT = 1
WHILE @id < 200
BEGIN
INSERT INTO #employee ( NAME ) VALUES ('employee_' + CAST(@id AS VARCHAR) )
SET @id = @id + 1
END
2) 现在应用解决方案。
这种情况假设 EMPID 是唯一且已排序的列。
在课程外,您将在不同的列中应用它...
DECLARE @pageSize INT = 20
SELECT * FROM (
SELECT *, PageNumber = CEILING(CAST(EMPID AS FLOAT)/@pageSize)
FROM #employee
) MyQuery
WHERE MyQuery.PageNumber = 1
【讨论】:
可爱的方法,但添加新列可能是开销。【参考方案5】:这些是我在 SQL 服务器端对查询结果进行分页的解决方案。我已经添加了过滤和排序的概念。当您在 Gridview 中进行分页、过滤和排序时,它非常有效。
在测试之前,您必须创建一个示例表并在该表中插入一些行:(在现实世界中,您必须考虑您的表字段来更改 Where 子句,也许您在选择的主要部分有一些连接和子查询)
Create Table VLT
(
ID int IDentity(1,1),
Name nvarchar(50),
Tel Varchar(20)
)
GO
Insert INTO VLT
VALUES
('NAME' + Convert(varchar(10),@@identity),'FAMIL' + Convert(varchar(10),@@identity))
GO 500000
在 SQL Server 2008 中,您可以使用 CTE 概念。因此,我为 SQL Server 2008+ 编写了两种类型的查询
-- SQL Server 2008+
DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.
SELECT
Data.ID,
Data.Name,
Data.Tel
FROM
(
SELECT
ROW_NUMBER()
OVER( ORDER BY
CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
THEN VLT.ID END ASC,
CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
THEN VLT.ID END DESC,
CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
THEN VLT.Name END ASC,
CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
THEN VLT.Name END ASC,
CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
THEN VLT.Tel END ASC,
CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
THEN VLT.Tel END ASC
) AS RowNum
,*
FROM VLT
WHERE
( -- We apply the filter logic here
CASE
WHEN @FilterType = 'None' THEN 1
-- Name column filter
WHEN @FilterType = 'Contain' AND @FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID NOT LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'Match' AND @FilterColumn = 1
AND VLT.ID = @FilterValue THEN 1
WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
AND VLT.ID <> @FilterValue THEN 1
-- Name column filter
WHEN @FilterType = 'Contain' AND @FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name NOT LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'Match' AND @FilterColumn = 2
AND VLT.Name = @FilterValue THEN 1
WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
AND VLT.Name <> @FilterValue THEN 1
-- Tel column filter
WHEN @FilterType = 'Contain' AND @FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel NOT LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'Match' AND @FilterColumn = 3
AND VLT.Tel = @FilterValue THEN 1
WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
AND VLT.Tel <> @FilterValue THEN 1
END
) = 1
) AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum
GO
在 SQL Server 2008+ 中使用 CTE 的第二个解决方案
DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.
;WITH
Data_CTE
AS
(
SELECT
ROW_NUMBER()
OVER( ORDER BY
CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
THEN VLT.ID END ASC,
CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
THEN VLT.ID END DESC,
CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
THEN VLT.Name END ASC,
CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
THEN VLT.Name END ASC,
CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
THEN VLT.Tel END ASC,
CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
THEN VLT.Tel END ASC
) AS RowNum
,*
FROM VLT
WHERE
( -- We apply the filter logic here
CASE
WHEN @FilterType = 'None' THEN 1
-- Name column filter
WHEN @FilterType = 'Contain' AND @FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID NOT LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'Match' AND @FilterColumn = 1
AND VLT.ID = @FilterValue THEN 1
WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
AND VLT.ID <> @FilterValue THEN 1
-- Name column filter
WHEN @FilterType = 'Contain' AND @FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name NOT LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'Match' AND @FilterColumn = 2
AND VLT.Name = @FilterValue THEN 1
WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
AND VLT.Name <> @FilterValue THEN 1
-- Tel column filter
WHEN @FilterType = 'Contain' AND @FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel NOT LIKE '%' + @FilterValue + '%'
OR
@FilterValue = ''
) THEN 1
WHEN @FilterType = 'Match' AND @FilterColumn = 3
AND VLT.Tel = @FilterValue THEN 1
WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
AND VLT.Tel <> @FilterValue THEN 1
END
) = 1
)
SELECT
Data.ID,
Data.Name,
Data.Tel
FROM Data_CTE AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum
【讨论】:
【参考方案6】:另一个至少从 SQL 2005 开始工作的解决方案是将 TOP 与 SELECT 子查询和 ORDER BY 子句一起使用。
简而言之,检索每页 10 行的第 2 行与检索前 20 行中的最后 10 行相同。这意味着使用 ASC 顺序检索前 20 行,然后使用 DESC 顺序检索前 10 行,然后再使用 ASC 进行排序。
示例:检索页面 2 行,每页 3 行
create table test(id integer);
insert into test values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
select *
from (
select top 2 *
from (
select top (4) *
from test
order by id asc) tmp1
order by id desc) tmp1
order by id asc
【讨论】:
TOP 解决了第一页的问题,但没有解决下一页的问题……我需要在有 50 条记录的页面中请求数据。我用的第一页选择 TOP 50,好的,但是第二页呢? 这就是我的示例显示的内容。获取第 2 页的 50 条记录:选择前 100 条记录,然后按倒序排列,选择前 50 条记录。您现在有您的第 2 页记录,但顺序相反。正确地重新排序它们,你就完成了。但那是复杂的代码,你应该只在你被旧的 SQL Server 版本卡住并且无法访问诸如 ROW_NUMBER() 之类的功能时才使用它。【参考方案7】:我用于分页的查询是这个(在 Oracle DB 上)。
SELECT * FROM tableName
WHERE RowNum >= 1
AND RowNum < 20
ORDER BY RowNum;
RowNum - 它是由 DB 提供的变量。对于查询返回的每一行,ROWNUM 伪列返回一个数字,指示 Oracle 从一个表或一组连接行中选择行的顺序。
【讨论】:
【参考方案8】:SELECT DISTINCT Id,ParticipantId,ActivityDate,IsApproved,
IsDeclined,IsDeleted,SubmissionDate, IsResubmitted,
[CategoryId] Id,[CategoryName] Name,
[ActivityId] [Id],[ActivityName] Name,Points,
[UserId] [Id],Email,
ROW_NUMBER() OVER(ORDER BY Id desc) AS RowNum from
(SELECT DISTINCT
Id,ParticipantId,
ActivityDate,IsApproved,
IsDeclined,IsDeleted,
SubmissionDate, IsResubmitted,
[CategoryId] [CategoryId],[CategoryName] [CategoryName],
[ActivityId] [ActivityId],[ActivityName] [ActivityName],Points,
[UserId] [UserId],Email,
ROW_NUMBER() OVER(ORDER BY Id desc) AS RowNum from
(SELECT DISTINCT ASN.Id,
ASN.ParticipantId,ASN.ActivityDate,
ASN.IsApproved,ASN.IsDeclined,
ASN.IsDeleted,ASN.SubmissionDate,
CASE WHEN (SELECT COUNT(*) FROM FDS_ActivitySubmission WHERE ParentId=ASN.Id)>0 THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END IsResubmitted,
AC.Id [CategoryId], AC.Name [CategoryName],
A.Id [ActivityId],A.Name [ActivityName],A.Points,
U.Id[UserId],U.Email
FROM
FDS_ActivitySubmission ASN WITH (NOLOCK)
INNER JOIN
FDS_ActivityCategory AC WITH (NOLOCK)
ON
AC.Id=ASN.ActivityCategoryId
INNER JOIN
FDS_ApproverDetails FDSA
ON
FDSA.ParticipantID=ASN.ParticipantID
INNER JOIN
FDS_ActivityJobRole FAJ
ON
FAJ.RoleId=FDSA.JobRoleId
INNER JOIN
FDS_Activity A WITH (NOLOCK)
ON
A.Id=ASN.ActivityId
INNER JOIN
Users U WITH (NOLOCK)
ON
ASN.ParticipantId=FDSA.ParticipantID
WHERE
IsDeclined=@IsDeclined AND IsApproved=@IsApproved AND ASN.IsDeleted=0
AND
ISNULL(U.Id,0)=ISNULL(@ApproverId,0)
AND ISNULL(ASN.IsDeleted,0)<>1)P)t where t.RowNum between
(((@PageNumber - 1) * @PageSize) + 1) AND (@PageNumber * PageSize)
AND t.IsDeclined=@IsDeclined AND t.IsApproved=@IsApproved AND t.IsDeleted = 0
AND (ISNULL(t.Id,0)=ISNULL(@SubmissionId,0)or ISNULL(@SubmissionId,0)<=0)
【讨论】:
请不要只发布一些代码,而是解释为什么和如何这段代码解决了这个问题。以上是关于如何在 SQL Server 2008 中进行分页的主要内容,如果未能解决你的问题,请参考以下文章
数据表过滤器在应用SQL Server 2008分页代码时无法正常工作