SQL Server递归CTE和分页

时间:2009-09-06 16:21:04

标签: sql-server select paging common-table-expression

我有这张桌子

 CREATE TABLE [dbo].[friend_blocked_list](
 [subdomain] [varchar](50) NOT NULL,
 [un] [nvarchar](50) NOT NULL,
 [friend] [nvarchar](50) NOT NULL,
 [is_blocked] [bit] NOT NULL,
 [approved] [bit] NOT NULL)

我用以下查询从中选择数据。选择查询将用户添加为朋友的用户和已被用户添加为朋友的用户

组合在一起
declare @un varchar(50), @subdomain varchar(50)

set @un='user2';
set @subdomain ='test.domain.com';

WITH FRIENDS as
(
  SELECT friend
  FROM friend_blocked_list
  WHERE un=@un and subdomain=@subdomain and approved=1 and is_blocked=0

  UNION ALL

  SELECT un as friend
  FROM friend_blocked_list
  WHERE friend=@un and subdomain=@subdomain and approved=1 and is_blocked=0
)
select friend from FRIENDS group by FRIENDS.friend order by FRIENDS.friend asc

它可以很好地处理小数据,但我希望能够在服务器端进行分页以减少负载。我正在尝试将它与我的分页sp结合起来

create PROCEDURE [dbo].[Paging]
    @subdomain varchar(50),
    @un varchar(50),
    @PageNumber int,
    @PageSize int
AS
BEGIN
   --paging
   DECLARE @FirstRow INT,@LastRow INT,@RowCount INT,@PageCount INT

   --find recordcount and pages
   SELECT @RowCount = COUNT(*), @PageCount = COUNT(*) / @PageSize 
   FROM friend_blocked_list 
   WHERE subdomain=@subdomain AND un=@un AND approved=1 AND is_blocked=0;

   --- calculate pages    
   IF @RowCount % @PageSize != 0 SET @PageCount = @PageCount + 1 
   IF @PageNumber < 1 SET @PageNumber = 1 
   IF @PageNumber > @PageCount SET @PageNumber = @PageCount 

   SELECT 
        CurrentPage = @PageNumber, 
        TotalPages = @PageCount, 
        TotalRows = @RowCount 

   -- mora calculation
   SELECT @FirstRow = ( @PageNumber - 1) * @PageSize + 1,
          @LastRow = (@PageNumber - 1) * @PageSize + @PageSize ;

   WITH MyTopics  AS
   (
      SELECT *, ROW_NUMBER() OVER (order by un asc) AS RowNumber
      FROM friend_blocked_list 
      WHERE subdomain=@subdomain AND un=@un AND approved=1 AND is_blocked=0
   )
   SELECT *
   FROM MyTopics
   WHERE RowNumber BETWEEN @FirstRow AND @LastRow
   ORDER BY RowNumber ASC;
end

但一如既往,我遇到了麻烦:)。主要问题是我的查询中的UNION ALL。它阻止我使用ROW_NUMBER() OVER

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

以下是为分页更新的过程:

CREATE PROCEDURE [dbo].[Paging]
  @subdomain varchar(50),
  @un varchar(50),
  @PageNumber int,
  @PageSize int
AS

  DECLARE @start_row int
  DECLARE @end_row int

  SET @end_row = @PageNumber * @PageSize
  SET @start_row = @end_row - (@PageSize - 1)

BEGIN

  WITH FRIENDS AS (
    SELECT t.friend
      FROM FRIEND_BLOCKED_LIST t
     WHERE t.un = @un 
       AND t.subdomain = @subdomain 
       AND t.approved = 1 
       AND t.is_blocked = 0
   UNION ALL
    SELECT t.un as friend
      FROM FRIEND_BLOCKED_LIST t
     WHERE t.friend = @un 
       AND t.subdomain = @subdomain 
       AND t.approved = 1 
       AND t.is_blocked = 0)
SELECT t.friend
  FROM (SELECT f.friend,
               ROW_NUMBER() OVER (ORDER BY f.friend) AS rownum
          FROM FRIENDS f
      GROUP BY f.friend) t
 WHERE t.rownum BETWEEN @start_row AND @end_row

END

如果您可以在FRIEND_BLOCKED_LIST表上提供更多信息,则可以更改查询以使用一个CTE。两个查询具有相同UNION子句的WHERE,同时区分两列,这让我想知道它是否能够更好地编写。 FRIENDS CTE可以改写为:

SELECT COALESCE(t.un, t.friend) as friend
  FROM FRIEND_BLOCKED_LIST t
 WHERE @un = COALESCE(t.un, t.friend)
   AND t.subdomain = @subdomain 
   AND t.approved = 1 
   AND t.is_blocked = 0

答案 1 :(得分:0)

将行号应用于您要返回的列表:

WITH FRIENDS as
(
SELECT friend
  FROM friend_blocked_list
  WHERE un=@un and subdomain=@subdomain and approved=1 and is_blocked=0
UNION ALL
SELECT un as friend
  FROM friend_blocked_list
  WHERE friend=@un and subdomain=@subdomain and approved=1 and is_blocked=0 )
, recursive_friends as  (
select friend 
 , row_number() over (order by friend asc) as rn
from FRIENDS )
select friend 
  from recursive_friends
  where rn between @firstRow and @lastRow
  order by FRIENDS.friend asc;