SQL Server查询:通过减少WHERE子句来提高性能

时间:2018-05-29 15:51:59

标签: sql sql-server performance where-clause querying

我有一个来自我的队友的SQL查询,我认为它有很多谓词,这是导致性能不佳的原因。它看起来像这样:

WHERE 
      (@IdSenales IS NULL OR senalesIds.id = comp.IdSenal) 
      AND
      (@IdAnunciantes IS NULL OR anunciantesIds.id = comp.IdAnunciante) 
      AND                                    
      (@IdProgramas IS NULL OR programasIds.id = emision.IdProgramaVariante) 
      AND   
      (@IdTipoPublicidades IS NULL OR publicidadesIds.id = orden.IdTipoPublicidad) 
      AND
      (@Canje = 0 OR (@canje = 1 AND comp.IdTipoCondicionCobro !=  12)) 
      AND 
      (emision.Fecha BETWEEN @FechaDesdeContrato AND ISNULL(@FechaHastaContrato, emision.fecha)) 
      AND
      (comp.FechaEmision BETWEEN @FechaDesde AND @FechaHasta) 
      AND                                                   
      (@IdSectorImputacion = 0 OR @IdSectorImputacion = simp.IdSectorImputacion) 

我来自阿根廷,所以它用西班牙语发表评论(对不起)。

我的问题是,是否可以通过将WHERE子句中的比较更改为由我创建的返回相同的函数来提高性能?

非常感谢,

大卫

3 个答案:

答案 0 :(得分:4)

评论时间有点长。

您可以真正显着提高性能的唯一方法是使用索引。这将需要一堆索引用于所有不同的组合 - 但也许少数更常见,并且足以满足大多数用例。

SQL Server在优化复杂的where子句方面非常糟糕。您可以做的是使用动态SQL。仅通过添加必要的条件来构造where子句。

然后,确保您有常见情况的索引。在编译查询时,它应该运行得更快。

答案 1 :(得分:0)

@GordonLinoff所述,您最好的选择是查看使用的索引。他也比我好得多;如果你能够,请听取他对我的建议。但是,如果您的公司由于某种原因不允许使用动态SQL,或者重写不是一个选项,请继续阅读...

你可能没有像你想象的那么大的问题;你有没有看到性能问题,或者你只是看着代码&思考"很多东西都有很多括号,所以这很糟糕"?

即。走这一行:(@IdSenales IS NULL OR senalesIds.id = comp.IdSenal)。 这将参数与null进行比较,因此只需要进行一次,而不是每行一次;这不是太糟糕。此后,与没有此声明或只有senalesIds.id = comp.IdSenal的情况没有什么不同。大多数这些线也是如此。

也就是说,SQL将在第一次运行此代码时生成查询计划,然后将其用于所有后续查询,无论使用哪个参数;因此,该计划可能完全不适合新的选项。这里一个很好的解决方法是添加OPTION (RECOMPILE)。你可以在这里找到一个很好的解释:https://blogs.msdn.microsoft.com/robinlester/2016/08/10/improving-query-performance-with-option-recompile-constant-folding-and-avoiding-parameter-sniffing-issues/

除此之外,这一行可能是一个问题,因为它涉及应用一个函数,其输出对于每一行是不同的;所以它很容易优化:

  (emision.Fecha BETWEEN @FechaDesdeContrato AND ISNULL(@FechaHastaContrato, emision.fecha)) 

将其更改为:

  (emision.Fecha >= @FechaDesdeContrato AND (emision.Fecha <= @FechaHastaContrato )) 

......你应该没事。

完整代码:

WHERE 
    (@IdSenales IS NULL OR senalesIds.id = comp.IdSenal) 
    AND
    (@IdAnunciantes IS NULL OR anunciantesIds.id = comp.IdAnunciante) 
    AND                                    
    (@IdProgramas IS NULL OR programasIds.id = emision.IdProgramaVariante) 
    AND   
    (@IdTipoPublicidades IS NULL OR publicidadesIds.id = orden.IdTipoPublicidad) 
    AND
    (@Canje = 0 OR (@canje = 1 AND comp.IdTipoCondicionCobro !=  12)) 
    AND 
    (emision.Fecha >= @FechaDesdeContrato AND (@FechaHastaContrato is null or emision.Fecha <= @FechaHastaContrato )) 
    AND
    (comp.FechaEmision BETWEEN @FechaDesde AND @FechaHasta) 
    AND                                                   
    (@IdSectorImputacion = 0 OR @IdSectorImputacion = simp.IdSectorImputacion) 
OPTION (RECOMPILE)

答案 2 :(得分:0)

感谢您的建议@JohnLBevan !! 我检查了谓词,因为我读了Gail Shaw的一篇文章说:

&#34; SQL Server选择扫描的另一个常见原因是包含多个谓词的查询,当没有单个索引存在具有评估WHERE子句所需的所有列时。例如,(FirstName,Surname)上的索引将完全支持具有FirstName = @FirstName和Surname = @Surname的WHERE子句的任何查询。但是,如果仅在FirstName上有一个索引,而在Surname上有第二个单独的索引,则SQL Server可以无效地使用它们。它可以选择寻找一个索引,对其他列进行查找,然后进行二次过滤;它可以选择寻找索引并执行索引交集,也可以放弃并扫描表格。&#34;

https://www.red-gate.com/simple-talk/sql/database-administration/gail-shaws-sql-server-howlers/

当我读到这篇文章时,我记得我在查询中看到过多个谓词。我想提一下,这个查询是最昂贵的查询之一,它返回我的查询以检查针对数据库的所有查询的开销。 好吧,我应该检查是否有足够的索引和/或创建新索引。

David Linares。