Sql server 摆脱完全索引扫描

Sql server 摆脱完全索引扫描,sql-server,tsql,sql-server-2008,query-optimization,Sql Server,Tsql,Sql Server 2008,Query Optimization,以下查询执行得很糟糕,因为在P4FileReleases中对650万条记录进行了完整的非聚集索引扫描,然后进行了哈希联接。我正在寻找优化器选择扫描而不是搜索的可能原因 SELECT p4f.FileReleaseID FROM P4FileReleases p4f INNER JOIN AnalyzedFileView af ON p4f.FileRelease = (af.path+'#'+cast(af.revision as varchar)) WHE

以下查询执行得很糟糕,因为在P4FileReleases中对650万条记录进行了完整的非聚集索引扫描,然后进行了哈希联接。我正在寻找优化器选择扫描而不是搜索的可能原因

SELECT p4f.FileReleaseID 
   FROM P4FileReleases p4f
   INNER JOIN AnalyzedFileView af 
      ON p4f.FileRelease = (af.path+'#'+cast(af.revision as varchar))  
   WHERE (af.tracked_change_id = 1)
据我所知,我认为优化器没有理由选择扫描P4FileReleases。WHERE子句将正确数据集的大小限制为1K左右的记录,优化器应该知道这一点(请参见下面的直方图)

事实上,如果我获取视图数据并将其放入一个堆表(与索引视图的结构相同),那么查询将在较大的表上执行索引查找,并使用内部连接循环而不是哈希连接(总成本从145降至1左右)

关于什么可能会使优化器失效,有什么想法吗

详细信息。Sql Server 2008(10.0.2757.0版)

P4FileReleases表 拥有650万条记录

CREATE TABLE [dbo].[P4FileReleases](
    [FileReleaseID] [int] IDENTITY(1,1) NOT NULL,
    [FileRelease] [varchar](254) NOT NULL,
    -- 5 more fields 
 CONSTRAINT [CIX_P4FileReleases_FileReleaseID_PK] PRIMARY KEY CLUSTERED 
(
    [FileReleaseID] ASC
),
CONSTRAINT [NCIX_P4FileReleases_FileRelease] UNIQUE NONCLUSTERED 
(
    [FileRelease] ASC
)
分析数据视图 是一个已启用统计信息且为最新的索引视图

它有四列:

   key int (int, PK) - clustered index
   tracked_change_id (int, FK) - non-unique, non-clustered index (covering 'path', 'revision')
   path (nvarchar(1024), null) 
   revision (smallint, null)
跟踪的\u更改\u id直方图:

1   0   1222    0   1
4   0   787     0   1
8   0   2754    0   1
12  0   254     0   1
13  0   34      0   1
查询计划

  |--Parallelism(Gather Streams)
       |--Hash Match(Inner Join, HASH:([Expr1011])=([Expr1010]), RESIDUAL:([Expr1010]=[Expr1011]))
            |--Bitmap(HASH:([Expr1011]), DEFINE:([Bitmap1015]))
            |    |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([Expr1011]))
            |         |--Compute Scalar(DEFINE:([Expr1011]=([qpsitools].[dbo].[analyzed_file_view].[path]+N'#')+CONVERT_IMPLICIT(nvarchar(30),CONVERT(varchar(30),[qpsitools].[dbo].[analyzed_file_view].[revision],0),0)))
            |              |--Index Seek(OBJECT:([qpsitools].[dbo].[analyzed_file_view].[tracked_change_id]), SEEK:([qpsitools].[dbo].[analyzed_file_view].[tracked_change_id]=(1)) ORDERED FORWARD)
            |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([Expr1010]), WHERE:(PROBE([Bitmap1015],[Expr1010])))
                 |--Compute Scalar(DEFINE:([Expr1010]=CONVERT_IMPLICIT(nvarchar(254),[Blueprint].[dbo].[P4FileReleases].[FileRelease] as [p4f].[FileRelease],0)))
                      |--Index Scan(OBJECT:([Blueprint].[dbo].[P4FileReleases].[NCIX_P4FileReleases_FileRelease] AS [p4f]))

你的问题不是WHERE而是JOIN,你得到了一个隐式转换和对JOIN的扫描,在WHERE条件下你得到了一个SEEK

ON p4f.FileRelease = (af.path+'#'+cast(af.revision as varchar))  
并行性也可能是个问题,请尝试添加MAXDOP=1

你的统计数据是最新的吗?是否存在过多的碎片?

尝试将“af.tracked\u change\u id=1”移动到join子句中

INNER JOIN AnalyzedFileView af 
ON p4f.FileRelease = (af.path+'#'+cast(af.revision as varchar))
AND af.tracked_change_id = 1

其中,在内部联接之后应用,您将使用nvarchar列(af.path)联接varchar列p4f.FileRelease。由于数据类型不匹配,SQL必须将一种类型转换为另一种类型(当然不能从nvarchar转换为varchar)。在将af.path转换为nvarchar时,它失去了使用索引查找/过滤这些值的能力,因此需要扫描和转换所有可能的行


最好的解决方案是将数据存储为匹配的数据类型(将列p4f.FileRelase更改为nvarchar,或将af.path更改为varchar)。由于没有人能够修改现有的数据库结构,因此一种解决方法可能是在查询中显式地将af.path强制转换为varchar。测试一下,看看。。。当然,如果数据确实需要双字节格式,您就不能这样做。

菲利普·凯利发现了这个问题。P4FileReleases中的varchar和AnalyzedFileView中的nvarchar之间的数据类型不匹配

即使它被存储为匹配类型,但他将两个字段合并为一个#中间值的事实将强制扫描,不是吗?太棒了!看起来问题出在varchar和nvarchar之间。谢谢@Mike M,SQL在“…WHERE ColName=@Variable”和“…WHERE ColName=@Variable+@Variable”之间没有什么区别。它获取您生成的任何值,并在表中查找它。是的,这就是我要说的:我正在查找where,扫描join。我想知道为什么。实际上我使用的是MAXDOP=1,我只是在这里省略了它。因此,我包含的所有数据都是用于非并行处理的。优化器不知道af.path+“#”+cast(af.revision as varchar)的值是多少,因此它必须扫描整个表,这样做没有任何影响。优化器是否应该足够聪明,可以自动进行这样的重新排列?