【问题标题】:SQL Server querying : improve performance by reducing WHERE clausesSQL Server 查询:通过减少 WHERE 子句提高性能
【发布时间】:2018-11-08 08:19:47
【问题描述】:

我的队友有一个 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 子句中的比较与我创建的返回相同的函数进行比较来提高性能?

非常感谢,

大卫

【问题讨论】:

  • 使用标量函数会更糟,而不是更好。但实际上,这是一种包罗万象的查询,由于执行计划缓存的工作方式,性能将无处不在。查看这篇文章,了解一些改善这一点的好方法。 sqlinthewild.co.za/index.php/2009/03/19/catch-all-queries
  • 可以发一下表结构和一些示例数据吗?并给出所涉及的记录数量的想法。
  • @SeanLange Gail 几个月前发布了对她帖子的更新:sqlinthewild.co.za/index.php/2018/03/13/…
  • @Larnu 我完全忘记了那篇文章。谢谢你的推动。 :)
  • @Nick,我收到这个查询是为了在家里检查它,我不在那个环境中。我想明天我可以从表结构中获得更多信息。我是这个环境中的新手,另一个想法是检查所有数据库设计。我认为这一切都是由不是 DBA 的人创建的......

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


【解决方案1】:

评论有点长。

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

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

然后,请确保您有针对常见情况的索引。并且当查询被编译时,它应该运行得更快。

【讨论】:

  • 只是补充一下,不要忘记,如果您使用的是动态 SQL,请对其进行适当的参数化;不要连接你的字符串!
  • 我从不使用动态 SQL。我必须检查我是否可以添加它或开始使用它。
【解决方案2】:

正如@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)

【讨论】:

    【解决方案3】:

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

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

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

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

    大卫·利纳雷斯。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多