SQL Server 突然使用索引扫描而不是查找

Viv*_*ngh 2 sql-server-2016 query-performance

我们有一个每天运行的存储过程。在其中一个子查询(执行计划中的查询 20)中,SQL Server 在 2021 年 9 月 24 日之前一直使用索引查找。但突然间,当查询在 2021/09/25 运行时,SQL Server 在同一步骤开始使用索引扫描。

以下是 2021 年 9 月 24 日执行搜寻的计划

2021/09/24查询计划 寻求2021/09/24手术

以下是 2021 年 9 月 25 日 SQL Server 执行索引扫描的计划

2021/09/25查询计划 2021/09/25 扫描操作

EMAIL_SENDS_CCMP_LTD 表是一个相当大的表,下面是该表的空间使用数据

EMAIL_SENDS_CCMP_LTD 统计信息

以下是上面突出显示的存储过程中的查询 -

insert into  TALBOTS_BASE.dbo.EMAIL_ACTIVITIES
(EMAILTYPE,INTSOURCE,EMAIL,ACCTNO,FNAME,LNAME,EMAILDATE,DNEFLAG,SOURCE_CD,CREATE_ID,EMAILPREF,HASH,FILENAME,SEQ,FILEDATE,MODDATE,IPADDRESS,HAV_EMAILDATE)
SELECT 
      'OPEN' AS EMAILTYPE
      ,'CCMP' AS INTSOURCE 
      , a.[P_email] AS EMAIL
      ,'' AS ACCTNO
      ,'' AS [FNAME]
      ,'' AS [LNAME]
      ,CASE WHEN ISNULL(c.Subchannel,'')<>'HWW' 
       THEN
            CASE WHEN ISDATE([click_time])=1 THEN CAST([click_time] AS DATE) ELSE NULL END
      ELSE NULL
      END AS EMAILDATE
      ,'' as DNEFLAG
      ,'' AS SOURCE_CD
      ,'' AS CREATE_ID
      ,null as EMAIL_CONTACT_PREFERENCE
      ,a.HASH
      ,a.FILENAME
      ,a.SEQ
      ,a.FILEDATE
      ,a.MODDATE
      ,a.ip_address
      ,CASE WHEN c.Subchannel='HWW' 
       THEN
            CASE WHEN ISDATE([click_time])=1 THEN CAST([click_time] AS DATE) ELSE NULL END--HAVEN
      ELSE NULL
      END AS HAV_EMAILDATE
FROM [TALBOTS_RAW].[dbo].[EMAIL_OPENS_CCMP_LTD] a
left join #activitiesfiles b 
on a.FILENAME = b.FILENAME 
LEFT JOIN [TALBOTS_RAW].[dbo].[EMAIL_SENDS_CCMP_LTD] c 
ON a.p_email = c.p_email
AND a.msg_id = c.msg_id  
WHERE b.FILENAME is null
Run Code Online (Sandbox Code Playgroud)

EMAIL_SENDS_CCMP_LTD 表上有一个非聚集索引,该索引位于 p_email(VARCHAR) 和 msg_id(VARCHAR) 列上,其中包含的列为 Subchannel(VARCHAR)。

我的问题是 SQL Server 决定更改哪些参数的查询计划,以及是否有办法允许 SQL Server 使用查找而不是扫描。

粘贴实际计划

2021/09/24查询计划

2021/09/25查询计划

Eri*_*ing 6

埃尔过滤器

您的计划更改的原因是因为对将通过过滤器运算符的行数的基数估计发生了很大的变化。

坚果

一般来说,这是一件很难估计的事情——您使用左连接来查找不存在的行#activitiesfiles——这通常使用 NOT EXISTS 可以更好地表达

FROM [TALBOTS_RAW].[dbo].[EMAIL_OPENS_CCMP_LTD] a
LEFT JOIN [TALBOTS_RAW].[dbo].[EMAIL_SENDS_CCMP_LTD] c 
    ON  a.p_email = c.p_email
    AND a.msg_id = c.msg_id  
WHERE NOT EXISTS
      (
          SELECT
              1/0
          FROM #activitiesfiles b 
          WHERE a.FILENAME = b.FILENAME 
      );
Run Code Online (Sandbox Code Playgroud)

看起来您并没有让自己的事情变得更轻松,因为您的电子邮件打开表似乎没有有用的索引。

坚果

我意识到这不是您有问题的计划的一部分,但值得注意,因为问题在于选择的连接算法。

您可能会得到一个更好的整体计划,其中filename, msg_id, p_email包含您正在选择的列的索引EMAIL_OPENS_CCMP_LTD

解决这个问题有点困难,因为您的列并不都以您为表提供的别名作为前缀,所以我将其留给您。

最后,您至少应该了解 SQL Server 如何在查询计划中缓存临时对象和统计信息,Paul White 在这三篇文章中对此进行了解释: