I have written two queries and I need help to know which one will be better in term of performance. Both do the same thing. Please also advise if you have better a way of writing the query
Query 1
DECLARE #TotalAvailedLateNightGeneral INT
;WITH CTE (TotalDaysAppliedFor) AS (
SELECT SUM(TotalDaysAppliedFor) AS TotalDaysAppliedFor
FROM [CURFEW_RLX_REQUEST] WITH (NOLOCK)
GROUP BY StaffSeqId, RequestTypeId, MONTH(PermissionRequiredFrom), StatusId, IsActive
HAVING StaffSeqId = 41130
AND RequestTypeId = 3
AND MONTH(PermissionRequiredFrom) = MONTH('2016-03-30 00:00:00.000')
AND StatusId <> 111
AND IsActive = 1
)
SELECT #TotalAvailedLateNightGeneral = SUM(TotalDaysAppliedFor)
FROM CTE
SELECT #TotalAvailedLateNightGeneral
QUERY 2
SELECT SUM(TotalDaysAppliedFor) AS TotalDaysAppliedFor
FROM [CURFEW_RLX_REQUEST] WITH (NOLOCK)
--GROUP BY StaffSeqId,RequestTypeId,MONTH(PermissionRequiredFrom),StatusId,IsActive
WHERE StaffSeqId = 41130
AND RequestTypeId = 3
AND MONTH(PermissionRequiredFrom) = MONTH('2016-03-30 00:00:00.000')
AND StatusId <> 111
AND IsActive = 1
DECLARE #StartDate DATETIME = '2016-03-01'
DECLARE #EndDate DATETIME = '2016-04-01'
SELECT TotalDaysAppliedFor = SUM(TotalDaysAppliedFor)
FROM dbo.QAG_GEMS_CURFEW_RLX_REQUEST
WHERE StaffSeqId = 41130
AND RequestTypeId = 3
AND PermissionRequiredFrom >= #StartDate
AND PermissionRequiredFrom < #EndDate
AND StatusId != 111
AND IsActive = 1
Related
Facing timeout expired issue in a code developed.
Shared below is the stored procedure where timeout occurs.
Purpose of the code : Dates being passed from frontend (using a forloop in Windows application vb.net code) for 1 million cases for which date difference needs to be calculated basis the date received.
create procedure sp_getdatediff
#strd1 date = null,
#strd2 date = null,
#strd3 date = null,
#strd4 date = null,
#strd5 date = null,
#strd6 date = null,
#strd7 date = null,
#strd8 date = null,
#strd9 date = null,
#strd10 date = null,
#strd11 date = null
as
begin
declare #vardatediff1 int
declare #vardatediff2 int
declare #vardatediff3 int
declare #vardatediff4 int
declare #vardatediff5 int
set #vardatediff1 = [fn_getdiff](#strd1,#strd2,#strd3) ----- input parameters are dates passed from frontend
set #vardatediff2 = [fn_getdiff](#strd2,#strd4,#strd5)
set #vardatediff3 = [fn_getdiff](#strd4,#strd5,#strd6)
set #vardatediff4 = [fn_getdiff](#strd5,#strd7,#strd8)
set #vardatediff5 = [fn_getdiff](#strd9,#strd10,#strd11)
update tbl_Scheduler set col_dif1 = #vardatediff1 , col_dif2 = #vardatediff2 ,
col_dif3 = #vardatediff3 , col_dif4 = #vardatediff4 , col_dif5 = #vardatediff5
where id = #id
end
Function code :
create function [fn_getdiff]
(
#startdate date = null,
#enddate date = null,
#ccode varchar(10) = null
)
returns integer
as
begin
declare #count integer
declare #tdaycount integer
if (#startdate is null or #startdate = '')
begin
set #count = 0
end
else if (#enddate is null or #enddate = '')
begin
set #count = 0
end
else
begin
select #tdaycount = count(distinct(convert(date,tdays))) from tbl_holidays with (nolock) where (convert(date,tdays,105) >= convert(date,#startdate,105))
and (convert(date,tdays,105) <= convert(date,#enddate,105)) and tcode in (select id from tbl_code with (nolock) where id = #ccode)
select #count = datediff(#startdate,#enddate)
set #count = #count - #tdaycount
end
return #count
end
Is there optimization required in this code to eliminate timeout issue? How can same be done?
The use of IN in your function can be replaced by a JOIN.
Your query :
SELECT #tdaycount = COUNT(DISTINCT(CONVERT(DATE, tdays)))
FROM tbl_holidays WITH(NOLOCK)
WHERE (CONVERT(DATE, tdays, 105) >= CONVERT(DATE, #startdate, 105))
AND (CONVERT(DATE, tdays, 105) <= CONVERT(DATE, #enddate, 105))
AND tcode IN (SELECT id
FROM tbl_code WITH(NOLOCK)
WHERE id = #ccode);
The rewriting :
SELECT #tdaycount = COUNT(DISTINCT(CONVERT(DATE, tdays)))
FROM tbl_holidays AS h
JOIN tbl_code AS c
ON h.tcode = c.id
WHERE (CONVERT(DATE, tdays, 105) >= CONVERT(DATE, #startdate, 105))
AND (CONVERT(DATE, tdays, 105) <= CONVERT(DATE, #enddate, 105))
AND c.id = #ccode;
When A = B and B = C the A = C, so :
...
ON h.tcode = c.id
WHERE ...
AND c.id = #ccode;
The rewriting (2) :
SELECT #tdaycount = COUNT(DISTINCT(CONVERT(DATE, tdays)))
FROM tbl_holidays AS h
WHERE (CONVERT(DATE, tdays, 105) >= CONVERT(DATE, #startdate, 105))
AND (CONVERT(DATE, tdays, 105) <= CONVERT(DATE, #enddate, 105))
AND tbl.tcode = #ccode;
You do not respects rules of posting... So we cannot helps you so much.
Put the description of your tables and indexes as DDL SQL Statement please.
Probably the CONVERTs are useless...
Your function contains this bit of SQL.
select #tdaycount = count(distinct(convert(date,tdays)))
from tbl_holidays with (nolock)
where (convert(date,tdays,105) >= convert(date,#startdate,105))
and (convert(date,tdays,105) <= convert(date,#enddate,105))
and tcode in (select id from tbl_code with (nolock) where id = #ccode)
Date-conversion error
CONVERT(DATE, #enddate, 105) produces a date of the form 31-12-2006. I suspect it makes no sense to use <= comparisons on that format because 30-12-2007 will come before 31-12-2006. Your data is already in the DATE format, so you can compare it without converting it.
Sargability performance antipattern
It contains a couple of performance antipatterns like this:
WHERE function(column) >= function(constant)
This is an antipattern because it's a non-sargable filter. What you want is this sort of pattern
WHERE column >= constant
or
WHERE column >= function(constant)
combined with an apppropriate index on the table.
Index
A multicolumn index on your tbl_holidays table on (tcode, tdays) will allow the server to satisfy your queries by range-scanning that index. Try this.
CREATE INDEX tcode_tdays ON tbl_holidays (tcode, tdays);
Rewrite
SELECT #tdaycount = COUNT(DISTINCT tdays)
FROM tbl_holidays WITH (NOLOCK)
WHERE tdays >= #startdate
AND tdays <= #enddate
AND tcode in (SELECT id
FROM tbl_code WITH (NOLOCK)
WHERE id = #ccode)
I don't know how much time this will save. But it will save some.
Default timeout too short
.NET's SQL classes are set up with 30-second timeouts. If you're processing millions (lakhs upon lakhs) of rows that's not long enough. Try setting the timeout to ten minutes or whatever duration makes sense.
Dim command = conn.CreateCommand()
...
command.CommandTimeout = 600 ' ten minutes
How can I achieve this? I am getting an error incorrect syntax near between
#STARTDATE, #ENDDATE, #CREATEDDATE -- INPUT PARAMETERS
SELECT *
FROM [DBO].[xxx] x
INNER JOIN [DBO].[yyy] y
ON x.ID = y.ID
AND CASE WHEN #CREATEDDATE = 1
THEN x.CreatedDate BETWEEN #STARTDATE AND #ENDDATE
WHEN #CREATEDDATE = 0
THEN x.ClosedDate BETWEEN #STARTDATE AND #ENDDATE
SQL Server does not treat boolean expressions as something that a case expression can return. In fact, case in on and where clauses is to be discouraged. And you can easily express this using more basic boolean operations:
SELECT *
FROM [DBO].[xxx] x INNER JOIN
[DBO].[yyy] y
ON x.ID = y.ID AND
( (#CREATEDDATE = 1 AND x.CreatedDate BETWEEN #STARTDATE AND #ENDDATE) OR
(#CREATEDDATE = 0 AND x.ClosedDate BETWEEN #STARTDATE AND #ENDDATE)
)
You can try the following query
SELECT *
FROM [DBO].[xxx] x
INNER JOIN [DBO].[yyy] y
ON x.ID = y.ID
AND CASE WHEN #CREATEDDATE = 1 AND x.CreatedDate BETWEEN #STARTDATE AND #ENDDATE THEN 1
WHEN #CREATEDDATE = 0 AND x.ClosedDate BETWEEN #STARTDATE AND #ENDDATE THEN 1
ELSE 0 END = 1
I've been working on a query that should :
business logic - show all members who attended the gym(selected from dropdown) within a specified date range.
Problem encountered -I realised that results shows duplicates of which mustn't , may you please assist.
Below is the query I used.
Declare #StartDate datetime = '29 May 2014'
Declare #EndDate datetime = '29 May 2014'
DEclare #SiteID INT =14
Declare #StartTime datetime = '05:00 AM'
Declare #EndTime datetime = '10:00 PM'
Declare #Start datetime = cast (#StartDate + ' '+ #StartTime as datetime)
Declare #End datetime = cast(#EndDate + ' ' + #EndTime as datetime )
SELECT isnull(atc.totalaccepted,0) TotalAcceptedVisits,ISNULL(att.TotalOverrideVisits, 0) AS TotalOverrides, isnull(att1.TotalOverrideDenieds,0) as TotalDenies, ast.Name as VisitiedSite, c.FirstName + ' ' + c.LastName as Name, md.MemRefNo
FROM Contacts c
inner JOIN Attendance a on a.ContactGUID = c.GUID
inner JOIN MemberDetail md on md.ContactGUID = c.GUID
inner JOIN Sites ast on a.SiteID = ast.ID
OUTER APPLY
(
SELECT a1.contactguid,COUNT(*) totalaccepted
FROM Attendance a1
WHERE a1.contactguid = c.guid
AND (a1.IsSwipeSuccessful = 1)
AND a1.accessoverridereasonid IS NULL
AND a1.AttendDate BETWEEN #Start AND #End
group by a1.contactguid
)atc
outer APPLY
(
SELECT a2.contactguid, COUNT(*) TotalOverrideVisits
FROM Attendance a2
WHERE a2.contactguid = c.guid
And (a.IsSwipeSuccessful = 1)
AND a2.accessoverridereasonid IS NOT NULL
AND a2.AttendDate BETWEEN #Start AND #End
and (a2.SiteID = #SiteID OR #SiteID = 0)
group by a2.contactguid
) att
OUTER APPLY
(
SELECT a3.contactguid,COUNT(*) TotalOverrideDenieds
FROM Attendance a3
WHERE a3.contactguid = c.guid
AND a3.IsSwipeSuccessful = 0
AND a3.accessoverridereasonid IS NULL
AND a3.AttendDate BETWEEN #Start AND #end
group by a3.contactguid
) att1
where (a.SiteID = #SiteID OR #SiteID = 0)
and a.AttendDate BETWEEN #Start AND #End
order by md.MemRefNo
Results: as you can see below there are members repeating themselves, I only need to see one row of each member with the number of total visits if any, overrides if any and denied visits if any.
TotalAcceptedVisits TotalOverrides TotalDenies VisitiedSite Name memRefNo
1 0 0 Groblersdal Jean T G0030
1 1 1 Groblersdal Koky Bakkes G0032
1 0 1 Groblersdal Koky Bakkes G0032
1 1 1 Groblersdal Koky Bakkes G0032
1 0 0 Groblersdal Naomi Fisher G0035
1 0 0 Groblersdal Arthur Bart G0089
1 0 0 Groblersdal Tulinda Swi G0034
1 1 0 Groblersdal Devon Mooi G0008
1 1 0 Groblersdal Devon Mooi G0008
I think you will need something like this:
Declare #StartDate datetime = '29 May 2014'
Declare #EndDate datetime = '29 May 2014'
DEclare #SiteID INT =14
Declare #StartTime datetime = '05:00 AM'
Declare #EndTime datetime = '10:00 PM'
Declare #Start datetime = cast (#StartDate + ' '+ #StartTime as datetime)
Declare #End datetime = cast(#EndDate + ' ' + #EndTime as datetime )
SELECT CASE WHEN TotalAcceptedVisits > 0 THEN 1 ELSE 0 END AS TotalAcceptedVisits,
CASE WHEN TotalOverrides > 0 THEN 1 ELSE 0 END AS TotalOverrides,
CASE WHEN TotalDenies > 0 THEN 1 ELSE 0 END AS TotalDenies,
VisitiedSite, Name, MemRefNo
FROM
(SELECT SUM(isnull(atc.totalaccepted,0)) TotalAcceptedVisits, SUM(ISNULL(att.TotalOverrideVisits, 0)) AS TotalOverrides, SUM(isnull(att1.TotalOverrideDenieds,0)) as TotalDenies, ast.Name as VisitiedSite, c.FirstName + ' ' + c.LastName as Name, md.MemRefNo
FROM Contacts c
inner JOIN Attendance a on a.ContactGUID = c.GUID
inner JOIN MemberDetail md on md.ContactGUID = c.GUID
inner JOIN Sites ast on a.SiteID = ast.ID
OUTER APPLY
(
SELECT a1.contactguid,COUNT(*) totalaccepted
FROM Attendance a1
WHERE a1.contactguid = c.guid
AND (a1.IsSwipeSuccessful = 1)
AND a1.accessoverridereasonid IS NULL
AND a1.AttendDate BETWEEN #Start AND #End
group by a1.contactguid
)atc
outer APPLY
(
SELECT a2.contactguid, COUNT(*) TotalOverrideVisits
FROM Attendance a2
WHERE a2.contactguid = c.guid
And (a.IsSwipeSuccessful = 1)
AND a2.accessoverridereasonid IS NOT NULL
AND a2.AttendDate BETWEEN #Start AND #End
and (a2.SiteID = #SiteID OR #SiteID = 0)
group by a2.contactguid
) att
OUTER APPLY
(
SELECT a3.contactguid,COUNT(*) TotalOverrideDenieds
FROM Attendance a3
WHERE a3.contactguid = c.guid
AND a3.IsSwipeSuccessful = 0
AND a3.accessoverridereasonid IS NULL
AND a3.AttendDate BETWEEN #Start AND #end
group by a3.contactguid
) att1
where (a.SiteID = #SiteID OR #SiteID = 0)
and a.AttendDate BETWEEN #Start AND #End
GROUP BY atc.totalaccepted, att.TotalOverrideVisits, att1.TotalOverrideDenieds, ast.Name, c.FirstName + ' ' + c.LastName, md.MemRefNo) TB
ORDER BY MemRefNo
Since, you don't want the SUM of the values, then you can do something as above which basically embeds your SELECT as a subquery and then has a CASE statement to see whether the Total Values is greater than 0, if so return 1 else 0.
I have 2 SQLs,
SELECT #AlreadyPerformedActionsPerDay = COUNT(*)
FROM UserRewardActions URA
INNER JOIN UserRewardActionTypes URAT ON URAT.ID = URA.UserRewardActionTypeID
WHERE URA.UserID = #UserID
AND (
#UserRewardActionTypeID = 1 -- Because the register action is not per-date-wise(only allows once in lifetime)
OR CreationDateInUtc BETWEEN CAST(GETUTCDATE() AS DATE) AND DATEADD(DAY, 1, CAST(GETUTCDATE() AS DATE)) -- Inside Current Date
);
SELECT #AlreadyRedeemedTheCurrentAction = 1
FROM UserRewardActions URA
INNER JOIN UserRewardActionTypes URAT ON URAT.ID = URA.UserRewardActionTypeID
WHERE URA.UserID = #UserID
AND CreationDateInUtc BETWEEN CAST(GETUTCDATE() AS DATE) AND DATEADD(DAY, 1, CAST(GETUTCDATE() AS DATE)) -- Inside Current Date
AND URL IS NOT NULL
AND URL = #Url;
The fist SQL is the grabbing total number of user actions today. Second, is checking whether the current url is already used by the same user. This is fine. But I am looking to merge this query for performance issue.
well, after reformatting your queries as below. I can see that they have different criteria.
DECLARE #from DateTime;
DECLARE #to DateTime;
SET #from = cast(getutcdate() as Date);
SET #to = dateadd(DAY, 1, #from));
SELECT
#AlreadyPerformedActionsPerDay = COUNT(*)
FROM
UserRewardActions URA
JOIN
UserRewardActionTypes URAT
ON URAT.ID = URA.UserRewardActionTypeID
WHERE
URA.UserID = #UserID
AND
(
#UserRewardActionTypeID = 1
OR
CreationDateInUtc BETWEEN #from AND #to
);
SELECT
#AlreadyRedeemedTheCurrentAction = 1
FROM
UserRewardActions URA
JOIN
UserRewardActionTypes URAT
ON URAT.ID = URA.UserRewardActionTypeID
WHERE
URA.UserID = #UserID
AND
CreationDateInUtc BETWEEN #from AND #to
AND
URL IS NOT NULL
AND
URL = #Url;
I have table users with columns ID,USERSID
table :
t_A(ID,usersID,ADATE,priceA,priceB)
t_B(ID,usersID,BDATE,priceA,priceB)
t_C(ID,usersID,CDATE,priceA,priceB)
I'm using this query to get SUM of price from 3 tables for X DATE , and USERSID
declare #id int
set #id = 3 -- for example
SELECT SUM(priceA) as TA, SUM(priceB) as TB
FROM t_A,t_B,t_C
WHERE t_A.USERSID = #id
AND t_B.USERSID = #id
AND t_C.USERSID = #id
AND ADATE >= DATEADD(DAY, 0, DATEDIFF(DAY, 0, getdate()))
AND BDATE >= DATEADD(DAY, 0, DATEDIFF(DAY, 0, getdate()))
AND CDATE >= DATEADD(DAY, 0, DATEDIFF(DAY, 0, getdate()))
this script work only if the USERSID had a row in the three tables otherwise script return nothing
Because if only one table has no USERID = 3 that result always will be empty. Workaround: use option with UNION ALL operator
DECLARE #id int
SET #id = 3
SELECT SUM(x.priceA) as TA, SUM(x.priceB) AS TB
FROM (
SELECT priceA, priceB
FROM t_A
WHERE t_A.USERSID = #id
AND ADATE >= DATEDIFF(d,0,dateadd(d,0,getdate()))
UNION ALL
SELECT priceA, priceB
FROM t_B
WHERE t_B.USERSID = #id
AND BDATE >= DATEDIFF(d,0,dateadd(d,0,getdate()))
UNION ALL
SELECT priceA, priceB
FROM t_C
WHERE t_C.USERSID = #id
AND CDATE >= DATEDIFF(d,0,dateadd(d,0,getdate()))
) x
You can't compare a date column with a datediff. Here are some things you could do:
AND ADATE >= GETDATE()
or:
AND DATEDIFF(d, ADATE, GETDATE()) > 1
You can check the syntax and some examples at MSDN.
I think this is what you need. This way, it will return results as long as one of the tables satisfies the condition:
SELECT SUM(priceA) as TA, SUM(priceB) as TB, SUM(priceC) as TC
FROM t_A
FULL JOIN t_B
FULL JOIN t_C
WHERE t_A.USERSID = #id
AND t_B.USERSID = #id
AND t_C.USERSID = #id
AND ADATE >= DATEDIFF(d,0,dateadd(d,0,getdate()))
AND BDATE >= DATEDIFF(d,0,dateadd(d,0,getdate()))
AND CDATE >= DATEDIFF(d,0,dateadd(d,0,getdate()))