SQL Row_Number() is very slow - sql

I have one complex query in SQL which is taking long time, i.e. more than 1 min, to produce the results due to Row_Number() in SQL. I am unable to find a better option in place of that. Below is my SQL query.
Declare #AgreementNumber nvarchar(500)
Declare #SerialNo nvarchar(100)
Declare #EmailId nvarchar(1000)
Declare #CountryId nvarchar(10)
Declare #SaleType tinyint
Declare #CompanyName nvarchar(255)
Declare #PONo numeric(18,0)
Declare #Status tinyint
Declare #POCDeliveryType nvarchar(10)
Declare #FromDate datetime
Declare #ToDate datetime
Declare #CurrentPage numeric
Declare #PageSize numeric
Declare #FileId numeric(18,0)
Declare #PurchaseOrg nvarchar(4)
set #AgreementNumber =''
set #SerialNo =''
set #EmailId =''
set #CountryId ='0014'
set #SaleType=0
set #CompanyName =''
set #PONo= 0
set #Status =0
set #POCDeliveryType =''
set #FromDate =''
set #ToDate =''
set #CurrentPage =1
set #PageSize =100000
set #FileId =0
set #PurchaseOrg =''
----For Paging---------------------------------------------------------------------
Declare #TotalRecord numeric
Declare #StartRow numeric
Declare #EndRow numeric
Set #StartRow=(#PageSize * (#CurrentPage-1)) + 1
Set #EndRow=(#PageSize * (#CurrentPage))
-----------------------------------------------------------------------------------
Select #TotalRecord=Count(*)
From OrderHeader OH inner join OrderLine OL on OH.OrderID=OL.OrderId
Where (OH.CountryId=#CountryId or ISNULL(#CountryId,'')='') and
(OH.PurchaseOrg=#PurchaseOrg or ISNULL(#PurchaseOrg,'')='') and
(SaleType=#SaleType or ISNULL(#SaleType,0)=0) and
(CompanyName like '%'+ isnull(#CompanyName,'') + '%'or isnull(#CompanyName,'')='')and
(PurchaseOrderNo = #PONo or isnull(#PONo,0)=0)and
(OL.Status=#Status or ISNULL(#Status,0)=0) and
(POCDeliveryPreference=#POCDeliveryType or ISNULL(#POCDeliveryType,'')='') and
((CONVERT(VARCHAR(12),OH.Created,112)>=CONVERT(VARCHAR(12),#FromDate,112))or (Isnull(#FromDate,'')=''))and
(CONVERT(VARCHAR(12),OH.Created,112)<=CONVERT(VARCHAR(12),#ToDate,112)or (Isnull(#ToDate,'')='')) and
(OL.SerialNumber like '%'+#SerialNo+'%' or ISNULL(#SerialNo,'') ='') and
(OL.AgreementNumber like '%'+#AgreementNumber+'%' or ISNULL(#AgreementNumber,'') ='') and
(OH.EmailAddress like '%'+#EmailId+'%' or ISNULL(#EmailId,'') ='') and
(OH.FileId=#FileId or isnull(#FileId,0)=0)
Select *, #TotalRecord as TotalRecord From
(
**Select ROW_NUMBER() OVER
(
Order By OH.Created Desc
)as Row**,OH.OrderID,ISNULL(CustomerId,'') as CustomerId,ISNULL(OH.CountryId,'') as CountryId,SaleType,HwPurchaseDate,ISNULL(AddressLine1,'') as
AddressLine1,ISNULL(AddressLine2,'') as AddressLine2,ISNULL(City,'') as City,ISNULL([State],'') as [State],ISNULL(Zip,'') as Zip,
ISNULL(County,'') as County,ISNULL(CompanyName,'') as CompanyName,ISNULL(EmailAddress,'') as EmailId,ISNULL(FirstName,'') as FirstName,
ISNULL(LastName,'') as LastName,ISNULL(PrimaryPhone,'') as Phone,ISNULL(PurchaseOrderNo,0) as POno,ISNULL(OL.[Status],0) as [Status],POCDeliveryPreference,
POCLanguage,CustPOReference,ISNULL(CurrencyCode,'') as CurrencyCode,TransactionId,ISNULL(OH.DeliveryStatus,0) as DeliveryStatus,
isnull(IPAddress,'') as IPAddress, ISNULL(InTouchSessionId,'') as InTouchSessionId, ISNULL(InTouchUserId,'') as InTouchUserId,
ISNULL(SourceSystem,0) as SourceSystem,SalesOrderDate,OH.Created,isnull(OL.SerialNumber,'') as SerialNumber,ISNULL(OL.AgreementNumber,'') as AgreementNumber,
ISNULL(OL.PurchaseOrderNoLineLevel,'') as PurchaseOrderNoLineLevel,ISNULL(OL.ProductName,'') as ProductName,ISNULL(OL.OrderQty,0) as OrderQty,
ISNULL(OL.ManufPartNo,'') as ManufPartNo,isnull(OL.VendorStatus,'') as VendorStatus, isnull(OL.StatusRemark, '') as StatusRemark
From OrderHeader OH inner join OrderLine OL on OH.OrderID=OL.OrderId
Where (OH.CountryId=#CountryId or ISNULL(#CountryId,'')='') and
(OH.PurchaseOrg=#PurchaseOrg or ISNULL(#PurchaseOrg,'')='') and
(SaleType=#SaleType or ISNULL(#SaleType,0)=0) and
(CompanyName like '%'+ isnull(#CompanyName,'') + '%'or isnull(#CompanyName,'')='')and
(PurchaseOrderNo = #PONo or isnull(#PONo,0)=0)and
(OL.Status=#Status or ISNULL(#Status,0)=0) and
(POCDeliveryPreference=#POCDeliveryType or ISNULL(#POCDeliveryType,'')='') and
((CONVERT(VARCHAR(12),OH.Created,112)>=CONVERT(VARCHAR(12),#FromDate,112))or (Isnull(#FromDate,'')=''))and
(CONVERT(VARCHAR(12),OH.Created,112)<=CONVERT(VARCHAR(12),#ToDate,112)or (Isnull(#ToDate,'')='')) and
(OL.SerialNumber like '%'+#SerialNo+'%' or ISNULL(#SerialNo,'') ='') and
(OL.AgreementNumber like '%'+#AgreementNumber+'%' or ISNULL(#AgreementNumber,'') ='') and
(OH.EmailAddress like '%'+#EmailId+'%' or ISNULL(#EmailId,'') ='') and
(OH.FileId=#FileId or isnull(#FileId,0)=0)
)
as RowResults
Where Row between #StartRow AND #EndRow
I have highlighted that part which is taking so long time.

OH.CountryId = #CountryId or ISNULL(#CountryId, '') = '')
Generally these sorts of things should be avoided. It prevents SQL Server from performing an index seek and instead performs an index scan. Can you use dynamic SQL instead and include in the where OH.CountryId = #CountryId only when its specified?

Replace
((CONVERT(VARCHAR(12),OH.Created,112)>=CONVERT(VARCHAR(12),#FromDate,112))
(CONVERT(VARCHAR(12),OH.Created,112)<=CONVERT(VARCHAR(12),#ToDate,112)
by Cast(OH.Created as date)>=Cast(#FromDate as date)
Cast(OH.Created as date)<=Cast(#FromDate as date)
the convert creating a plan that is not the best.

There are lots of issue in query itself
(1) Using same query twice
(2) trying to catch all query(inefficiently ) that can produce horrible plan
(3) Using predicate like OH.EmailAddress like '%'+#EmailId+'%'
(4) inefficient paging
Declare #AgreementNumber nvarchar(500)
Declare #SerialNo nvarchar(100)
Declare #EmailId nvarchar(1000)
Declare #CountryId nvarchar(10)
Declare #SaleType tinyint
Declare #CompanyName nvarchar(255)
Declare #PONo numeric(18,0)
Declare #Status tinyint
Declare #POCDeliveryType nvarchar(10)
Declare #FromDate datetime
Declare #ToDate datetime
Declare #CurrentPage numeric
Declare #PageSize numeric
Declare #FileId numeric(18,0)
Declare #PurchaseOrg nvarchar(4)
set #AgreementNumber =''
set #SerialNo =''
set #EmailId =''
set #CountryId ='0014'
set #SaleType=0
set #CompanyName =''
set #PONo= 0
set #Status =0
set #POCDeliveryType =''
set #FromDate =''
set #ToDate =''
set #CurrentPage =1
set #PageSize =100000
set #FileId =0
set #PurchaseOrg =''
----For Paging---------------------------------------------------------------------
Declare #TotalRecord numeric
Declare #StartRow numeric
Declare #EndRow numeric
Set #StartRow=(#PageSize * (#CurrentPage-1)) + 1
Set #EndRow=(#PageSize * (#CurrentPage))
-----------------------------------------------------------------------------------
--Select #TotalRecord=Count(*)
--From OrderHeader OH inner join OrderLine OL on OH.OrderID=OL.OrderId
--Where (OH.CountryId=#CountryId or ISNULL(#CountryId,'')='') and
--(OH.PurchaseOrg=#PurchaseOrg or ISNULL(#PurchaseOrg,'')='') and
-- (SaleType=#SaleType or ISNULL(#SaleType,0)=0) and
-- (CompanyName like '%'+ isnull(#CompanyName,'') + '%'or isnull(#CompanyName,'')='')and
-- (PurchaseOrderNo = #PONo or isnull(#PONo,0)=0)and
-- (OL.Status=#Status or ISNULL(#Status,0)=0) and
-- (POCDeliveryPreference=#POCDeliveryType or ISNULL(#POCDeliveryType,'')='') and
-- ((CONVERT(VARCHAR(12),OH.Created,112)>=CONVERT(VARCHAR(12),#FromDate,112))or (Isnull(#FromDate,'')=''))and
-- (CONVERT(VARCHAR(12),OH.Created,112)<=CONVERT(VARCHAR(12),#ToDate,112)or (Isnull(#ToDate,'')='')) and
-- (OL.SerialNumber like '%'+#SerialNo+'%' or ISNULL(#SerialNo,'') ='') and
-- (OL.AgreementNumber like '%'+#AgreementNumber+'%' or ISNULL(#AgreementNumber,'') ='') and
-- (OH.EmailAddress like '%'+#EmailId+'%' or ISNULL(#EmailId,'') ='') and
-- (OH.FileId=#FileId or isnull(#FileId,0)=0)
With RowResults as
(
Select ROW_NUMBER() OVER
(
Order By OH.Created Desc
)as Row ,OH.OrderID,ISNULL(CustomerId,'') as CustomerId,ISNULL(OH.CountryId,'') as CountryId,SaleType,HwPurchaseDate,ISNULL(AddressLine1,'') as
AddressLine1,ISNULL(AddressLine2,'') as AddressLine2,ISNULL(City,'') as City,ISNULL([State],'') as [State],ISNULL(Zip,'') as Zip,
ISNULL(County,'') as County,ISNULL(CompanyName,'') as CompanyName,ISNULL(EmailAddress,'') as EmailId,ISNULL(FirstName,'') as FirstName,
ISNULL(LastName,'') as LastName,ISNULL(PrimaryPhone,'') as Phone,ISNULL(PurchaseOrderNo,0) as POno,ISNULL(OL.[Status],0) as [Status],POCDeliveryPreference,
POCLanguage,CustPOReference,ISNULL(CurrencyCode,'') as CurrencyCode,TransactionId,ISNULL(OH.DeliveryStatus,0) as DeliveryStatus,
isnull(IPAddress,'') as IPAddress, ISNULL(InTouchSessionId,'') as InTouchSessionId, ISNULL(InTouchUserId,'') as InTouchUserId,
ISNULL(SourceSystem,0) as SourceSystem,SalesOrderDate,OH.Created,isnull(OL.SerialNumber,'') as SerialNumber,ISNULL(OL.AgreementNumber,'') as AgreementNumber,
ISNULL(OL.PurchaseOrderNoLineLevel,'') as PurchaseOrderNoLineLevel,ISNULL(OL.ProductName,'') as ProductName,ISNULL(OL.OrderQty,0) as OrderQty,
ISNULL(OL.ManufPartNo,'') as ManufPartNo,isnull(OL.VendorStatus,'') as VendorStatus, isnull(OL.StatusRemark, '') as StatusRemark
From OrderHeader OH inner join OrderLine OL on OH.OrderID=OL.OrderId
Where (OH.CountryId=#CountryId or ISNULL(#CountryId,'')='') and
(OH.PurchaseOrg=#PurchaseOrg or ISNULL(#PurchaseOrg,'')='') and
(SaleType=#SaleType or ISNULL(#SaleType,0)=0) and
(CompanyName like '%'+ isnull(#CompanyName,'') + '%'or isnull(#CompanyName,'')='')and
(PurchaseOrderNo = #PONo or isnull(#PONo,0)=0)and
(OL.Status=#Status or ISNULL(#Status,0)=0) and
(POCDeliveryPreference=#POCDeliveryType or ISNULL(#POCDeliveryType,'')='') and
((CONVERT(VARCHAR(12),OH.Created,112)>=CONVERT(VARCHAR(12),#FromDate,112))or (Isnull(#FromDate,'')=''))and
(CONVERT(VARCHAR(12),OH.Created,112)<=CONVERT(VARCHAR(12),#ToDate,112)or (Isnull(#ToDate,'')='')) and
(OL.SerialNumber like '%'+#SerialNo+'%' or ISNULL(#SerialNo,'') ='') and
(OL.AgreementNumber like '%'+#AgreementNumber+'%' or ISNULL(#AgreementNumber,'') ='') and
(OH.EmailAddress like '%'+#EmailId+'%' or ISNULL(#EmailId,'') ='') and
(OH.FileId=#FileId or isnull(#FileId,0)=0)
)
Select max (Row) , * FRom RowResults
Where Row between #StartRow AND #EndRow
option (recompile)
-- quick fix only
-- --if using SQL SERVER 2012
-- OFFSET #StartRow ROWS
--FETCH NEXT #EndRow -#StartRow ROWS ONLY
--if ans still not gaining performance we wold like to see actual exection plan
--(1) Using same query twice DONE
--(2) trying to catch all query(inefficiently ) that can produce horrible plan DONE by option recompile (still you need to understand this use dynamic query if you are not exposed to sql injection)
--(3) Using predicate like OH.EmailAddress like '%'+#EmailId+'%' -- you need to adress this
--(4) inefficient paging -- fetch next is quite fast that row_num .

Related

How to declare variables in T-SQL from select statements

declare #Week nvarchar(16) = quotename(concat('Week',#CID,#SID));
declare #Week2 nvarchar(256) = concat('SELECT TOP 1 [Year],[Week] FROM',#Week,'GROUP BY [Year],[Week] ORDER BY [Year] DESC,[Week] DESC');
exec sp_executesql #Week2
how can I declare #Year and #Week respectively from the result of exec sp_executesql #Week2? (declare as in assign the result values into #year and #week)
Thanks
declare #latestyear NVARCHAR(50), #latestweek NVARCHAR(50)
declare #Week2 nvarchar(256) = 'SELECT TOP 1 #latestyear=[Year],#latestweekweek=[Week] FROM [Week45] GROUP BY [Year],[Week] ORDER BY [Year] DESC,[Week] DESC';
exec sp_executesql #Week2
PRINT #latestyear
PRINT #latestweek
The above doesn't seem to work for me
Also trying to do this in 1 select statement if possible

SQL Server: set 2 column into 1 variable from variable table

Can someone check what is wrong with this codes? I already check the other questions for reference but its still not working.
declare #sourceTable varchar(500)
declare #year varchar(22)
declare #month varchar(3)
declare #test varchar(12)
declare #result varchar(8)
declare #index int
declare #string varchar(15)
set #string = (SELECT DISTINCT TOP 1 REPLACE(dbo.fn_Parsename(WHOLEROW, '|', 0), CHAR(9), '') FROM #temp1)
set #test = (select UPPER(convert(datetime,substring(#string,2,charindex('-',#string,1)-2))))
set #month =(left(#test,3))
set #year = (right(#test,5))
set #result = #month + #year
-- select #result
set #sourceTable = 'gen_048_'+#result
select #sourceTable
declare #string2 varchar(255)
set #string2 = (select convert(varchar(55),refdate)+''-''+convert(varchar(55),refcount) FROM #sourceTable)
select #string2
This is the error
Must declare the table variable "#sourceTable".
You need dynamic query
SET #string2 = 'select convert(varchar(55),refdate)+''-''+convert(varchar(55),refcount) FROM '
+ Quotename(#sourceTable)
EXEC (#string2)
You are not declaring #sourcetable , the error shouts it loud and clear.
Add this At the beginning :
declare #sourcetable varchar(50)
Also, I believe you need to use dynamic SQL for this sort of queries and variable using.
set #string2 = (select convert(varchar(55),refdate)+'-'+convert(varchar(55),refcount) FROM #sourceTable)

Single quote in custom query

I have this procedure for custom paging, search and sort options.
ALTER PROCEDURE [dbo].[stp_OrdersPaginated]
#Name NVARCHAR(50)=NULL,
#OrderNumber NVARCHAR(50)=NULL,
#Status NVARCHAR(50)=NULL,
#OrderBy NVARCHAR(100)=NULL,
#PageNumber INT,
#PageSize INT
AS
BEGIN
SET NOCOUNT ON;
CREATE TABLE #ORDERS
(
[OrderId] Bigint
,[Name] Varchar(100)
,[Status] Varchar(50)
,[CreatedDate] Date
,[OrderNumber] Varchar(100)
,[UserId] Bigint
,[Amount] Decimal
,RowNumber Bigint IDENTITY(1,1)
)
DECLARE #intTotal INT
SET #intTotal = #PageSize * #PageNumber
DECLARE #sSQL NVARCHAR(MAX)
DECLARE #Where NVARCHAR(MAX) = ''
DECLARE #Order NVARCHAR(MAX) = ''
SET #sSQL = 'SELECT dbo.[Order].OrderId, [User].Name, dbo.[Order].Status,
dbo.[Order].CreatedDate, [Order].OrderNumber, dbo.[User].UserId,
dbo.Order.[Amount]
FROM dbo.[Order]
INNER JOIN dbo.User
ON dbo.[User].UserId = dbo.[Order].UserId'
SET #Order =' ORDER BY ' +#OrderBy
IF #Name is not null
SET #Where = #Where + ' AND dbo.[User].Name LIKE ''%'+#Name+'%'''
IF #OrderNumber is not null
SET #Where = #Where + ' AND dbo.[Order].OrderNumber LIKE '''+#OrderNumber+'%'''
IF #Status is not null
SET #Where = #Where + ' AND dbo.[Order].[Status] LIKE '''+#Status+'%'''
IF LEN(#Where) > 0
SET #sSQL = #sSQL + ' WHERE ' + RIGHT(#Where, LEN(#Where)-4)
INSERT INTO #ORDERS
EXECUTE (#sSQL + #Order)
Select [OrderId],[Name],[Status],[CreatedDate],[OrderNumber,[UserId]
,[Amount],RowNumber
From #ORDERS
WHERE RowNumber between ((#PageNumber * #PageSize)-(#PageSize- 1)) AND (#PageNumber * #PageSize)
Declare #TotalRecords Integer
Declare #TotalPage Integer
SELECT #TotalRecords=MAX(RowNumber) from #ORDERS
if(#TotalRecords is not NULL)
begin
if(#TotalRecords%#PageSize = 0)
begin
SET #TotalPage = #TotalRecords/#PageSize
end
else
begin
SET #TotalPage = #TotalRecords/#PageSize + 1
end
end
else
begin
set #TotalPage = 1
end
Select #TotalPage [TotalPages], #TotalRecords [TotalRecords]
DROP Table #ORDERS
END
As you can see one of the Search params is Name. The Procedure works perfectly for all except for Single Quote(') for obvious reason. Example: if I pass O' Brien for name it would fail. Is there any way to handle such single quote values with custom queries on SQL Server?
Your problem stems from not constructing your dynamic SQL in a best-practice manner, which along with making it difficult to construct the correct SQL, is also exposing you to SQL injection attacks.
Essentially, you should never use concatenation when adding parameters to your SQL string. I also use char(37) to represent the % sign, as this way it isn't necessary to escape it with apostrophes.
So your SQL becomes something like
IF #Name is not null
SET #Where += 'AND Name LIKE char(37)+#Name+char(37)'
IF #OrderNumber is not null
SET #Where += ' AND OrderNumber LIKE #OrderNumber+char(37)'
IF #Status is not null
SET #Where += ' AND [Status] LIKE #Status+char(37)'
IF LEN(#Where) > 0
SET #sSQL += ' WHERE ' + RIGHT(#Where, LEN(#Where)-4)
Creating the OrderBy is harder as you cannot parameterise that. if you absolutely trust the value passed in, then your code is okay, but the safest way would be to have something like an if statement that
tests the value passed in and creates the appropriate clause. e.g.
IF #OrderBy = 'status'
SET #Ssql += ' ORDER BY Status'
--Next you need to declare the parameters being included in the dynamic SQL. i'm making up the variable types, as you didn't specify what they were.
declare #params nvarchar(1000) = '#name nvarchar(100), #ordernumber nvarchar(100), #status nvarchar(10)'
--Then you can execute your dynamic SQL, passing to it the parameters provided to your procedure
insert into #temp
ExeCUTE sp_executesql #sSQL, #params, #name, #ordernumber, #status
One other benefit of constructing dynamic SQL in this manner, rather than concatenating strings, is that SQL Server can actually cache the query plan like it does for non-dynamic SQL and you don't have the performance hit that you get when you use concatenation.
Try:
IF #Name is not null
BEGIN
SET #Name = REPLACE(#Name, '''', '''''')
SET #Where = #Where + ' AND dbo.[User].Name LIKE ''%'+#Name+'%'''
END

How to set mutliple variables using SELECT and CASE

I am trying to set variables in my stored procedure when the passed parameter is a certain value like this:
ALTER PROCEDURE [dbo].[Procedure1]
(
#ID int,
#WatchType varchar(100)
)
AS
SET NOCOUNT ON
DECLARE #stock int
DECLARE #price float
DECLARE #details varchar(max)
CASE #WatchType
WHEN 'TIMEX' THEN
(SELECT #stock= Stock,
#price= Price,
#details= Details,
FROM tblWatches WHERE Uid= #ID)
ELSE
END
I keep getting an error when I try to parse it. Does anyone know why?
ALTER PROCEDURE [dbo].[Procedure1]
(
#ID int,
#WatchType varchar(100)
)
AS
SET NOCOUNT ON
DECLARE #stock int
DECLARE #price float
DECLARE #details varchar(max)
IF #WatchType = 'TIMEX'
BEGIN
(SELECT #stock= Stock,
#price= Price,
#details= Details,
FROM tblWatches WHERE Uid = #ID)
END

Must declare the scalar variable?

ALTER PROCEDURE selectedrfp
#from datetime,
#to datetime,
#bdename varchar(max),
#status int
AS
BEGIN
DECLARE #query varchar(max)
set #query='
SELECT RS.[RFP_ICode]
,RS.[Client_ICode]
,RS.[Client_Name]
,RS.[Project_Title]
,RS.[Client_Country]
,RS.[RFP_Technology]
,RS.[RFP_Received_Date]
,RS.[Cgvak_Comh_WishList]
FROM dbo.Cgvak_RFP_Status AS RS
INNER JOIN CGVAK_RFP_Status_Master AS RSM
ON RSM.[RFP_Status_Icode]=RS.[RFP_Status]
INNER JOIN CGVAK_RFP_Users AS RU
ON RU.[RFP_User_ICode]=RS.[BDE_Icode]
WHERE RS.[Cgvak_Comh_WishList]=1
AND RS.[RFP_Received_Date] BETWEEN #from AND #to
-- AND RS.[BDE_Name]=#bdename
AND RSM.[RFP_Status_ICode]=#status
AND RU.[RFP_User_Type] IN(1,2,3)'
DECLARE #withoutbde varchar(max)
SET #withoutbde='ORDER BY RS.[RFP_Received_Date] DESC'
DECLARE #withbde varchar(max)
SET #withbde='AND RS.[BDE_Name]=#bdename ORDER BY RS.[RFP_Received_Date] DESC'
IF(#bdename='All')
EXEC (#query + #withoutbde )
ELSE IF(#bdename!='All')
EXEC (#query + #withbde )
END
EXEC selectedrfp #from='2010/01/01',#to='2011/06/22',#bdename='chad',#status=4
The local variables #bdename etc are not in scope in the dynamic SQL.
You should use sp_executesql to parameterise them properly. Don't concatenate them.