摆脱全索引扫描

Dmi*_* B. 2 t-sql sql-server query-optimization sql-server-2008

以下查询执行得很糟糕,因为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)
Run Code Online (Sandbox Code Playgroud)

据我所知,我认为优化器没有理由选择P4FileReleases的扫描.WHERE子句将右侧数据集的大小限制为大约1K的记录,优化器应该知道它(参见下面的直方图).

事实上,如果我获取视图数据并将其扔入堆表(与索引视图相同的结构),则执行查询,在较大的表上使用索引搜索,而不是使用散列连接(和总成本从145降至1左右.

关于什么可能抛出优化器的任何想法?

细节. Sql Server 2008(v.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
)
Run Code Online (Sandbox Code Playgroud)

AnalyzedFileView 是一个索引视图,其中包含统计信息并且已启用最新信息.

它有四列:

   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)
Run Code Online (Sandbox Code Playgroud)

tracked_change_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
Run Code Online (Sandbox Code Playgroud)

查询计划

  |--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]))
Run Code Online (Sandbox Code Playgroud)

Phi*_*ley 5

您正在使用nvarchar列(af.path)加入varchar列p4f.FileRelease.由于数据类型不匹配,SQL必须将一个类型转换为另一个类型(当然它不能从nvarchar转换为varchar).在将af.path转换为nvarchar时,它将失去使用索引查找/过滤这些值的能力,从而导致需要扫描和转换所有可能的行.

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