Reputation: 487
I have the below query that matches records based on CategoryId in Booking and SMSConfiguration.
Here I need to find the records for NULL CategoryId in SMSConfiguration also. I get my expected output. But the query runs 2 mins.
If I remove the additional condition OR SMS.CategoryId IS NULL
in JOIN it runs faster and less than 1 second. I am not sure how to move this out of JOIN.
SELECT SMS.Id AS ConfigId
,B.BookingId AS BookingId
,B.StartTime AS BookingStartTime
,B.EndTime AS BookingEndTime
,B.BookingDate AS BookingDate
,B.Price AS Price
FROM Booking B
INNER JOIN SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId
OR SMS.CategoryId IS NULL -- CAUSING SLOW
AND SMS.TenantId = B.TenantId
EDIT:
Full Query
DECLARE @CurrentDateTime SMALLDATETIME
SET @CurrentDateTime='2016-08-11 08:00:00.247'
SELECT SMS.Id AS ConfigId
,B.BookingId AS BookingId
,B.StartTime AS BookingStartTime
,B.EndTime AS BookingEndTime
,B.BookingDate AS BookingDate
,B.Price AS Price
,C.CategoryName AS CategoryName
,PER.PersonId AS PersonId
,P.PatientId AS PatientId
,PER.FirstName AS PatientFirstName
,PER.LastName AS PatientLastName
,PER.MobileNumber AS PatientMobileNumber
,RP.FirstName AS DoctorFirstName
,RP.LastName AS DoctorLastName
,SMS.SMSText
,B.TenantId AS TenantId
FROM Booking B
INNER JOIN SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId
OR SMS.CategoryId IS NULL
AND SMS.TenantId = B.TenantId
INNER JOIN Tenant T ON T.TenantId = B.TenantId --AND T.IsSMSEnabled=1
INNER JOIN Patient P ON B.PatientId = P.PatientId
INNER JOIN Person PER ON P.PersonId = PER.PersonId
INNER JOIN Person RP ON RP.PersonId = B.ResponsiblePersonId
LEFT JOIN Category C ON C.CategoryId = B.CategoryId
WHERE
PER.MobileNumber IS NOT NULL
AND PER.MobileNumber <> ''
AND (
(
(
DATEDIFF(DAY, @CurrentDateTime, B.StartTime) = SMS.Duration
AND SMS.DurationType = 1
)
OR (
DATEDIFF(DAY, @CurrentDateTime, B.StartTime) = SMS.Duration * 7
AND SMS.DurationType = 2
)
OR (
DATEDIFF(DAY, @CurrentDateTime, B.StartTime) = SMS.Duration * 30
AND SMS.DurationType = 3
)
)
AND BeforeAfter = 0
OR (
(
(
DATEDIFF(DAY, B.StartTime, @CurrentDateTime) = SMS.Duration
AND SMS.DurationType = 1
)
OR (
DATEDIFF(DAY, @CurrentDateTime, B.StartTime) = SMS.Duration * 7
AND SMS.DurationType = 2
)
OR (
DATEDIFF(DAY, @CurrentDateTime, B.StartTime) = SMS.Duration * 30
AND SMS.DurationType = 3
)
)
AND BeforeAfter = 1
)
)
Slow Plan
Fast Plan when I commented that OR SMS.CategoryId IS NULL
Upvotes: 0
Views: 50
Reputation: 96600
OR is almost always slow. Usually it is far faster to use a UNION all query instead. However it is not clear to me exactly what the conditions on the join are supposed to be. Do you mean:
INNER JOIN SMSConfiguration SMS ON (SMS.CategoryId = B.CategoryId
OR SMS.CategoryId IS NULL)
AND SMS.TenantId = B.TenantId
Or
INNER JOIN SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId
OR (SMS.CategoryId IS NULL
AND SMS.TenantId = B.TenantId)
the first is what you are getting, however even if you mean that , you should use () to make it clear when someone goes to maintain this.
Upvotes: 1
Reputation: 401
Put SMS.CategoryId and B.CategoryId in WHERE condition and then check for null - its more readable that way. Try running the following query, it reduces the table iteration cycle by 1
SELECT
SMS.Id AS ConfigId
,B.BookingId AS BookingId
,B.StartTime AS BookingStartTime
,B.EndTime AS BookingEndTime
,B.BookingDate AS BookingDate
,B.Price AS Price
FROM
Booking B
INNER JOIN SMSConfiguration SMS
ON SMS.TenantId = B.TenantId
WHERE
B.CategoryId = ISNULL(SMS.CategoryId,B.CategoryId)
Upvotes: 1