views:

501

answers:

2

I have this table

 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)

where i select data from it with below query. Select query combines users that added user as friend and users that have been added as friend by user

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

It works fine with small amout of data but i want to be able to do a paging on the server side in order to reduce load. I am trying to combine it with my paging sp below

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

But as always i am having trouble :). Main problem is the UNION ALL in my query. It prevents me using ROW_NUMBER() OVER.

Any ideas?

A: 

Apply the row number to the list you want to return:

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;
Remus Rusanu
, recursive_friends as (select friend , row_number() over (order by friend asc) as rnfrom FRIENDS ) doesn't seem to be right
nLL
A: 

Here's your procedure updated for paging:

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

It might be possible to change the query to use one CTE if you could provide more information on the FRIEND_BLOCKED_LIST table. The UNION of two queries with identical WHERE clauses while differentiating between two columns makes me wonder if it couldn't be written better. Could the FRIENDS CTE be rewritten as:

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
OMG Ponies
What sort of info would you need on FRIEND_BLOCKED_LIST table?
nLL
also in second select after union all it should be WHERE t.friend= @un istead of WHERE t.un = @un so they are not identical queries
nLL
Column list if it's not terribly long, and what your logic is for the UNION on FRIEND_BLOCKED_LIST. The query as is, looks like it would return FRIEND_BLOCKED_LIST.friend and FRIEND_BLOCKED_LIST.un for the same person(?). It's not clear what is going on in the table.
OMG Ponies
Fixed the 2nd part of the UNION, sorry for my typo.
OMG Ponies
i am trying to select users that (say) usera have as friend and users that have usera as friend
nLL
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 = 0doesn;t return desired result
nLL
would it be easier with temp table?
nLL
No, a CTE is a temp table under the covers. I understand what you want to get from the database, but I don't have enough information to be able to craft a better query for you.
OMG Ponies
what do you need to know about table? what sort of data in it?
nLL