当条件查询缓慢时,将条件移出JOIN

时间:2016-08-11 20:44:48

标签: sql sql-server sql-server-2012

我在Booking和SMSConfiguration中使用以下查询来匹配基于CategoryId的记录。

这里我需要在SMSConfiguration中找到NULL CategoryId的记录。我得到了我预期的输出。但查询运行2分钟。

如果我在JOIN中移除附加条件OR SMS.CategoryId IS NULL,它运行得更快且不到1秒。我不知道如何将其移出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

修改

完整查询

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
            )
            )

慢速计划

enter image description here

当我评论OR SMS.CategoryId为空时,

快速计划

enter image description here

2 个答案:

答案 0 :(得分:1)

将SMS.CategoryId和B.CategoryId放在WHERE条件中,然后检查null - 它的可读性更强。尝试运行以下查询,它将表迭代周期减少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)

答案 1 :(得分:1)

OR几乎总是很慢。通常使用UNION all查询要快得多。但是,我不清楚确切的连接条件应该是什么。你的意思是:

 INNER JOIN SMSConfiguration SMS ON (SMS.CategoryId = B.CategoryId
        OR SMS.CategoryId IS NULL)
        AND SMS.TenantId = B.TenantId

或者

 INNER JOIN SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId
        OR (SMS.CategoryId IS NULL
        AND SMS.TenantId = B.TenantId)

第一个是你得到的,但即使你的意思是,你应该使用()在有人去维护时明确说明。