SQL Server recursive CTE and paging - sql-server

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?

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

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;

Related

Attempting to return more rows than what is available

Below is an example of what my stored procedure looks like.
One problem occurs when a user attempts to fetch a number of rows (#PageSize) more than what is currently available in the last page (#PageNumber), then the returned result is some how zero, instead of returning what is available even if less than the passed PageSize param.
This call returns result
exec [dbo].[CustomersPaginationProc] #LocationID ='0',#PageNumber=17999,#PageSize=10,#SortColumn=N'Name',#SortOrder=N'ASC'
while this call does not
exec [dbo].[CustomersPaginationProc] #LocationID ='0',#PageNumber=17999,#PageSize=20,#SortColumn=N'Name',#SortOrder=N'ASC'
Procedure detail:
ALTER PROCEDURE [CustomersPaginationProc]
-- Add the parameters for the stored procedure here
#LocationID VARCHAR(50) = NULL
#PageNumber INT = NULL,
#PageSize INT = NULL,
#SortColumn NVARCHAR(20) = NULL,
#SortOrder NVARCHAR(4) = NULL
AS BEGIN
SET NOCOUNT ON;
WITH CTE_Results AS
(
SELECT
cs.LocationID
, cs.Name
FROM Customers cs with (nolock)
WHERE
(#LocationID IS NULL OR cs.LocationID LIKE '%' + #LocationID + '%')
),
CTE_TotalRows AS
(
SELECT COUNT(*) AS MaxRows FROM CTE_Results
)
SELECT * FROM CTE_Results, CTE_TotalRows
ORDER BY
CASE WHEN (#SortColumn IS NOT NULL AND #SortColumn = 'LocationID' AND #SortOrder= 'ASC')
THEN CTE_Results.LocationID
END ASC
OFFSET #PageSize * (#PageNumber) ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION (RECOMPILE)
END
I don't get why you are doing the multiplication? Why the full join? This can be simplified, and works when the second parameter exceeds the number of available rows.
declare #startRows int = 95
declare #nextRows int = 10
--create 100 rows
;WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E2
)
select N
from cteTally
order by N
offset #startRows rows
fetch next #nextRows rows only

Sort row on datetime in stored procedure with temp table

I am having a stored procedure which selects data from multiple tables(using joins) and returns it in a sorted order (asc or desc) based on the provided column.
For generating the sorted result based on the input, I use a Temp Table.
My problem is, when the provided column is of datetime, then the sorting is not working.
For other columns, the procedure works as expected.
This is the code I a using.
ALTER PROCEDURE [dbo].[usp_GetEULADetails]
(
#OrgKey INT
,#FilterParams FilterTypes READONLY,
/*– Sorting Parameters */
#SortColumn nvarchar(20) = 'Status' -- BranchCode
,#SortOrder nvarchar(4) = 'Asc'
/*– Pagination Parameters */
,#PageNumber int = 0
,#PageSize int = 0
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SortCol nvarchar(20),
#FirstRec int,
#LastRec int,
#TotalRows int,
#GlobalSearch nvarchar(100),
#EULAVersionNumber nvarchar(100)
DECLARE #StatusKey INT =(SELECT dbo.GetStatusKey('ACTIVE'))
DECLARE #RowCountSelected INT
SELECT
#EULAVersionNumber = LTRIM(RTRIM(Value))
FROM #FilterParams
WHERE [FieldName] = 'EULAVersions'
SELECT
#GlobalSearch = LTRIM(RTRIM(Value))
FROM #FilterParams
WHERE [IsGlobal] = 1
SET #SortCol = LTRIM(RTRIM(#SortColumn))
SET #FirstRec = (#PageNumber - 1) * #PageSize
SET #LastRec = (#PageNumber * #PageSize + 1)
CREATE TABLE #ResultTempTable (
ROWNUM int
,ORGKEY int
,EULAVersionKey int
,EULAVersionNumber varchar(25)
,EULAVersionUpdateDate datetime
,STATUSKEY tinyint
,DocumentFileName varchar(100)
,DocumentGUID varchar(100)
,DocumentTypeName varchar(50)
,DocumentDescription varchar(128)
,ContactKey int
,ContactFirstName varchar(50)
,ContactMiddleName varchar(50)
,ContactLastName varchar(50)
,StatusName varchar(25)
)
insert into #ResultTempTable (
ROWNUM
,ORGKEY
,EULAVersionKey
,EULAVersionNumber
,EULAVersionUpdateDate
,STATUSKEY
,DocumentFileName
,DocumentGUID
,DocumentTypeName
,DocumentDescription
,ContactKey
,ContactFirstName
,ContactMiddleName
,ContactLastName
,StatusName
)
(
SELECT
ROW_NUMBER() OVER (ORDER BY
CASE
WHEN (#SortCol = 'EULAVersionNumber' AND
#SortOrder = 'ASC') THEN V.EULAVersionNumber
END ASC,
CASE
WHEN (#SortCol = 'EULAVersionNumber' AND
#SortOrder = 'DESC') THEN V.EULAVersionNumber
END DESC,
CASE
WHEN (#SortCol = 'DocumentFileName' AND
#SortOrder = 'ASC') THEN V.DocumentFileName
END ASC,
CASE
WHEN (#SortCol = 'DocumentFileName' AND
#SortOrder = 'DESC') THEN V.DocumentFileName
END DESC,
CASE
WHEN (#SortCol = 'EULAVersionUpdateDate' AND
#SortOrder = 'ASC') THEN convert(date, V.EULAVersionUpdateDate,103)
END ASC,
CASE
WHEN (#SortCol = 'EULAVersionUpdateDate' AND
#SortOrder = 'DESC') THEN convert(date, V.EULAVersionUpdateDate,103)
END DESC,
CASE
WHEN (#SortCol = 'ContactFirstName' AND
#SortOrder = 'ASC') THEN V.ContactFirstName
END ASC,
CASE
WHEN (#SortCol = 'ContactFirstName' AND
#SortOrder = 'DESC') THEN V.ContactFirstName
END DESC,
CASE
WHEN (#SortCol = 'StatusKey' AND
#SortOrder = 'ASC') THEN V.StatusKey
END ASC,
CASE
WHEN (#SortCol = 'StatusKey' AND
#SortOrder = 'DESC') THEN V.StatusKey
END DESC
) AS ROWNUM
,[ORGKEY]
,[EULAVersionKey]
,[EULAVersionNumber]
,[EULAVersionUpdateDate]
,[STATUSKEY]
,[DocumentFileName]
,[DocumentGUID]
,[DocumentTypeName]
,[DocumentDescription]
,[ContactKey]
,[ContactFirstName]
,[ContactMiddleName]
,[ContactLastName]
,[StatusName]
FROM (
SELECT
EUV.[ORGKEY]
,EUV.[EULAVersionKey]
,EUV.[EULAVersionNumber]
,EUV.[EULAVersionUpdateDate]
,EUV.[STATUSKEY]
,DOC.[DocumentFileName]
,DOC.[DocumentGUID]
,DOC.[DocumentDescription]
,DOCTYP.[DocumentTypeName]
,CN.[ContactKey]
,CN.[ContactFirstName]
,CN.[ContactMiddleName]
,CN.[ContactLastName]
,ST.[StatusName]
FROM [dbo].[EULAVersions] EUV
JOIN [dbo].[Documents] DOC
ON DOC.[DocumentKey] =EUV.[DocumentKey]
JOIN[dbo].[DocumentTypes] DOCTYP
ON DOCTYP.[DocumentTypeKey]=EUV.[DocumentTypeKey]
JOIN [dbo].[UserContacts] UC
ON UC.[UserKey]=EUv.[EULAVersionUpdatedBy]
JOIN [dbo].[Contacts] CN
ON CN.[ContactKey]=UC.[ContactKey]
JOIN [dbo].[StatusTypes] ST
ON ST.[StatusKey]=EUV.[StatusKey]
WHERE
EUV.[ORGKEY]=#OrgKey --AND EUV.[StatusKey]=#StatusKey
AND EUV.[EULAVersionNumber] LIKE '%' + ISNULL(#EULAVersionNumber, EULAVersionNumber) + '%'
AND EUV.[EULAVersionNumber] LIKE '%' + ISNULL(#GlobalSearch, EULAVersionNumber) + '%') AS V
)
IF (#PageNumber <> 0)
BEGIN
SELECT
#TotalRows = COUNT(1)
FROM #ResultTempTable AS CPC
WHERE ROWNUM BETWEEN #FirstRec AND CASE
WHEN #PageNumber = 0 THEN #TotalRows
ELSE #LastRec
END
SELECT
[ORGKEY]
,[EULAVersionKey]
,[EULAVersionNumber]
,[EULAVersionUpdateDate]
,[STATUSKEY]
,[DocumentFileName]
,[DocumentGUID]
,[DocumentDescription]
,[DocumentTypeName]
,[ContactFirstName]
,[ContactMiddleName]
,[ContactLastName]
,[StatusName]
FROM #ResultTempTable AS CPC
WHERE ROWNUM BETWEEN #FirstRec AND CASE
WHEN #PageNumber = 0 THEN #TotalRows
ELSE #LastRec
END
ORDER BY ROWNUM ASC
END
ELSE
BEGIN
SELECT
#TotalRows = COUNT(1)
FROM #ResultTempTable AS CPC
SELECT
[ORGKEY]
,[EULAVersionKey]
,[EULAVersionNumber]
,[EULAVersionUpdateDate]
,[STATUSKEY]
,[DocumentFileName]
,[DocumentGUID]
,[DocumentDescription]
,[DocumentTypeName]
,[ContactFirstName]
,[ContactMiddleName]
,[ContactLastName]
,[StatusName]
FROM #ResultTempTable AS CPC
ORDER BY ROWNUM ASC
END
SELECT #TotalRows AS TotalCount
IF OBJECT_ID('tempdb..#ResultTempTable') IS NOT NULL
DROP TABLE #ResultTempTable
END
--FilterTypes Details
CREATE TYPE [dbo].[FilterTypes] AS TABLE(
[FieldName] [varchar](100) NOT NULL,
[Value] [varchar](800) NOT NULL,
[IsGlobal] [bit] NULL DEFAULT ((0))
)
Sample Exec
declare #FilterParams AS FilterTypes;
insert into #FilterParams values('EULAVersions','',1)
exec [usp_GetEULADetails] 2,#FilterParams,'EULAVersionUpdateDate','desc',0,0
Obtained Result
Expected Result
declare #FilterParams AS FilterTypes;
insert into #FilterParams values('EULAVersions','',1)
exec [usp_GetEULADetails] 2,#FilterParams,'ContactFirstName','desc',0,0
The issue I face is how to properly sort the data when data type is of datetime ?
Thanks for the help in advance....
You are converting 'V.EULAVersionUpdateDate' to date DataType (...THEN convert(date, V.EULAVersionUpdateDate,103)) , use DATETIME Datatype to convert
THEN convert(DateTime, V.EULAVersionUpdateDate,103)

Query taking too long to execute with scalar function in where clause

Below is my scalar function:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [CheckClients]
(
#UserId Varchar(3),
#DbrNo varchar(10),
#V_DBR_CLIENT varchar(6)
)
RETURNS int
AS
BEGIN
Declare #Flag int
set #Flag=1
if(#V_DBR_CLIENT='XXXXXX')
BEGIN
if((select COUNT(USR_CLI)
from USRAGYCLI
inner join DBR on DBR_CLIENT = USR_CLI
where USR_CODE = #UserId and DBR_SERIES like #DbrNo +'T') <>
(select COUNT(DBR_CLIENT)
from DBR
where DBR_SERIES like #DbrNo + 'T') OR
(select COUNT(DBR_CLIENT)
from DBR
where DBR_SERIES like #DbrNo +'T') <= 0)
BEGIN
set #Flag=0
END
END
RETURN #Flag
END
This is my stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [SEL_CLI]
#V_USER_ID VARCHAR(3),
#V_NUMBER_OF_ROWS INT,
#V_STARTS_WITH INT
AS
BEGIN
CREATE TABLE #tmpDbrNo
(
Code VARCHAR(10),
Name VARCHAR(100),
NumberOfDebtors int,
rownum int
)
;WITH Temp AS
(
SELECT
CLT_NO AS Code,
CLT_NAME AS Name,
COUNT(DBR_NO) AS NumberOfDebtors
FROM
DBR
JOIN
USRAGYCLI ON DBR_CLIENT = USR_AGY_CLI
JOIN
CLT ON DBR_CLIENT = CLT_NO
WHERE
AND USR_CODE = #V_USER_ID
AND 1 = CheckClients(#V_USER_ID, DBR_NO, DBR_CLIENT)
GROUP BY
CLT_NO, CLT_NAME
)
INSERT INTO #tmpDbrNo
SELECT
Code, Name, NumberOfDebtors,
ROW_NUMBER() OVER (ORDER by Code) rownum
FROM
Temp
SELECT
Code, Name, NumberOfDebtors
FROM
#tmpDbrNo
WHERE
rownum BETWEEN #V_STARTS_WITH AND #V_STARTS_WITH + #V_NUMBER_OF_ROWS
END
Above query takes about 25 sec to execute which is too long to wait. And if I comment out the line where I have called the scalar function in the where clause, it takes 0 secs to execute the query.
Can anybody suggest better way which may take minimum secs to execute the query? I have tried to put call to function in case like as below, but no success.
AND 1 = CASE WHEN DBR_CLIENT='XXXXXX' THEN CheckClients(#V_USER_ID,DBR_NO,DBR_CLIENT) ELSE 1 END
This is just a shot in the dark because we were not provided with any ddl or much to work with. I think I interpreted the existing logic in your scalar function correctly. As a general rule you should probably avoid using flags. This is a very old school mindset and is not suited to relational data very well at all. I suspect this could be greatly improved with an understanding of the actual requirements but this is the best I could do with the limited details.
CREATE FUNCTION [CheckClients]
(
#UserId Varchar(3),
#DbrNo varchar(10),
#V_DBR_CLIENT varchar(6)
)
RETURNS table as return
with RowCounts as
(
select
(
select COUNT(DBR_CLIENT)
from DBR
where DBR_SERIES like #DbrNo + 'T'
) as ClientCount
,
(
select COUNT(USR_CLI)
from USRAGYCLI u
inner join DBR d on d.DBR_CLIENT = u.USR_CLI
where u.USR_CODE = #UserId
and d.DBR_SERIES like #DbrNo +'T'
) as UserCount
)
select case
when #V_DBR_CLIENT = 'XXXXXX' then
Case when rc.UserCount <> rc.ClientCount then 0
when rc.ClientCount < 0 then 0
else 1
end
else 1
end as Flag
from RowCounts rc
You can optimize your scalar function query to reduce doing multiple read. Like:
ALTER FUNCTION [CheckClients] (
#UserId VARCHAR(3),
#DbrNo VARCHAR(10),
#V_DBR_CLIENT VARCHAR(6)
)
RETURNS INT
AS
BEGIN
DECLARE #Flag INT
SET #Flag = 1
IF (#V_DBR_CLIENT = 'XXXXXX')
BEGIN
DECLARE #Count INT = ISNULL((
SELECT COUNT(DBR_CLIENT)
FROM DBR
WHERE DBR_SERIES LIKE #DbrNo + 'T'
), 0);
IF (
(ISNULL((
SELECT COUNT(USR_CLI)
FROM USRAGYCLI
INNER JOIN DBR ON DBR_CLIENT = USR_CLI
WHERE USR_CODE = #UserId
AND DBR_SERIES LIKE #DbrNo + 'T'
), 0) <> #Count)
OR (#Count <= 0)
)
BEGIN
SET #Flag = 0
END
END
RETURN #Flag
END
Also, you need to study your execution plan of the query to find out where the query is having high cost of execution time. And create non-clustered index if necessary.
-- EDITED LATER --
The non-Sargable Problem (Calling Scalar Function):
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [SEL_CLI]
#V_USER_ID VARCHAR(3),
#V_NUMBER_OF_ROWS INT,
#V_STARTS_WITH INT
AS
BEGIN
CREATE TABLE #tmpDbrNo
(
Code VARCHAR(10),
Name VARCHAR(100),
NumberOfDebtors int,
rownum int
)
;WITH Temp AS
(
SELECT
CLT_NO AS Code,
CLT_NAME AS Name,
COUNT(DBR_NO) AS NumberOfDebtors
FROM
DBR
JOIN
USRAGYCLI ON DBR_CLIENT = USR_AGY_CLI
JOIN
CLT ON DBR_CLIENT = CLT_NO
WHERE
USR_CODE = #V_USER_ID
AND 1 =
(CASE
WHEN (#V_DBR_CLIENT = 'XXXXXX') THEN
(CASE
WHEN (
ISNULL((
SELECT COUNT(USR_CLI)
FROM USRAGYCLI
INNER JOIN DBR ON DBR_CLIENT = USR_CLI
WHERE USR_CODE = #UserId
AND DBR_SERIES LIKE #DbrNo + 'T'
), 0) <> ISNULL((
SELECT COUNT(DBR_CLIENT)
FROM DBR
WHERE DBR_SERIES LIKE #DbrNo + 'T'
), 0)
)
OR (ISNULL((
SELECT COUNT(DBR_CLIENT)
FROM DBR
WHERE DBR_SERIES LIKE #DbrNo + 'T'
), 0) <= 0)
THEN 0
ELSE 1
END)
ELSE 1
END)--CheckClients(#V_USER_ID, DBR_NO, DBR_CLIENT)
GROUP BY
CLT_NO, CLT_NAME
)
INSERT INTO #tmpDbrNo
SELECT
Code, Name, NumberOfDebtors,
ROW_NUMBER() OVER (ORDER by Code) rownum
FROM
Temp
SELECT
Code, Name, NumberOfDebtors
FROM
#tmpDbrNo
WHERE
rownum BETWEEN #V_STARTS_WITH AND #V_STARTS_WITH + #V_NUMBER_OF_ROWS
END
As you can see, the scalar function can be included in the same query, but if you study the function nicely than it is clear that the query in scalar function is not fully dependent on the query in the store procedure. It is making count and will reread and manipulate the data from the table every time.
So, with this type of query making non-Sargable to Sargable will not improve the performance. The possible solution to the problem will be
To previously add the required data in the table and check from there.
To study your query plans(Design and Execution) and optimize it accordingly.

SQL Server Full Text Search Very Slow

I have a stored procedure that searches a table which has about 200000+ rows with full text FREETEXT.
Here is the basics of it:
declare #searchKey varchar(150)
if #searchKey Is Null OR LEN(#searchKey)=0
Set #searchKey='""';
Set #searchKey='car';
declare #perPage int
Set #perPage=40
declare #pageNo int
Set #pageNo=1
declare #startIndex int,#endIndex int;
Set #startIndex=#perPage*#pageNo-#perPage+1;
Set #endIndex=#perPage*#pageNo;
Select totalItems
--i pull other colums as well
from (
Select Row_Number() over(order by CreateDate DESC) As rowNumber
,COUNT(*) OVER() as totalItems
--other columns are pulled as well
from MyTable P
Where
#searchKey='""'
OR FreeText((P.Title,P.Description),#searchKey)
) tempData
--where rowNumber>=#startIndex AND rowNumber<=#endIndex
where
rowNumber>=CASE WHEN #startIndex>0 AND #endIndex>0 THEN #startIndex ELSE rowNumber END
AND rowNumber<=CASE WHEN #startIndex>0 AND #endIndex>0 THEN #endIndex ELSE rowNumber END
order by rowNumber
The problem is its running slower then i would like it. Its taking about 3 seconds to load the page. Same page was loading in less then 1 sec when i was using like operator.
In my experience, full text index functions do not work well in a clause that contains an "OR" operator. I have had to get the same behavior by adjusting my query to use a UNION. Try this and see if you can get better performance.
declare #searchKey varchar(150)
if #searchKey Is Null OR LEN(#searchKey)=0
Set #searchKey='""';
Set #searchKey='car';
declare #perPage int
Set #perPage=40
declare #pageNo int
Set #pageNo=1
declare #startIndex int,#endIndex int;
Set #startIndex=#perPage*#pageNo-#perPage+1;
Set #endIndex=#perPage*#pageNo;
Select totalItems
--i pull other colums as well
from (
Select Row_Number() over(order by CreateDate DESC) As rowNumber
,COUNT(*) OVER() as totalItems
--other columns are pulled as well
from
(
select * from
MyTable A
Where
#searchKey='""'
UNION
select * from MyTable B
where FreeText((B.Title,B.Description),#searchKey)
) as innerTable
) tempData
--where rowNumber>=#startIndex AND rowNumber<=#endIndex
where
rowNumber>=CASE WHEN #startIndex>0 AND #endIndex>0 THEN #startIndex ELSE rowNumber END
AND rowNumber<=CASE WHEN #startIndex>0 AND #endIndex>0 THEN #endIndex ELSE rowNumber END
order by rowNumber

Why do my results not stay consistent?

I have the following stored procedure that I working on. I have noticed that every 5th or 6th time I refresh my results there are new values in there. Which considering that the data is in a static environment and no one is making any changes to the data at this time I really can't understand. Can someone please enlighten me as to why I would see different results even though I am running this procedure with the exact same parameters. I even tried it in query analyzer and still see the same strange results.
I am running in Sql 2008.
Here is the proc:
ALTER PROCEDURE [dbo].[SelectSearchBy_Category]
#userId INT,
#page INT,
#results INT,
#category NVARCHAR(50),
#searchTerm NVARCHAR(200) = NULL
AS
BEGIN
SET NOCOUNT ON
SET ROWCOUNT #results
DECLARE #categoryId INT
IF (#category IS NOT NULL) BEGIN
SET #categoryId = ( SELECT categoryId FROM Category WHERE categoryDescription = #category )
END
DECLARE #rowEnd INT
DECLARE #rowStart INT
SET #rowEnd = (#page * #results)
SET #rowStart = #rowEnd - #results
;WITH OrderedItems AS
(
SELECT
i.itemId,
title,
i.[description],
i.url,
i.categoryId,
i.ratingId,
i.requirements,
ISNULL(i.rating, 0) AS tating,
ISNULL(i.raters, 0) AS raters,
i.urlFriendlyPath,
ROW_NUMBER() OVER
(
ORDER BY i.dateAdded, (ISNULL(i.rating, 0) * ISNULL(i.raters, 0))
) AS RowNumber
FROM
[dbo].[Item] i
LEFT JOIN
UserItemIgnore uii ON uii.itemId = i.itemId AND uii.userId = #userId
INNER JOIN
ItemLanguage il ON il.itemId = i.itemId
WHERE
(#searchTerm IS NULL OR a.title LIKE '%' + #searchTerm + '%') AND
i.categoryId = #categoryId AND
il.languageId = 1 AND
uii.itemId IS NULL
)
SELECT *
FROM OrderedItems
WHERE RowNumber BETWEEN #rowStart AND #rowEnd
END
You will probably have consistent results if you put an order by clause in your OrderedItems temporary table definition.
Try using
ROW_NUMBER() OVER (ORDER BY i.dateAdded,
(ISNULL(i.rating, 0) * ISNULL(i.raters, 0)),
i.itemId)
i.itemId will act as a tie breaker to ensure that the results of ROW_NUMBER are deterministic in the event you have rows with equal ranks for i.dateAdded, (ISNULL(i.rating, 0) * ISNULL(i.raters, 0))

Resources