CTE's are surprisingly useful for paging data, but I'm running into a problem where I have to add a distinct clause. The idea is to pull topic records for a user's forum posts, but only one topic record even if there are multiple posts in the topic. Here's what I have so far:
CREATE PROCEDURE pf_PagedTopicsByUser(
@StartRow int,
@PageSize int,
@UserID int
)
AS
DECLARE @Counter int
SET @Counter = (@StartRow + @PageSize - 1)
SET ROWCOUNT @Counter;
WITH Entries AS (
SELECT ROW_NUMBER() OVER (ORDER BY IsPinned DESC, LastPostTime DESC)
AS Row, pf_Topic.TopicID, pf_Topic.ForumID, pf_Topic.Title, pf_Topic.ReplyCount, pf_Topic.ViewCount,
pf_Topic.StartedByUserID, pf_Topic.StartedByName, pf_Topic.LastPostUserID, pf_Topic.LastPostName,
pf_Topic.LastPostTime, pf_Topic.IsClosed, pf_Topic.IsPinned, pf_Topic.IsDeleted, pf_Topic.IsIndexed
FROM pf_Topic JOIN pf_Post ON pf_Topic.TopicID = pf_Post.TopicID
WHERE pf_Post.UserID = @UserID AND pf_Topic.IsDeleted = 0)
SELECT TopicID, Entries.ForumID, Entries.Title, ReplyCount, ViewCount,
StartedByUserID, StartedByName, LastPostUserID, Entries.LastPostName,
Entries.LastPostTime, IsClosed, IsPinned, IsDeleted, IsIndexed, pf_Forum.Title
FROM Entries JOIN pf_Forum ON Entries.ForumID = pf_Forum.ForumID
WHERE Row between
@StartRow and @StartRow + @PageSize - 1
SET ROWCOUNT 0
GO
The problem is that you can't put the DISTINCT in the last SELECT, because then you won't return an entire "page" of results. It would also appear that you can't use DISTINCT in the CTE declaration.
Any suggestions

Where to put DISTINCT in a CTE?
Juraj Borza
Progalex
Biocide
Old Jeffrey Zhao
Dave12349
Chrispragash
Something like this using a second CTE:
WITH FirstEntries AS (
SELECT ROW_NUMBER() OVER (PARTITION BY Topic ORDER BY IsPinned DESC, LastPostTime DESC)
AS GroupRow, pf_Topic.TopicID, pf_Topic.ForumID, pf_Topic.Title, pf_Topic.ReplyCount, pf_Topic.ViewCount,
pf_Topic.StartedByUserID, pf_Topic.StartedByName, pf_Topic.LastPostUserID, pf_Topic.LastPostName,
pf_Topic.LastPostTime, pf_Topic.IsClosed, pf_Topic.IsPinned, pf_Topic.IsDeleted, pf_Topic.IsIndexed
FROM pf_Topic JOIN pf_Post ON pf_Topic.TopicID = pf_Post.TopicID
WHERE pf_Post.UserID = @UserID AND pf_Topic.IsDeleted = 0),
Entries as (
SELECT *,ROW_NUMBER() OVER (ORDER BY IsPinned DESC, LastPostTime DESC) AS Row
FROM FirstEntries
WHERE GroupRow = 1)
SELECT TopicID, Entries.ForumID, Entries.Title, ReplyCount, ViewCount,
StartedByUserID, StartedByName, LastPostUserID, Entries.LastPostName,
Entries.LastPostTime, IsClosed, IsPinned, IsDeleted, IsIndexed, pf_Forum.Title
FROM Entries JOIN pf_Forum ON Entries.ForumID = pf_Forum.ForumID
WHERE Row between
@StartRow and @StartRow + @PageSize - 1
Tang Meister
Thank you for your help.
xavito
TTris
One little change, the column after the PARTITION BY should be pf_Topic.TopicID, otherwise, perfect!
Tdah
helghe
You could add another layer of CTE and another ROW_NUMBER:
WITH FirstEntries AS ( --untested, so you might have to tweak just a bit to get the first
--row you want
SELECT ROW_NUMBER() OVER (PARTITION BY Topic ORDER BY IsPinned DESC, LastPostTime DESC)
AS GroupRow, ...
Then add to the WHERE clause
WHERE GroupRow = 1
That will work. Then do the paging query.
Mirko Messori
COUNT(*) OVER()
in your select clause, before you've done your filtering. Like this (hopefully you can see the green stuff):
WITH FirstEntries AS (
SELECT ROW_NUMBER() OVER (PARTITION BY Topic ORDER BY IsPinned DESC, LastPostTime DESC)
AS GroupRow, pf_Topic.TopicID, pf_Topic.ForumID, pf_Topic.Title, pf_Topic.ReplyCount, pf_Topic.ViewCount,
pf_Topic.StartedByUserID, pf_Topic.StartedByName, pf_Topic.LastPostUserID, pf_Topic.LastPostName,
pf_Topic.LastPostTime, pf_Topic.IsClosed, pf_Topic.IsPinned, pf_Topic.IsDeleted, pf_Topic.IsIndexed
FROM pf_Topic JOIN pf_Post ON pf_Topic.TopicID = pf_Post.TopicID
WHERE pf_Post.UserID = @UserID AND pf_Topic.IsDeleted = 0),
Entries as (
SELECT *,ROW_NUMBER() OVER (ORDER BY IsPinned DESC, LastPostTime DESC) AS Row, COUNT(*) OVER () as cnt
FROM FirstEntries
WHERE GroupRow = 1)
SELECT TopicID, Entries.ForumID, Entries.Title, ReplyCount, ViewCount,
StartedByUserID, StartedByName, LastPostUserID, Entries.LastPostName,
Entries.LastPostTime, IsClosed, IsPinned, IsDeleted, IsIndexed, pf_Forum.Title, cnt
FROM Entries JOIN pf_Forum ON Entries.ForumID = pf_Forum.ForumID
WHERE Row between
@StartRow and @StartRow + @PageSize - 1
--Hope this helps...
Rob