查询性能在两个类似查询之间发生显着变

时间:2017-10-11 14:53:01

标签: sql sql-server

我有一个简单的查询,如下所示:

SELECT 
    COUNT(*)
FROM  
    [dbo].[Tenants] AS [Extent1]
LEFT OUTER JOIN 
    [dbo].[Sectors] AS [Extent2] ON [Extent1].[SectorId] = [Extent2].[Id]
WHERE 
    (CONTAINS([Extent1].[Name], '"asdf*"')) OR 
    (CONTAINS([Extent2].[Name], '"asdf*"'))

名为Tenants的表有386533行,Sectors有40行。我为这两列启用了全文搜索。此查询的执行计划如下:

enter image description here

此查询需要8秒才能运行。但是,当我评论其中一个过滤器时(对于两个过滤器都是如此),它们会立即生效。例如,以下内容立即起作用:

SELECT 
    COUNT(*)
FROM  
    [dbo].[Tenants] AS [Extent1]
LEFT OUTER JOIN 
    [dbo].[Sectors] AS [Extent2] ON [Extent1].[SectorId] = [Extent2].[Id]
WHERE 
    --(CONTAINS([Extent1].[Name], '"asdf*"')) OR 
    (CONTAINS([Extent2].[Name], '"asdf*"'))

此查询的查询执行计划如下:

enter image description here

我尝试重建索引和统计信息,但没有帮助。另外,我尝试将PK索引作为提示(因为第二个查询使用它而第一个查询没有),但事情变得更糟。我该怎么做才能加快第一次查询?我不想将查询分成2并将计数加起来(虽然可以立即生效..)

编辑:SQL Server版本为" Microsoft SQL Server 2014 - 12.0.2342.0(X64)"

修改2:客户统计信息:

enter image description here

2 个答案:

答案 0 :(得分:2)

在第一种情况下,SQL Server需要执行OUTER JOIN,因为您使用两个表来过滤信息。

在第二种情况下,SQL Server只需要进行INNER JOIN,因为您已经过滤了其中一个表中的信息而您并不关心另一个表,因为这不会影响您的count(*)语句

让我告诉你关于第二种情况的更多细节:

如果按Extend1.Name过滤,则SQL Server不关心Extend2表,因为您已经过滤了信息,而Extend2表不会更改记录数。

如果按Extend2.Name过滤,则SQL Server不需要OUTER JOIN生成的NULL值,这意味着所有记录都已在Extend1表中。 (对于那种情况,你不需要外连接,内部连接就足够了)

(您可以在两个查询计划中看到这一点)

您可以在表Tenants中将SectorId列更改为NOT NULL并指定默认值。之后,您可以将OUTER JOIN更改为INNER JOIN,性能应该更好。

答案 1 :(得分:2)

尝试使用UNION

SELECT COUNT(*)
FROM ((SELECT . . .
       FROM [dbo].[Tenants] [Extent1] LEFT OUTER JOIN 
            [dbo].[Sectors] [Extent2]
            ON [Extent1].[SectorId] = [Extent2].[Id]
       WHERE CONTAINS([Extent1].[Name], '"asdf*"')
      ) UNION
      (SELECT . . .
       FROM [dbo].[Tenants] [Extent1] INNER JOIN 
            [dbo].[Sectors] [Extent2]
            ON [Extent1].[SectorId] = [Extent2].[Id]
       WHERE CONTAINS([Extent2].[Name], '"asdf*"')
      ) 
     ) x;

注意:

  • 单独的查询可能允许优化器更有效地使用全文索引。
  • 这使用UNION,因为这两个查询可能会返回相同的行。
  • . . .用于标识要计算的每一行的相应列。这是UNION
  • 所必需的
  • 第二个JOIN内部联接WHERE子句中的条件意味着第二个表中必须存在匹配。