【问题标题】:Getting rid of full index scan摆脱全索引扫描
【发布时间】:2011-03-12 11:07:02
【问题描述】:

以下查询执行不佳,因为对 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 (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
)

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)

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

查询计划

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

【问题讨论】:

    标签: sql-server tsql sql-server-2008 query-optimization


    【解决方案1】:

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

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

    【讨论】:

    • 即使它被存储为匹配类型,他将两个带有 # 的字段连接起来的事实会强制进行扫描,不是吗?
    • 太棒了!看起来 varchar vs nvarchar 是问题所在。谢谢!
    • @Mike M,to SQL 在“...WHERE ColName = @Variable”和“...WHERE ColName = @Variable + @Variable”之间几乎没有区别.它采用您生成的任何值,并在表中查找。
    【解决方案2】:

    您的问题不是 WHERE,而是 JOIN,您正在获得隐式转换和 JOIN 扫描,在 WHERE 条件下您正在获得 SEEK

    ON p4f.FileRelease = (af.path+'#'+cast(af.revision as varchar))  
    

    并行度也可能是个问题,请尝试添加 MAXDOP=1

    您的统计数据是最新的吗?是否存在过度碎片化?

    【讨论】:

    • 是的,这就是我要说的:我正在寻找位置并扫描连接。我想知道为什么。我实际上使用的是 MAXDOP=1,我只是在这里省略了它。所以我包含的所有数据都是用于非并行处理的。
    • 优化器不知道 af.path+'#'+cast(af.revision as varchar) 的值是多少,所以它必须扫描整个表
    【解决方案3】:

    尝试将“af.tracked_change_id = 1”移动到连接子句中。

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

    WHERE 在 INNER JOIN 之后应用

    【讨论】:

    • 这样做没有任何影响。优化器不应该足够聪明以自动进行这样的重新排列吗?
    【解决方案4】:

    Philip Kelley 发现了问题所在。这是 P4FileReleases 中的 varchar 和 AnalyzedFileView 中的 nvarchar 之间的数据类型不匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-28
      • 2016-11-24
      • 1970-01-01
      • 2013-10-01
      • 2020-01-10
      相关资源
      最近更新 更多