如何教优化器在数据记录主/明细表上使用索引而不是 fts 连接?

Mor*_*oth 5 performance sql-server optimization sql-server-2012 query-performance

SQL Server 2012 优化器不正确。

测试用例,总结:

这是一个简化的测试场景。底部的 DDL 语句。

我有两个用于数据记录的表,A以及B. 有一个 1:n 关系 -A有一个日期时间的标题记录a_timeB详细记录,一个字段B.akey是引用A.id,字段name(data)

A 有大约。25,000,000 条记录,B大约有。500,000,000 条记录。B大约有 200 条记录引用A. 一个A和大约。B每五分钟一次插入200条记录,由A.a_time.

聚集索引是主键id,类型为 int 标识。

B有一个非聚集索引,命名IX_B_akeyB.akey

A.a_time 也被(非聚集)索引。

现在这个查询:

 SELECT A.a_time, B.*
 FROM B
   join A on B.akey = A.id
 where  
    A.a_time > '2017-01-13T01:30:00' and A.a_time < '2017-01-14T07:30:00'
       and B.name in ('name33', 'name55', 'name66')
Run Code Online (Sandbox Code Playgroud)

在我的数据库服务器上大约需要3 分钟。执行计划:这里(更准确的执行计划见下文)

当我添加一个简单的提示来使用时 IX_B_akey

 SELECT A.a_time, B.*
 FROM B
   with (index(IX_B_akey))
   join A on B.akey = A.id
 where  
    A.a_time > '2017-01-13T01:30:00' and A.a_time < '2017-01-14T07:30:00'
       and B.name in ('name33', 'name55', 'name66')
Run Code Online (Sandbox Code Playgroud)

它在不到一秒的时间内运行。执行计划:这里(更准确的执行计划见下文)

当我update statistics在两个表上手动操作时,这不会改变。

没有提示的查询的查询计划显示服务器将对 进行表扫描B,寻找匹配的names。这将需要一段时间也就不足为奇了。通过提示,它使用索引并通过索引查找 B引用匹配A记录的记录。这要快得多。

我不想将查询优化器代码放入我的软件中。另外,我使用 NHibernate。尽管这是可能的,但使用 NHibernate 拦截器并编辑其 S​​QL 会很丑陋。

也许优化器不知道B引用一个A记录的所有记录在物理上彼此相邻。它们彼此相邻,因为它们是同时插入的。如果它们分散在整个数据库中,则进行所有查找的成本可能会更高。

问题:如何帮助优化器在查询中没有提示的情况下选择快速计划?我可以添加特定的统计数据来帮助这里吗?我需要存储查询计划吗?

作为参考,这里是用于创建表的 DDL 语句。

create table A (
    id int not null identity(1,1),
    a_time datetime,
    constraint pkA primary key (id)
)

create table B (
    id int not null identity(1,1),
    akey int not null references A (id),
    name nvarchar(50),
    d decimal(5,3),
    constraint pkB primary key (id)
    )

create index IX_B_akey on B (akey)
create index IX_A_a_time on A (a_time)
Run Code Online (Sandbox Code Playgroud)

更新:添加name到索引IX_B_Akey可能会有所帮助,但它也会使数据量增加近一倍。这不是一个好的选择。

执行计划更新:发布问题后,我创建了另一个具有相同数据结构但更多数据的测试场景。查询相同,但查询的日期范围已扩展。数据库在 A 中包含 1 个 mio 记录,在 B 中包含 200 个 mio 记录。这允许我提供实际的执行计划:

查询无提示,耗时 27 秒,时间可重现

带提示的查询,需要 3 秒,也可重现

Dav*_*oft 2

糟糕的计划源于艰难的选择。您可以重新组织 B 以优化从 A 到 B 的访问路径,而不是让优化器在具有两个嵌套循环连接的计划和具有大型并行散列连接的计划之间进行选择。

这里最好的索引可能是对B(akey,id)进行聚簇PK。那么在已经更快的计划中将只有一个嵌套循环连接,这使得它明显优于并行哈希连接计划。