我有一个来自我的队友的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
子句中的比较更改为由我创建的返回相同的函数来提高性能?
非常感谢,
大卫
答案 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。