【问题标题】:How to structure a query with a large, complex where clause?如何使用大型、复杂的 where 子句构建查询?
【发布时间】:2012-01-31 05:39:47
【问题描述】:

我有一个使用这些参数的 SQL 查询:

@SearchFor  nvarchar(200) = null 
,@SearchInLat Decimal(18,15) = null
,@SearchInLng Decimal(18,15) = null
,@SearchActivity int = null
,@SearchOffers bit = null
,@StartRow int
,@EndRow int

变量@SearchFor@SearchActivity@SearchOffers 可以为空或不为空。 @SearchInLat@SearchInLng 必须都为 null,或者都具有值。

我不打算发布整个查询,因为它无聊且难以阅读,但 WHERE 子句的形状如下:

( -- filter by activity --
    (@SearchActivity IS NULL)
    OR (@SearchActivity = Activities.ActivityID)
)
AND ( -- filter by Location --
    (@SearchInLat is NULL AND @SearchInLng is NULL)
    OR ( ... )
)
AND ( -- filter by activity --
    @SearchActivity is NULL
    OR ( ... )
)
AND ( -- filter by has offers --
    @SearchOffers is NULL
    OR ( ... )
)
AND (
    ... -- more stuff
)

我已经读到这是一种构建查询的糟糕方法 - SqlServer 在制定包含大量此类子句的高效执行计划时遇到了麻烦,因此我正在寻找其他方法来做到这一点。

我看到了两种方法:

  1. 在我的客户端应用程序中将查询构造为字符串,以便WHERE 子句仅包含相关参数的过滤器。这样做的问题是它意味着不通过存储过程访问数据库,就像目前其他一切一样。
  2. 更改存储过程,使其检查哪些参数为空,并根据传递的参数执行子过程。这里的问题是,这意味着在 proc 的定义中重复我自己,因此更难维护。

我该怎么办?还是应该像现在这样继续?我为程序设置了OPTION (RECOMPILE),但我听说这在 Server 2005 中无法正常工作。另外,我计划向这个程序添加更多参数,所以我想确保我拥有的任何解决方案都是公平的可扩展。

【问题讨论】:

  • 过早的优化是万恶之源。你试过当前的查询吗?它的运行速度够快吗?
  • 您也可以在字符串中构建查询,仅在需要时添加到 WHERE 子句,然后执行该字符串。以这种方式执行计划可能仍然存在问题
  • @Jnk 这已经非常慢了,但我在修复它时遇到了很多麻烦。我不希望它在添加更多参数时变得更慢。

标签: sql sql-server sql-server-2005


【解决方案1】:

答案是使用DynamicSQL(无论是在客户端,还是在SP中使用sp_executesql),但是原因很长,所以这里有一个链接...

Dynamic Search Conditions in T-SQL

一个非常短的版本是一种尺寸并不适合所有人。由于优化器为一个查询创建一个计划,所以速度很慢。因此,解决方案是继续使用参数化查询(用于执行计划缓存),但要针对可能发生的不同类型的搜索进行许多查询。

【讨论】:

  • 如果我在客户端应用程序中使用sp_executesql,只要我使用参数,执行计划缓存是否有效?
  • @Oliver - 是的,尽管我不知道您需要直接使用它的原因。大多数框架(ADO.NET 等)将允许您动态构建查询,并为它们提供参数。任何一种方法都提供计划缓存。如果正在构建的查询是在 t-sql 中构建的,我只会使用 sp_executesql。
【解决方案2】:

也许另一种方法是执行几个单独的选择语句?

例如

( -- filter by activity --
if @SearchActivity is not null
    insert into tmpTable (<columns>)
    select *
    from myTable
    where (@SearchActivity = Activities.ActivityID)
)

( -- filter by Location --
if @SearchInLat is not null and @SearchInLng is not null
    insert into tmpTable (<columns>)
    select *
    from myTable
    where (latCol = @SearchInLat AND lngCol = @SearchInLng)

等等……

然后选择临时表返回最终结果集。

我不确定这对于优化器和查询计划如何工作,但每个单独的选择都非常简单,并且可以利用您在每列上创建的索引,这应该会使它们非常快速。

根据您的要求,在临时表上创建一个主键以允许您在每次选择时加入它(以避免重复)也是有意义的。

【讨论】:

    【解决方案3】:

    先看表现,就像其他人说的那样。

    如果可能,您可以使用 IF 子句根据提供的参数简化查询。

    如果你发现你经常重复代码,你也可以使用函数或视图来封装一些代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-14
      • 2021-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多