Hopefully somebody can help me with a MS SQL function performance issue.
I have the following function which matches users in the same table. User A is searching for users he can match with. If he finds a User B that matches his criteria, User B checks wether User A matches their criteria. If they have already matched they are in the ExcludedCandidates table.
The problem I have is that the query takes too long.
I tried a lot of things but have no more ideas on how to improve it.
Maybe some indices or changes to the query could help here.
Any help would be appreaciated.
create function [dbo].[FindUser](#UserId uniqueidentifier, #dis int, #gen int, #age datetime, #f int, #ti int, #s int, #La float, #Lo float)
returns #T table (UserId uniqueidentifier, Profile nvarchar(MAX), Filter nvarchar(MAX), F int, I bit, IsP bit)
as
BEGIN
declare #Tmp table(UserId uniqueidentifier, a nvarchar(MAX), b nvarchar(MAX), c int, d bit, e bit)
DECLARE #source geography
select #source = geography::Point(#La, #Lo, 4326)
insert into #Tmp
SELECT TOP 10 U.UserId, U.Profile, U.F, #s as Fil, U.Ve, U.TT from Users AS U WITH (NOLOCK)
WHERE ((#gen & U.Ge) != 0) AND
(Sea = 1 OR Sea = 2) AND
#s = U.Sear AND
U.La is not null and U.Lon is not null AND
#dis >= (#source.STDistance(geography::Point(U.La, U.Lon, 4326)) / 1000) AND
(#f <= YEAR(GETUTCDATE()) - YEAR(#age)) AND (#ti >= YEAR(GETUTCDATE()) - YEAR(#age)) AND
U.UserId != #UserId
and not exists
(select TOP 1 IC1.InitiatorUserId from ExcludedCandidates AS IC1 with (NOLOCK)
where (IC1.InitiatorUserId = #UserId and IC1.PartnerUserId = U.UserId) OR
(IC1.InitiatorUserId = U.UserId and IC1.PartnerUserId = #UserId))
and exists(
SELECT U.UserId from Users UserP with (NOLOCK)
WHERE ((JSON_VALUE(UserP.Filter, '$.gender') & U.Ge) != 0) AND
(Sea = 1 OR Sea = 2) AND
#s = Sea AND
(JSON_VALUE(UserP.Filter, '$.age.lo') <= YEAR(GETUTCDATE()) - YEAR(#age)) AND (JSON_VALUE(UserP.Filter, '$.age.up') >= YEAR(GETUTCDATE()) - YEAR(#age)) AND
JSON_VALUE(UserP.Filter, '$.di') >= (geography::Point(UserP.La, UserP.Lon, 4326).STDistance(geography::Point(#La, #Lo, 4326)) / 1000) AND
UserId = U.UserId
)
order by U.Sea DESC
insert into #T
select UserId, a, b, c, d, e from #Tmp
return
END
Indexes we use
CREATE NONCLUSTERED INDEX [NonClusteredIndex_Users_Search] ON [dbo].[Users]
(
[Sea] DESC
)WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [NonClusteredIndex_Users_SearchSearchState] ON [dbo].[Users]
(
[Sear] ASC,
[Sea] ASC
)
INCLUDE ( [Filter],
[La],
[Lon]) WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
GO
What you have here is a multi-line table value function, which are known to perform poorly. You will very likely find that converting it to an inline-table value function will provide much better performance.
Due to not having the definition of the other objects, I can't test this (so any syntax errors have not been checked), however, this is a literal conversion to a inline table-value function. There is, however, likely more you can do here (for example #dis >= (V.srv.STDistance(geography::Point(U.La, U.Lon, 4326)) / 1000) is not SARGable), but without a true goal, along with sample data and expected results, this would be impossible to do more than guess at:
CREATE FUNCTION [dbo].[FindUser](#UserId uniqueidentifier, #dis int, #gen int, #age datetime, #f int, #ti int, #s int, #La float, #Lo float)
RETURNS table --#T table (UserId uniqueidentifier, Profile nvarchar(MAX), Filter nvarchar(MAX), F int, I bit, IsP bit)
AS RETURN
--declare #Tmp table(UserId uniqueidentifier, a nvarchar(MAX), b nvarchar(MAX), c int, d bit, e bit)
--
--DECLARE #source geography
--select #source = geography::Point(#La, #Lo, 4326)
SELECT TOP (10)
U.UserId,
U.Profile,
U.F,
#s as Fil,
U.Ve,
U.TT
FROM dbo.Users AS U-- WITH (NOLOCK) --Unless you really undersatnd what NOLOCK does, you shou.dn't be using this.
CROSS APPLY(VALUES(geography::Point(#La, #Lo, 4326)))V(src)
WHERE ((#gen & U.Ge) != 0)
AND (Sea = 1 OR Sea = 2)
AND #s = U.Sear
AND U.La IS NOT NULL
and U.Lon IS NOT NULL
AND #dis >= (V.srv.STDistance(geography::Point(U.La, U.Lon, 4326)) / 1000)
AND (#f <= YEAR(GETUTCDATE()) - YEAR(#age)) AND (#ti >= YEAR(GETUTCDATE()) - YEAR(#age))
AND U.UserId != #UserId
AND NOT EXISTS (SELECT 1 --No need for a TOP (1) here
FROM ExcludedCandidates AS IC1-- with (NOLOCK) --Unless you really undersatnd what NOLOCK does, you shou.dn't be using this.
WHERE (IC1.InitiatorUserId = #UserId AND IC1.PartnerUserId = U.UserId)
OR (IC1.InitiatorUserId = U.UserId AND IC1.PartnerUserId = #UserId))
AND exists(SELECT 1
from Users UserP-- with (NOLOCK) --Unless you really undersatnd what NOLOCK does, you shou.dn't be using this.
WHERE ((JSON_VALUE(UserP.Filter, '$.gender') & U.Ge) != 0)
AND (Sea = 1 OR Sea = 2)
AND #s = Sea
AND (JSON_VALUE(UserP.Filter, '$.age.lo') <= YEAR(GETUTCDATE()) - YEAR(#age))
AND (JSON_VALUE(UserP.Filter, '$.age.up') >= YEAR(GETUTCDATE()) - YEAR(#age))
AND JSON_VALUE(UserP.Filter, '$.di') >= (geography::Point(UserP.La, UserP.Lon, 4326).STDistance(geography::Point(#La, #Lo, 4326)) / 1000)
AND UserId = U.UserId)
ORDER BY U.Sea DESC;
GO
I think I would change this to a stored procedure if you don't have an explicit reason you need a function. You create a function to include the results in a select statement with another table. It doesn't sound like that's what you are doing here ...
Also, I would separate the logic that parses the JSON data from the table and perform that apart from the main query. Those parsing statements are probably taking you the most time. You can potentially optimize that separately.
Last, ensure you have an index on your ExcludedCandidates tables like the following ...
CREATE INDEX [IX_ExcludedCandidates_UserIDs] ON [dbo].[ExcludedCandidates] (
InitiatorUserId, PartnerUserId
)
Here's the PROC definition I would try ...
CREATE PROC [dbo].[FindUser] (
#UserId uniqueidentifier,
#dis int,
#gen int,
#age datetime,
#f int,
#ti int,
#s int,
#La float,
#Lo float
) AS BEGIN
DECLARE #source geography = geography::Point(#La, #Lo, 4326)
SELECT
*
INTO
#UserInfo
FROM (
SELECT
UserId,
La,
Lon,
Sea,
JSON_VALUE(Filter, '$.gender') gender,
JSON_VALUE(Filter, '$.age.lo') age_lo,
JSON_VALUE(Filter, '$.age.up') age_up,
JSON_VALUE(Filter, '$.di') di
FROM
Users
) S
WHERE
di >= (geography::Point(La, Lon, 4326).STDistance(geography::Point(#La, #Lo, 4326)) / 1000)
SELECT TOP 10
U.UserId,
U.Profile,
U.F,
#s AS Fil,
U.Ve,
U.TT
FROM
Users AS U WITH (NOLOCK)
WHERE
((#gen & U.Ge) != 0) AND (
Sea = 1
OR Sea = 2
) AND
#s = U.Sear AND
U.La IS NOT NULL AND
U.Lon IS NOT NULL AND
#dis >= (#source.STDistance(geography::Point(U.La, U.Lon, 4326)) / 1000) AND
(#f <= YEAR(GETUTCDATE()) - YEAR(#age)) AND
(#ti >= YEAR(GETUTCDATE()) - YEAR(#age)) AND
U.UserId != #UserId AND
NOT EXISTS (
SELECT TOP 1
IC1.InitiatorUserId
FROM
ExcludedCandidates AS IC1 WITH (NOLOCK)
WHERE (
IC1.InitiatorUserId = #UserId AND
IC1.PartnerUserId = U.UserId
) OR (
IC1.InitiatorUserId = U.UserId AND
IC1.PartnerUserId = #UserId
)
) AND
EXISTS(
SELECT
U.UserId
FROM
#UserInfo UserP WITH (NOLOCK)
WHERE
(UserP.gender & U.Ge) != 0 AND
UserP.Sea IN (1, 2) AND
#s = UserP.Sea AND (UserP.age_lo <= YEAR(GETUTCDATE()) - YEAR(#age)) AND
(UserP.age_up >= YEAR(GETUTCDATE()) - YEAR(#age)) AND
UserP.UserId = U.UserId
)
ORDER BY
U.Sea DESC
END
Related
I have strange issue and I am getting more confused about this. I am using UWP on the .NET framework and using SQL Server as database. The problem is my application working too much slow and when I check my stored procedure execution time then I am notice that may be a problem is from the database stored procedure side.
My stored procedure sometimes takes 4-5 seconds to execute and sometimes takes more like 15-16 seconds and sometimes it takes more than 1 minute.
My questions are:
Why is it taking a lot more time sometimes, but not every time?
How can I solve this issue and boost my application execution time?
This is the stored procedure:
ALTER PROCEDURE [dbo].[SPNAME]
#HouseID INT,
#FromDate DATETIME,
#ToDate DATETIME,
#PageNumber INT,
#PageSize INT,
#RollID INT
AS
BEGIN
DECLARE #RoomID INT = 0
DECLARE #UnitID INT = 0
DECLARE #HouseCode INT = 0
DECLARE #StartRow INT
DECLARE #EndRow INT
SET #StartRow = #PageNumber
SET #StartRow = ((#StartRow-1) * #PageSize) + 1
SET #EndRow = (#StartRow + #PageSize) - 1;
IF OBJECT_ID('tempdb.dbo.#TableName') IS NOT NULL
DROP TABLE dbo.#TableName
SELECT
#HouseCode = [HouseCode],
#UnitID = UnitId
FROM
[dbo].[tblHouse]
WHERE
[HouseId] = #HouseID
SELECT #RoomID = RoomCode
FROM [dbo].[tblUnit] u
LEFT JOIN tblRoom h ON u.RoomID = h.Room
WHERE UnitID = #UnitID
SELECT MAX(RoNum) RoNum
INTO dbo.#tableName
FROM [dbo].[TAbleNAme]
WHERE FanId = #HouseCode
AND InActive = 0
AND ((InactivatedOn IS NULL AND StopDate IS NULL)
OR (ISNULL(InactivatedOn, StopDate) BETWEEN #FromDate AND #ToDate)
OR (ISNULL(InactivatedOn,StopDate) >= #ToDate))
AND (FirstFillDate <= #ToDate)
GROUP BY
OrigRoNum
SELECT
K.Topic, K.Comment, K.RoNum
INTO
#TmptblRoCom
FROM
[dbo].[TableNAme] K
LEFT JOIN
#TableName T ON K.RoNum = T.RoNum
WHERE
K.Topic = 'Nar Auto'
AND ISNUMERIC(SUBSTRING(K.Comment, 0, 3)) = 1
SELECT ButtonTypeID, ISNULL(SortOrder, 17) AS SortOrder
INTO dbo.#TableName
FROM [dbo].[TAbleName]
WHERE NHID = #RoomID AND NHSortID = 29
SELECT
DIN,
'LU: ' + LTRIM(RTRIM(ReasonCode)) +
'Exp:' + ISNULL(FORMAT(ExpiryDate, 'dd/MM/yyyy'), 'Indefinite') AS HLCode
INTO
dbo.#TableName
FROM
[dbo].[TableNAme]
WHERE
PatID = #HouseCode
AND (ExpiryDate IS NULL OR ExpiryDate > GETDATE())
SELECT
X.DgID AS DumbID,
X.ID AS RID,
CASE
WHEN DR.BandName IS NULL
THEN '#' + dm.Description
ELSE CONCAT(BandName, ' ', strength)
END AS DugName,
StrengthType, FormType,
CASE
WHEN GericName IS NULL
THEN
CASE
WHEN BandName IS NULL
THEN '#' + dm.Description
ELSE CONCAT(DR.BandName, ' ', DR.strength)
END
ELSE CONCAT(DR.GericName, ' ', DR.strengthTypr)
END AS GericName,
DR.[Description],
dbo.GetDirectionWithFullForm (X.IG,' ') AS Direction,
CAST(X.RoNum AS BIGINT) AS RoNum,
FORMAT(X.FirstFillDate,'dd/MM/yyyy') AS FirstFillDate,
FORMAT(X.LastFillDate,'dd/MM/yyyy') AS DugOrderDate,
ISNULL(TP.[ID],0) AS RollID,
TP.[Cont] AS Cont,
TP.[DisContinue] AS Disc,
TP.[Hold] AS Hold,
CAST(m.OrderType AS INT) AS OrderType,
pl.HLCode,
rc.Comment,
TP.[Comments]
FROM
dbo.#TAbleNAme rn
INNER JOIN
[dbo].[RollRx] X ON rn.RoNum = X.RoNum
INNER JOIN
[dbo].[RollDg] DR ON X.[DgID] = DR.ID
LEFT JOIN
[dbo].[RollDgMix] DM ON X.MixID = DM.ID
LEFT JOIN
[dbo].[RollPreviousNumber] TP ON X.DrgID = TP.[RollMedicationID]
AND X.[ID] = TP.[RoID]
AND TP.[HouseID] = #HouseID
AND TP.[TableName] = #RollID
LEFT JOIN
dbo.#TableNAme m ON m.TypeID = X.TypeID
LEFT JOIN
dbo.#TableName pl ON pl.DNNUM = X.DNNUM
LEFT JOIN
dbo.#TableName rc ON rc.RoNum = X.RoNum
END
Execution Plan
Yes sounds like parameter sniffing or lack of memory issue (this has made it block for me at times when I am running to much on my box), look at the execution plan.
Run the stored proc with your parameters and see if there is a difference here, by right clicking on the execution plan select, below shows an example.
I have one SP for mobile application chat app (used with API) it has 25 filters and 10 tables join and return posts for user, it works good for 2 days but after 2 days it stop working, then i need to refresh it again with alter query or something then it start working for next few days.
If filter stop for any particular user than why others have issue with SP.
USE [DatabaseName]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOALTER PROC [dbo].[uspGetShoutouts] (
#UserId UNIQUEIDENTIFIER,
#anotherUserid UNIQUEIDENTIFIER,
#Interest NVARCHAR(100),
#Gender NVARCHAR(100),
#AgeFrom INT,
#AgeTo INT,
#StartLatitude NUMERIC(20,15),
#StartLongitude NUMERIC(20,15),
#Radius FLOAT,
#Location BIT = NULL,
#IsTraveler BIT,
#IsLocal BIT,
#HomeTownCity VARCHAR(100),
#UserName NVARCHAR(200),
#FirstName NVARCHAR(200),
#LastName NVARCHAR(200),
#CreatedDate DATETIME,
#IsMyShoutout BIT,
#ShoutoutTypeId INT=0,
#PostTypeId INT=0,
#oldMoment BIT,
#SortExpression CHAR(1) = 1,
#CurrentPage INT = 1,
#PageSize INT = 10,
#IsFriend BIT=0,
#IsPrivate BIT,
#GroupId NVARCHAR(200),
#TotalRows INT OUTPUT
)
AS
BEGIN
DECLARE #UserList TABLE(
UserId uniqueidentifier,
FirstName NVARCHAR(100),
LastName NVARCHAR(100),
QuickBloxId NVARCHAR(100),
SocialId NVARCHAR(100),
Gender char(1),
Email NVARCHAR(100),
IsProfileImageSync bit,
IsFriend bit,
IsRequestSent bit,
RequestSender NVARCHAR(100),
IsBlocked bit,
FriendBlocked bit,
Age int,
HomeTownCity NVARCHAR(100),
ImageName NVARCHAR(200),
Distance numeric(20,6),
DistanceTemp numeric(20,6),
LastActiveDateTime datetime,
ShoutoutGuid uniqueidentifier,
ShoutoutId bigint not null,
InterestId int,
InterestName NVARCHAR(100),
ShoutoutImageName NVARCHAR(200),
Description NVARCHAR(max),
CreatedOn DateTime,
IsImageSync bit,
DisplayUpdatedOn NVARCHAR(50),
ShoutoutCity NVARCHAR(500),
ShoutoutCountry NVARCHAR(500),
AdministrativeAreaLevel1 NVARCHAR(500),
PlaceId NVARCHAR(500),
FormattedAaddress NVARCHAR(500),
Url NVARCHAR(500),
TotalLike int,
TotalComment int,
Selflike bit,
ShoutoutTypeId int,
ShoutoutType NVARCHAR(200),
PostTypeId int,
PostTypeName NVARCHAR(200),
oldMoment bit,
UserLatitude numeric(20,6),
UserLongitude numeric(20,6),
ShoutoutLatitude numeric(20,6),
ShoutoutLongitude numeric(20,6),
DistanceShoutoutMiles numeric(20,6)
)
SET NOCOUNT ON;
DECLARE #StartLatitude1 numeric(20,2) = #StartLatitude
DECLARE #StartLongitude1 numeric(20,2) =#StartLongitude;
;with
UserList as (
select
distinct
USRSHTOUT.ShoutoutId,
USRSHTOUT.ShoutoutGuid,
U.UserId,
U.FirstName,
U.LastName,
U.QuickbloxId,
convert(int,round(datediff(hour,dob,getdate())/8766.0,0)) as Age,
U.SocialId,
U.Gender,
U.Email,
isnull(U.IsProfileImageSync,0) IsProfileImageSync,
U.HomeTownCity,
U.ImageName,
isnull(fr.IsFriend,0) as IsFriend,
isnull(fr.IsRequestSent,0) as IsRequestSent,
isnull(fr.RequestSender,cast(cast(0 as binary) as uniqueidentifier)) as RequestSender,
isnull(fr.IsBlocked,0) as IsBlocked,
isnull(fr.FriendBlocked,0) as FriendBlocked,
[dbo].[fnCalcDistanceKM](cast(#StartLatitude1 as float),cast(#StartLongitude1 as float),cast (isnull(USRSHTOUT.Latitude,0) as float) ,cast(isnull(USRSHTOUT.Longitude,0) as float)) as Distance,
(geography::Point(coalesce(USRSHTOUT.Latitude,0), coalesce(USRSHTOUT.Longitude,0), 4326).STDistance(geography::Point(#StartLatitude1, #StartLongitude1, 4326))/1000) as DistanceTemp,
ULAL.UpdatedOn as LastActiveDateTime,
0 as InterestId,
'' as InterestName,
isnull(USRSHTOUT.ImageName,'') 'ShoutoutImageName',
isnull(USRSHTOUT.Description,'') Description,USRSHTOUT.CreatedOn,IsImageSync,
isnull(format(UGL.UpdatedOn,'dd-MMM, yyyy HH:mm tt'),'') 'DisplayUpdatedOn',
isnull(ADC.locality,'')[ShoutoutCity],
isnull(ADC.country,'')[ShoutoutCountry],
isnull(ADC.administrative_area_level_1,'') [AdministrativeAreaLevel1],
isnull(ADC.place_id,'') [PlaceId],
isnull(ADC.formatted_address,'') FormattedAaddress,
isnull(ADC.url,'') Url,
isnull(lt.TotalLike, 0) as TotalLike,
isnull(ct.TotalComment, 0) as TotalComment,
isnull(lt.SelfLike, 0) as SelfLike,
SHT.ShoutoutTypeId,
isnull(SHT.Name,'') ShoutoutType,
UPT.PostTypeId,
isnull(UPT.Name,'') PostTypeName,
USRSHTOUT.oldMoment,
UGL.Latitude 'UserLatitude',
UGL.Longitude 'UserLongitude',
USRSHTOUT.Latitude 'ShoutoutLatitude',
USRSHTOUT.Longitude 'ShoutoutLongitude',
DistanceShoutoutMiles
from
Users U
inner join UserShoutouts USRSHTOUT on U.UserId=USRSHTOUT.UserId
left join UserLastActivityLog ULAL on ULAL.UserId=U.UserId
left join UserGeoLocation UGL on UGL.UserId=U.UserId
left join AddressShoutoutMapping ASMP on ASMP.ShoutoutId=USRSHTOUT.ShoutoutId
left join AddressComponents ADC on ADC.AddressComponentId=ASMP.AddressComponentId
left join [ShoutoutType] SHT on SHT.ShoutoutTypeId=isnull(USRSHTOUT.ShoutoutTypeId,0)
LEFT JOIN [UserPostType] UPT ON UPT.PostTypeId = ISNULL(USRSHTOUT.PostTypeId,0)
cross apply (select cos(radians(#StartLatitude1)) * cos(radians(USRSHTOUT.Latitude)) * cos(radians(USRSHTOUT.Longitude) - radians(#StartLongitude1)) + sin(radians(#StartLatitude1)) * sin(radians(USRSHTOUT.Latitude))) T(ACosInput)
cross apply (select ((3959 * acos(case when abs(ACosInput) > 1 then sign(ACosInput)*1 else ACosInput end)))) T2(DistanceShoutoutMiles)
left join (
select ItemId,
count(1) TotalLike,
cast(sum(case when UserId = #anotherUserid then 1 else 0 end) as bit) as SelfLike
from dbo.LikeDetails
where Liked=1 and LikeSourceId=1
group by ItemId) as lt on USRSHTOUT.ShoutoutId = lt.ItemId
left join (
select ParentId,
count(1) as TotalComment
from Comments
where CommentSourceId=1 and Comments.IsDeleted=0
group by ParentId) as ct on USRSHTOUT.ShoutoutId = ct.ParentId
left join dbo.tvf_GetFriendsByUserId (#UserId) fr on u.UserId = fr.ThisFriendId
left join UserShoutoutsPrivate USP on USP.ShoutoutId = USRSHTOUT.ShoutoutId
left join UserInterest UI on UI.InterestId = USP.GroupId
left join Interest INTRST on INTRST.Id = UI.InterestId
where
USRSHTOUT.IsDeleted=0
and (((#oldMoment = 0) and USRSHTOUT.oldMoment = #oldMoment)
or ((#oldMoment = 1) and USRSHTOUT.oldMoment = 1 or USRSHTOUT.oldMoment = 0) )
and U.IsEmailVerified=1 and isnull(U.IsDeactivated,0)=0
and (#ShoutoutTypeId=0 or USRSHTOUT.ShoutoutTypeId=#ShoutoutTypeId)
and (#IsMyShoutout=0 or (#IsMyShoutout=1 and USRSHTOUT.UserId = #UserId))
and ((#UserName is not null and #UserName='all'
OR ((U.FirstName + ' ' + U.LastName) = #UserName)OR (U.FirstName like LTRIM(RTRIM(#UserName))+'%') OR (U.LastName like LTRIM(RTRIM(#UserName))+'%'))
OR ( #FirstName is not null AND #LastName is null and
((U.FirstName = #FirstName ) OR (U.FirstName like LTRIM(RTRIM(#FirstName))+'%')))
OR ( #LastName is not null AND #FirstName is null and
((U.LastName = #LastName ) OR (U.LastName like LTRIM(RTRIM(#LastName))+'%')))
OR ((( #FirstName is not null AND #LastName is not null AND
((U.FirstName + ' ' + U.LastName) = #FirstName + ' ' + #LastName) OR (U.FirstName like LTRIM(RTRIM(#FirstName))+'%')) and (U.LastName like LTRIM(RTRIM(#LastName))+'%'))))
and((#Interest <>'' AND #IsPrivate = 1 AND (exists (select * from dbo.UserInterest where (UserId = U.UserId and InterestId in (select * from dbo.Split(#Interest,','))))
or #Interest ='')
or(#IsPrivate = 1
and (UI.Interestid in (select items from dbo.Split(#GroupId,',')) and INTRST.IsPrivate = 1 and INTRST.IsDelete = 0 and USRSHTOUT.IsDeleted=0 and
USP.ShoutoutId = USRSHTOUT.ShoutoutId and UI.IsActive = 1 and USP.GroupId in (select items from dbo.Split(#GroupId,','))))
)
OR(#Interest <>'' AND #IsPrivate = 0 AND (exists (select * from dbo.UserInterest where (UserId = U.UserId and InterestId in (select * from dbo.Split(#Interest,','))))
or #Interest ='')
or(#IsPrivate = 1
and (UI.Interestid in (select items from dbo.Split(#GroupId,',')) and INTRST.IsPrivate = 1 and INTRST.IsDelete = 0 and USRSHTOUT.IsDeleted=0 and
USP.ShoutoutId = USRSHTOUT.ShoutoutId and UI.IsActive = 1 and USP.GroupId in (select items from dbo.Split(#GroupId,','))))
)
OR(#Interest ='' AND #IsPrivate = 0 AND (exists (select * from dbo.UserInterest where (UserId = U.UserId and InterestId in (select * from dbo.Split(#Interest,','))))
or #Interest =''))
)
and (#PostTypeId=0 or USRSHTOUT.PostTypeId=#PostTypeId)
)
insert into #UserList
select
UserId,
FirstName,
LastName,
QuickBloxId,
SocialId,
Gender,
Email,
IsProfileImageSync,
IsFriend,
IsRequestSent,
RequestSender,
IsBlocked,
FriendBlocked,
Age,
HomeTownCity,
ImageName,
Distance,
DistanceTemp,
LastActiveDateTime,
ShoutoutGuid,
ShoutoutId,
InterestId,
InterestName,
ShoutoutImageName,
Description,
CreatedOn,
IsImageSync,
DisplayUpdatedOn,
ShoutoutCity,
ShoutoutCountry,
AdministrativeAreaLevel1,
PlaceId,
FormattedAaddress,
Url,
u.TotalLike,
u.TotalComment,
u.Selflike,
ShoutoutTypeId,
ShoutoutType,
PostTypeId,
PostTypeName,
oldMoment,
UserLatitude,
UserLongitude,
ShoutoutLatitude,
ShoutoutLongitude,
DistanceShoutoutMiles
from UserList u
where
(#Gender ='' or ((Gender in(select * from dbo.Split(#Gender,','))) or (#Gender like '%3%' and Gender=0)))
and(
((#AgeFrom = 18) AND ((#AgeTo= 65 and Age >= 15 )
or (Age >= 15 and Age <= #AgeTo)))
OR
((#AgeTo= 65 and Age >= #AgeFrom )
or (Age >= #AgeFrom and Age <= #AgeTo))
)
and IsBlocked <> 1
and FriendBlocked <> 1
and (#Location=0 or ((DistanceShoutoutMiles<= #Radius) or (#IsLocal=1 and HomeTownCity=#HomeTownCity)))
and (#IsFriend=0 or (#IsFriend=1 and IsFriend=1))
and (#Location=0 or (
((#IsTraveler=1 and #IsLocal=1) or (#IsTraveler=0 and #IsLocal=0)) or
((#IsTraveler=1 and #IsLocal=0 and lower(HomeTownCity) != lower(#HomeTownCity))
or (#IsTraveler=0 and #IsLocal=1 and lower(HomeTownCity) = lower(#HomeTownCity)))))OPTION(RECOMPILE)
set #TotalRows = ##rowcount
IF(#CurrentPage=0)
begin
select * from #UserList order by Distance asc OPTION(OPTIMIZE FOR UNKNOWN)
end
ELSE
BEGIN
declare #OffSetSize as bigint
set #OffSetSize = ((#CurrentPage * #PageSize) - #PageSize)
IF (#SortExpression = '1')
BEGIN
SELECT *
FROM #UserList
WHERE CreatedOn<#CreatedDate
ORDER BY CreatedOn DESC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
ELSE IF (#SortExpression = '2')
BEGIN
SELECT *
FROM #UserList
WHERE CreatedOn>#CreatedDate
ORDER BY CreatedOn DESC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
ELSE IF (#SortExpression = '3')
BEGIN
SELECT *
FROM #UserList
ORDER BY Distance ASC, CreatedOn DESC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
ELSE IF (#SortExpression = '4')
BEGIN
SELECT *
FROM #UserList
ORDER BY Distance ASC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
ELSE
BEGIN
SELECT *
FROM #UserList
ORDER BY CreatedOn DESC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
END
ENDGO
I have very typical situation where I'm doing wrong. I have query which have executed fast initially but later on it taking loads of time to execute ..
My query :
Declare
#fromdate varchar(20) = '01/01/2014',
#todate varchar(20)= '27/05/2015',
#SERVICE_ID CHAR(5) = '123'
DECLARE #FDATE DATETIME ,
#TDATE DATETIME
SET #FDATE = (CONVERT(DATETIME,#fromdate,103))
SET #TDATE = (CONVERT(DATETIME,#todate,103))
IF OBJECT_ID('tempdb..#RUID') IS NOT NULL
DROP TABLE #RUID
CREATE TABLE #RUID(
OFFICEID INT,
OFFICE_TITTLE INT,
MAIN_OFFICE_TITTLE VARCHAR(50),
RLB_NAME VARCHAR(20),
DIST_NAME INT,
district_description VARCHAR(30))
CREATE CLUSTERED INDEX IDX_C_RUID_ID ON #RUID(OFFICEID)
CREATE NONCLUSTERED INDEX IDX_RUID_Name ON #RUID(OFFICE_TITTLE,DIST_NAME)INCLUDE(district_description)
INSERT INTO #RUID
SELECT OFFICEID,
OFFICE_TITTLE,
MAIN_OFFICE_TITTLE,
RLB_NAME,
DIST_NAME,
D.district_description
FROM APSDC..DISTRICT D
INNER JOIN cdma..Unified_RUID_WARD_MSTR I WITH(NOLOCK)
ON D.CDMA_DistrictID = I.DIST_NAME
WHERE RLB_NAME in(3) AND I.STATEID ='01'
select C.MAIN_OFFICE_TITTLE AS 'OFFICE_TITTLE',C.officeid, C.DIST_NAME AS DistrictName, C.district_description,
ISNULL(count(I.ApplicationNumber),0) 'Total_Trans',
isnull(sum(case when Data_Available='Y' AND DataTampered = 'N' then 1 else 0 end),0) 'CategoryA'
from #RUID c with(nolock)
LEFT JOIN Unified_BirthDeathAppDetails I WITH(NOLOCK) ON
(C.OFFICE_TITTLE=I.RUID AND C.DIST_NAME=I.DistrictName)
AND I.Service_Type= '01' AND
(DATEADD(DD,0,DATEDIFF(DD,0,I.Created_Date))) BETWEEN #FDATE AND #TDATE
AND NOT EXISTS(select application_number from reversal_details WITH(NOLOCK) WHERE ApplicationNumber <> i.ApplicationNumber AND service_id='123' )
group by C.MAIN_OFFICE_TITTLE,C.officeid, C.DIST_NAME,C.district_description
order by C.district_description ,C.MAIN_OFFICE_TITTLE
I have tried with #temp table and table variable but it is not even showing any result set. But the same query executed in 2 secs now it is taking lot of time. I have tried UPDATE Statstics on this tables and I have checked with locking also. What I need to do I have followed every other peformance optimized techinique.
Try:
Declare
#FDate Date = '01/01/2014',
#TDate Date= '27/05/2015',
#SERVICE_ID CHAR(5) = '123'
;WITH RUID
AS
(
SELECT OFFICEID,
OFFICE_TITTLE,
MAIN_OFFICE_TITTLE,
RLB_NAME,
DIST_NAME,
D.district_description
FROM APSDC..DISTRICT D
INNER JOIN cdma..Unified_RUID_WARD_MSTR I
ON D.CDMA_DistrictID = I.DIST_NAME
WHERE RLB_NAME in(3) AND I.STATEID ='01'
),
AppDetails
AS
(
SELECT ApplicationNumber,
CASE WHEN Data_Available='Y' AND DataTampered = 'N'
THEN 1
ELSE 0
END CategoryA
FROM Unified_BirthDeathAppDetails I
WHERE I.CreateDate >= #FDate AND I.CreatedDate < #TDate AND
NOT EXISTS
( select application_number
FROM reversal_details
WHERE I.Service_Type= '01' AND
ApplicationNumber <> i.ApplicationNumber AND
service_id= #Service_iD
)
)
SELECT C.MAIN_OFFICE_TITTLE AS OFFICE_TITTLE
,C.officeid, C.DIST_NAME AS DistrictName
, C.district_description
,ISNULL(count(I.ApplicationNumber),0) Total_Trans
,isnull(sum(CategoryA),0) CategoryA
FROM RUID c
LEFT JOIN AppDetails I
ON C.OFFICE_TITTLE=I.RUID AND C.DIST_NAME=I.DistrictName
GROUP BY C.MAIN_OFFICE_TITTLE
,C.officeid
,C.DIST_NAME
,C.district_description
ORDER BU C.district_description
,C.MAIN_OFFICE_TITTLE
Make sure you have decent indexes on RLB_name, StateId, CDMA_DistrictId, Dist_Name, CreatedDate etc.
Arrrgh, I am not getting this.
I have a table of accounts from Dynamics GP that has 7 columns. I need to fill in the blank months for accounts that didn't have any activity for a given month.
I have created an in memory table #MONTHS that has lines like so:
Account, Description, Year, Month, Month Name, Netchange, PeriodBal
1110000, NULL, 2006, 1, NULL, 0, NULL
This should match up with the same information coming from Dynamics GP. A similar line from GP would look like this:
1110000, Petty Cash, 2006, 1, January, 15.00, 343.97
If we did not spend any petty cash in February, then there would be no line for that account in 2/2006, I want to make the #MONTHS table RIGHT JOIN with the DynamicsGP table so that empty months are filled in.
Here's the abbreviated SQL shortened for readability:
SELECT Z.GPACCOUNTNO,
Z.DESCRIPTION,
Z.FISCALYEAR,
Z.FISCALPERIOD,
Z.FISCALPERIODNAME,
Z.NETCHANGE,
Z.PERIODBALANCE
FROM Z
RIGHT JOIN #MONTHS M
ON Z.GPACCOUNTNO = M.GPACCOUNTNO
AND Z.FISCALPERIOD = M.FISCALPERIOD
AND Z.FISCALYEAR = M.FISCALYEAR
The SQL just runs forever. (i.e. 5 minutes before I lose my patience)
I have verified that my #MONTHS table looks like I intend. I have tried doing a "UNION ALL" with the two tables and it gives me duplicates.
If Table Z does not have a current line for a given account/year/month, I want my #MONTHS table to add that line with a Netchange balance of 0.
Thank you for your help. The full SQL is below.
/* Create in memory table to hold account numbers */
DECLARE #i int
DECLARE #c int
DECLARE #ACCT char(129)
DECLARE #numrows int
DECLARE #numyears int
DECLARE #y int
DECLARE #m int
DECLARE #ACCT_TABLE TABLE (
idx smallint Primary Key IDENTITY(1,1),
account char(129)
)
/* Populate account number table */
INSERT #ACCT_TABLE
select distinct ACTNUMST from SBM01.[dbo].[GL00105]
/* Year table reads available years in the DB */
DECLARE #YEAR_TABLE TABLE (
idx smallint Primary Key IDENTITY(1,1),
YEAR1 smallint
)
/* Populate year table */
INSERT #YEAR_TABLE
SELECT distinct YEAR1 FROM SBM01.dbo.SY40101 ORDER BY YEAR1
/* Create our table of months to UNION to the main accounts */
DECLARE #MONTHS table (
GPACCOUNTNO char(129),
DESCRIPTION char(51),
FISCALYEAR smallint ,
FISCALPERIOD smallint,
FISCALPERIODNAME char(21),
NETCHANGE numeric(19, 5),
PERIODBALANCE numeric(19, 5)
)
/* Here comes the heavy lifting.
We loop over the account numbers and add year and month values.
*/
SET #i = 1
SET #numrows = (SELECT COUNT(*) FROM #ACCT_TABLE)
IF #numrows > 0
WHILE(#i <= (SELECT MAX(idx) FROM #ACCT_TABLE))
BEGIN
/* Get the next account number */
SET #ACCT = (SELECT account FROM #ACCT_TABLE WHERE idx = #i)
SET #c = 1
SET #numyears = (SELECT COUNT(*) FROM #YEAR_TABLE)
WHILE(#c <= (SELECT MAX(idx) FROM #YEAR_TABLE))
BEGIN
SET #y = (SELECT YEAR1 FROM #YEAR_TABLE WHERE idx = #c)
SET #m = '0'
WHILE(#m < '13')
BEGIN
INSERT INTO #MONTHS (GPACCOUNTNO, DESCRIPTION, FISCALPERIOD, FISCALYEAR, FISCALPERIODNAME, NETCHANGE, PERIODBALANCE)
VALUES (#ACCT, NULL, #m, #y, NULL, '0', NULL)
SET #m = #m + 1
END
SET #c = #c + 1
END
SET #i = #i + 1
END
/* We should now have a populated Database */
SELECT Z.GPACCOUNTNO, Z.DESCRIPTION, Z.FISCALYEAR, Z.FISCALPERIOD, Z.FISCALPERIODNAME, Z.NETCHANGE, Z.PERIODBALANCE
FROM ( SELECT RTRIM(B.[ACTNUMST]) AS GPACCOUNTNO,
RTRIM(C.[ACTDESCR]) AS DESCRIPTION,
A.[YEAR1] AS FISCALYEAR,
A.[PERIODID] AS FISCALPERIOD,
E.[PERNAME] AS FISCALPERIODNAME,
ISNULL(A.[PERDBLNC], 0) AS NETCHANGE,
( SELECT ISNULL(SUM(D.[PERDBLNC]), 0)
FROM SBM01.[dbo].[GL10110] D
WHERE D.[ACTINDX] = A.[ACTINDX]
AND D.[YEAR1] = A.[YEAR1]
AND D.[PERIODID] <= A.[PERIODID]
) AS PERIODBALANCE
FROM SBM01.[dbo].[GL10110] A
INNER JOIN SBM01.[dbo].[GL00105] B ON B.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[GL00100] C ON C.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[SY40100] E ON E.[YEAR1] = A.[YEAR1]
AND E.[PERIODID] = A.[PERIODID]
AND E.[SERIES] = 0
UNION ALL
SELECT RTRIM(B.[ACTNUMST]) AS GPACCOUNTNO,
RTRIM(C.[ACTDESCR]) AS DESCRIPTION,
A.[YEAR1] AS FISCALYEAR,
A.[PERIODID] AS FISCALPERIOD,
E.[PERNAME] AS FISCALPERIODNAME,
ISNULL(A.[PERDBLNC], 0) AS NETCHANGE,
( SELECT ISNULL(SUM(D.[PERDBLNC]), 0)
FROM SBM01.[dbo].[GL10111] D
WHERE D.[ACTINDX] = A.[ACTINDX]
AND D.[YEAR1] = A.[YEAR1]
AND D.[PERIODID] <= A.[PERIODID]
) AS PERIODBALANCE
FROM SBM01.[dbo].[GL10111] A
INNER JOIN SBM01.[dbo].[GL00105] B ON B.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[GL00100] C ON C.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[SY40100] E ON E.[YEAR1] = A.[YEAR1]
AND E.[PERIODID] = A.[PERIODID]
AND E.[SERIES] = 0
) Z
RIGHT JOIN #MONTHS M
ON Z.GPACCOUNTNO = M.GPACCOUNTNO
AND Z.FISCALPERIOD = M.FISCALPERIOD
AND Z.FISCALYEAR = M.FISCALYEAR
ORDER BY Z.[GPACCOUNTNO],
M.[FISCALYEAR],
M.[FISCALPERIOD]
Why don't you use the #Months table as the starting point (since it already gives you all the months you need) and fill-in the values from Z if they are available?
SELECT
M.GPACCOUNTNO,
M.DESCRIPTION,
M.FISCALYEAR,
M.FISCALPERIOD,
M.FISCALPERIODNAME,
ISNULL(Z.NETCHANGE, 0) as NETCHANGE
ISNULL(Z.PERIODBALANCE, 0) as PERIODBALANCE
FROM #MONTHS M
LEFT JOIN Z
ON Z.GPACCOUNTNO = M.GPACCOUNTNO
AND Z.FISCALPERIOD = M.FISCALPERIOD
AND Z.FISCALYEAR = M.FISCALYEAR
You can use a SQL case statement to join when null
CREATE TABLE #TMP
(
id int,
[month] datetime
)
INSERT INTO #TMP(id,[month])values(1,GETDATE())
INSERT INTO #TMP(id,[month])values(2,null)
INSERT INTO #TMP(id,[month])values(3,GETDATE())
INSERT INTO #TMP(id,[month])values(4,GETDATE())
CREATE TABLE #TMP2
(
id int,
[month] datetime
)
INSERT INTO #TMP2(id,[month])values(1,GETDATE())
INSERT INTO #TMP2(id,[month])values(2,GETDATE())
INSERT INTO #TMP2(id,[month])values(3,GETDATE())
INSERT INTO #TMP2(id,[month])values(4,GETDATE())
select * from #TMP
select * from #TMP2
SELECT #TMP.[id], case when #TMP.[month] is null then #TMP2.[month] else #TMP.month end
from #tmp
inner join #tmp2 on #tmp.id= #tmp2.id
drop table #tmp,#tmp2
I have the below stored procedure and would like to only have one SQL statement. At the moment you can see there are two statements, one for the actual paging and one for a count of the total records which needs to be return to my app for paging.
However, the below is inefficient as I am getting the total rows from the first query:
COUNT(*) OVER(PARTITION BY 1) as TotalRows
How can I set TotalRows as my output parameter?
ALTER PROCEDURE [dbo].[Nop_LoadAllOptimized]
(
#PageSize int = null,
#PageNumber int = null,
#WarehouseCombinationID int = null,
#CategoryId int = null,
#OrderBy int = null,
#TotalRecords int = null OUTPUT
)
AS
BEGIN
WITH Paging AS (
SELECT rn = (ROW_NUMBER() OVER (
ORDER BY
CASE WHEN #OrderBy = 0 AND #CategoryID IS NOT NULL AND #CategoryID > 0
THEN pcm.DisplayOrder END ASC,
CASE WHEN #OrderBy = 0
THEN p.[Name] END ASC,
CASE WHEN #OrderBy = 5
THEN p.[Name] END ASC,
CASE WHEN #OrderBy = 10
THEN wpv.Price END ASC,
CASE WHEN #OrderBy = 15
THEN wpv.Price END DESC,
CASE WHEN #OrderBy = 20
THEN wpv.Price END DESC,
CASE WHEN #OrderBy = 25
THEN wpv.UnitPrice END ASC
)),COUNT(*) OVER(PARTITION BY 1) as TotalRows, p.*, pcm.DisplayOrder, wpv.Price, wpv.UnitPrice FROM Nop_Product p
INNER JOIN Nop_Product_Category_Mapping pcm ON p.ProductID=pcm.ProductID
INNER JOIN Nop_ProductVariant pv ON p.ProductID = pv.ProductID
INNER JOIN Nop_ProductVariant_Warehouse_Mapping wpv ON pv.ProductVariantID = wpv.ProductVariantID
WHERE pcm.CategoryID = #CategoryId
AND (wpv.Published = 1 AND pv.Published = 1 AND p.Published = 1 AND p.Deleted = 0 AND pv.Deleted = 0 and wpv.Deleted = 0)
AND wpv.WarehouseID IN (select WarehouseID from Nop_WarehouseCombination where UserWarehouseCombinationID = #WarehouseCombinationID)
)
SELECT TOP (#PageSize) * FROM Paging PG
WHERE PG.rn > (#PageNumber * #PageSize) - #PageSize
SELECT #TotalRecords = COUNT(p.ProductId) FROM Nop_Product p
INNER JOIN Nop_Product_Category_Mapping pcm ON p.ProductID=pcm.ProductID
INNER JOIN Nop_ProductVariant pv ON p.ProductID = pv.ProductID
INNER JOIN Nop_ProductVariant_Warehouse_Mapping wpv ON pv.ProductVariantID = wpv.ProductVariantID
WHERE pcm.CategoryID = #CategoryId
AND (wpv.Published = 1 AND pv.Published = 1 AND p.Published = 1 AND p.Deleted = 0 AND pv.Deleted = 0 and wpv.Deleted = 0)
AND wpv.WarehouseID IN (select WarehouseID from Nop_WarehouseCombination where UserWarehouseCombinationID = #WarehouseCombinationID)
END
I think I understand your issue here. Have you considered that the Count could be done BEFORE the CTE
and then passed in as value to the CTE as a variable.
i.e, set the value for #TotalRecords up front, pass it in, and so the CTE will use this count rather than executing the count a second time?
Does this make sense, or have I missed your point here.
no problem friend, highly possible i missed a trick here. However without the schema and data its tricky to test what I am suggesting. In the absence of someone giving a better answer, I've put this test script with data together to demo what I am talking about. If this isn't what you want then no problem. If it is just plain missing the point again, then I'll take that on the chin.
Declare #pagesize as int
Declare #PageNumber as int
Declare #TotalRowsOutputParm as int
SET #pagesize = 3
SET #PageNumber = 2;
--create some test data
DECLARE #SomeData table
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[SomeValue] [nchar](10) NULL
)
INSERT INTO #SomeData VALUES ('TEST1')
INSERT INTO #SomeData VALUES ('TEST2')
INSERT INTO #SomeData VALUES ('TEST3')
INSERT INTO #SomeData VALUES ('TEST4')
INSERT INTO #SomeData VALUES ('TEST5')
INSERT INTO #SomeData VALUES ('TEST6')
INSERT INTO #SomeData VALUES ('TEST7')
INSERT INTO #SomeData VALUES ('TEST8')
INSERT INTO #SomeData VALUES ('TEST9')
INSERT INTO #SomeData VALUES ('TEST10');
--Get total count of all rows
Set #TotalRowsOutputParm = (SELECT COUNT(SomeValue) FROM #SomeData p) ;
WITH Paging AS
(
SELECT rn = (ROW_NUMBER() OVER (ORDER BY SomeValue ASC)),
#TotalRowsOutputParm as TotalRows, p.*
FROM [SomeData] p
)
SELECT TOP (#PageSize) * FROM Paging PG
WHERE PG.rn > (#PageNumber * #PageSize) - #PageSize
PRINT #TotalRowsOutputParm
I don't think you can do it without running the query twice if you want to assign it to a variable
however, can't you just add another column and do something like this instead?
;WITH Paging AS (select *,ROW_NUMBER() OVER(ORDER BY name) AS rn FROM sysobjects)
SELECT (SELECT MAX(rn) FROM Paging) AS TotalRecords,* FROM Paging
WHERE rn < 10
Or in your case
SELECT TOP (#PageSize) *,(SELECT MAX(PG.rn) FROM Paging) AS TotalRecords
FROM Paging PG
WHERE PG.rn > (#PageNumber * #PageSize) - #PageSize
Then from the front end grab that column
In the end I decided just to use two different SQL statements, one for count, one for select.
The "COUNT(*) OVER(PARTITION BY 1) as TotalRows" actually was pretty expensive and it turned out much quicker to just use two different statements.
Thank you everyone who helped with this question.