谓词不会导致预期的扫描

Zan*_*ane 6 sql-server sql-server-2012

所以我有一个我正在尝试调整的查询,但遇到了一个我无法理解的问题。首先是我正在使用的查询。

SELECT  
    si.LoanNbr AS [LoanNumber],
    fi.[SvcClientNbr] AS ClientID,
    si.LoanMasterID,
    si.LoanSrcCode AS [LoanSourceCode],
    fi.LoanPurpCode,
    fi.[PropState] AS [Property State],
    im.ImagedocumentID AS [Image Document ID],
    -- im.requestID AS [Request ID],
    CONVERT(VARCHAR(10),im.[ImageDate],101) AS ImageDate,
    im.[PageCount],
    im.[SignatureInd]
FROM dbo.NotMybaseTable Si
INNER JOIN dbo.NotMyTableName fi
    ON si.LoanMasterID = fi.LoanMasterID
INNER JOIN [dbo].[ImagedDocument] im
    ON si.loanmasterid = im.loanmasterid
    AND im.[DocTypeCode] = '10112'
WHERE CASE WHEN si.loansrccode = 'CORE' AND  Im.[SignatureInd] IN ('Y') THEN 1
        WHEN si.FundingSysCode = 'LIS' and CASE WHEN si.loansrccode = 'CORE' THEN 0 ELSE 1 END = 1 THEN 1
    ELSE 0 END = 1
    AND [ImageDate] BETWEEN DATEADD(WK, DATEDIFF(WK, 0, GETDATE()) - 4, -30) AND DATEADD(WK, DATEDIFF(WK, 0, GETDATE()) - 4, 0) + 5
Run Code Online (Sandbox Code Playgroud)

当我运行此查询时,我得到的执行计划如下所示。

扫描

所以查询正在对这个大表进行非常大的扫描,返回 3000 万行,只是为了将其减少到 3K。它使用的索引看起来像这样。

CREATE NONCLUSTERED INDEX [IX_ImageDoc_DocType] ON [dbo].[ImageDoc]
(
    [LoanMasterID] ASC,
    [ImageDate] ASC,
    [ImageDocType] ASC
)
Run Code Online (Sandbox Code Playgroud)

我改变了查询的方式,通过将谓词的日期部分移动到初始查询之外,从而提供更好的性能。

select
    *
from (
    SELECT  
        si.LoanNbr AS [LoanNumber],
        fi.[SvcClientNbr] AS ClientID,
        si.LoanMasterID,
        si.LoanSrcCode AS [LoanSourceCode],
        fi.LoanPurpCode,
        fi.[PropState] AS [Property State],
        im.ImagedocumentID AS [Image Document ID],
        CONVERT(VARCHAR(10),im.[ImageDate],101) AS ImageDate,
        im.[PageCount],
        im.[SignatureInd]  
    FROM dbo.NotMybaseTable Si
    INNER JOIN dbo.NotMyTableName fi
      ON si.LoanMasterID = fi.LoanMasterID
    INNER JOIN [dbo].[ImagedDocument] im
      ON si.loanmasterid = im.loanmasterid
      AND im.[DocTypeCode] = '10112'
    WHERE CASE WHEN si.loansrccode = 'CORE' AND  Im.[SignatureInd] IN ('Y') THEN 1
            WHEN si.FundingSysCode = 'LIS' and CASE WHEN si.loansrccode = 'CORE' THEN 0 ELSE 1 END = 1 THEN 1
        ELSE 0 END = 1
        ) as F
WHERE [ImageDate] BETWEEN DATEADD(WK, DATEDIFF(WK, 0, GETDATE()) - 4, -30) AND DATEADD(WK, DATEDIFF(WK, 0, GETDATE()) - 4, 0) + 5
Run Code Online (Sandbox Code Playgroud)

这导致了更好的性能和明显优越的查询计划。

更好的扫描

它正在扫描的索引现在看起来像这样。

CREATE NONCLUSTERED INDEX [IX_ImageLoanDoc_SystemName_ICMPDocTypeCode] ON [dbo].[ImageDoc]
(
    [SystemName] ASC,
    [ICMPDocTypeCode] ASC,
    [LoanMasterID] ASC,
    [ImageDate] ASC
)
INCLUDE (   [ImageDocumentID],
    [ImageDocType],
    [BatchName],
    [SignatureInd],
    [ICMPDocCategoryCode],
    [ICMPDocSubTypeCode])
Run Code Online (Sandbox Code Playgroud)

所以这是我的问题。ImageDate 在这个索引中,为什么在我的查询中使用它会如此严重地损害性能?包含该谓词不应该更容易消除行吗?我在做什么/思考错误?

Mar*_*ith 4

您的查询正在查找2015-04-25AND之间的值2015-05-30

其中看起来你有大约 3000 万。

不幸的是,基数估计存在一个错误DATEDIFF,其中涉及到组件颠倒的情况。

SELECT DATEADD(WK, DATEDIFF(WK, GETDATE(), 0) - 4, -30) ,  
       DATEADD(WK, DATEDIFF(WK, GETDATE(), 0) - 4, 0) + 5
Run Code Online (Sandbox Code Playgroud)

1784-05-15返回到的范围1784-06-19。极有可能表(或统计直方图)中没有该范围内的日期,因此 SQL Server 将估计扫描中不会返回任何行,当然不需要 3000 万次查找。

这是一个已修复的错误,但需要启用跟踪标志 4199

如果不可能,您可以尝试以不同的方式重新表述谓词,以避免使用变量DATEDIFF或将值分配给变量并使用OPTION (RECOMPILE)