Cyberpks
Cyberpks

Reputation: 1421

T-SQL Order by most relevant and most recent on top

I am using ContainsTable in my T-SQL statement to get most relevant content as per some given keywords in below query.

Select @Values = '"Keyword 1","Keyword 2,"Keyword 3"', 
Select @FirstLevelValues = 'Isabout ('+@Values+' Weight(0.7))'
Select @SecondLevelValues = 'Isabout ('+@Values+' Weight(0.3))'

;with cte as (
     (Select [Key], [Rank] from ContainsTable(PostsTable, Title, @FirstLevelValues))
     union
     (Select [Key], [Rank] from ContainsTable(PostsTable, Description, @SecondLevelValues))
) 
Select Top 100 P.* from PostsTable P
Left Join cte on cte.[Key] = P.ID where
P.PostDate between DATEADD(DAY, -5, GETUTCDATE()) and GETUTCDATE() 
Order by cte.[Rank] DESC, PostDate DESC

Now my problem is that I have [Rank] column, which rates a Post from PostsTable on the basis of most relevant (most keyword matches has higher ranking). But, this should work in conjunction with PostDate column. Means the most relevant and the most recent Post should be rated higher than only the most relevant post. I do understand the concept of Order by, where one resultset is ordered basis one column first followed by the second column and so on. But in my case, I want that the PostDate should also be considered a criteria, probably with a Weight as in IsAbout clause, so it can also decide the ranking of the post.

Upvotes: -1

Views: 78

Answers (1)

Charlieface
Charlieface

Reputation: 72258

You can just flip the order of the ORDER BY columns to sort by PostDate first. But you need to change to a JOIN otherwise you will just get all Posts sorted by date.

SET @Values = '"Keyword 1","Keyword 2,"Keyword 3"';
SET @FirstLevelValues = 'Isabout ('+@Values+' Weight(0.7))';
SET @SecondLevelValues = 'Isabout ('+@Values+' Weight(0.3))';

with cte as
(
    Select [Key], [Rank]
    from ContainsTable(PostsTable, Title, @FirstLevelValues)
    union
    Select [Key], [Rank]
    from ContainsTable(PostsTable, Description, @SecondLevelValues)
) 
Select Top (100) P.*
from PostsTable P
Join cte on cte.[Key] = P.ID
where P.PostDate between DATEADD(DAY, -5, GETUTCDATE()) and GETUTCDATE() 
Order by
  p.PostDate DESC,
  cte.[Rank] DESC;

I note though that you may get duplicate results if a Key is returned in both CONTAINSTABLE functions. You then need to also to take into account the higher of the ranks.

Instead you need either a FULL JOIN:

with cte as
(
    Select
      [Key] = ISNULL(t.[Key], d.[Key]),
      Rank = CASE WHEN t.Rank < d.Rank THEN d.Rank ELSE t.Rank END  -- be careful of NULL
    from ContainsTable(PostsTable, Title, @FirstLevelValues) t
    full join ContainsTable(PostsTable, Description, @SecondLevelValues) d
      on d.[Key] = t.[Key]
) 
Select Top (100) P.*
from PostsTable P
Join cte on cte.[Key] = P.ID
where P.PostDate between DATEADD(DAY, -5, GETUTCDATE()) and GETUTCDATE() 
Order by
  p.PostDate DESC,
  cte.[Rank] DESC;

Or you can use two LEFT JOINs.

Select Top (100) P.*
from PostsTable P
Left Join ContainsTable(PostsTable, Title, @FirstLevelValues) t on t.[Key] = P.ID
left join ContainsTable(PostsTable, Description, @SecondLevelValues) d on d.[Key] = P.ID
where P.PostDate between DATEADD(DAY, -5, GETUTCDATE()) and GETUTCDATE()
  and (d.Rank is not null or t.Rank is not null)
Order by
  p.PostDate DESC,
  ISNULL(d.Rank, t.Rank) DESC;

The best option is probably to just use multiple columns in the CONTAINSTABLE, although this won't work if you want multiple weightings.

Select Top (100) P.*
from PostsTable P
Join ContainsTable(PostsTable, (Title, Description), @FirstLevelValues) ct on t.[Key] = P.ID
where P.PostDate between DATEADD(DAY, -5, GETUTCDATE()) and GETUTCDATE() 
Order by
  p.PostDate DESC,
  ct.Rank DESC;

Upvotes: 0

Related Questions