sql-server - 摆脱全索引扫描

标签 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))  
   WHERE (af.tracked_change_id = 1)

据我所知,我认为优化器没有理由选择 P4FileReleases 扫描。 WHERE 子句将正确数据集的大小限制为大约 1K 条记录,优化器应该知道这一点(请参见下面的直方图)。

事实上,如果我获取 View 数据并将其放入堆表中(与索引 View 的结构相同),则查询是通过在较大的表上进行索引查找和内部联接循环而不是哈希来执行的加入(总成本从 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
)

分析文件 View 是启用统计且最新的索引 View 。

它有四列:

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

最佳答案

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

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

关于sql-server - 摆脱全索引扫描,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3188944/

相关文章:

sql-server - 如何获得最高薪员工行

sql - 在日期时间间隔内展开表格

Java/SQL Server 2008 R2 Express 连接问题

sql-server - 运行 fn_trace_gettable

c# - 使用 C# 进行 SQL Server 备份

sql-server - SQL Server 复杂排名

sql - 连接两个具有不同数据的相同表结构

sql-server - SQL 2012 - 出于性能原因对用户表字段电子邮件和密码建立索引

带时区的 SQL Server DateTime

sql - GRANT EXECUTE 所需的权限