2012-01-30 68 views
1

我有一個SQL查詢,需要這些參數:如何使用大型複雜where子句構建查詢?

@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必須都爲空,或兩者都有值。

我沒有要發佈整個查詢作爲它的枯燥,難以閱讀,但在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. 更改存儲過程,以便它檢查哪些參數爲空,並根據傳遞的參數執行子過程。這裏的問題是,這意味着在過程的定義中重複我自己,因此更難以維護。

我該怎麼辦?還是應該繼續保持現狀?我爲程序設置了OPTION (RECOMPILE),但我聽說這在Server 2005中不起作用。另外,我打算爲此proc添加更多參數,因此我想確保我擁有的任何解決方案都是可擴展的。

+0

**不成熟的優化是所有邪惡的根源**您是否嘗試過當前查詢?它工作速度夠快嗎? – JNK 2012-01-30 16:00:22

+0

您也可以在字符串中構建查詢,只在需要時添加到WHERE子句,然後執行該字符串。你可能仍然有這樣的執行計劃的問題 – msmucker0527 2012-01-30 16:00:42

+0

@Jnk它已經超級慢,但我遇到了很多麻煩修復。我不想讓它變得更慢,因爲我添加了更多的參數。 – Oliver 2012-01-30 16:01:17

回答

4

的答案是使用DynamicSQL(無論是在客戶端,或者在使用sp_executesql的SP),但之所以很長,所以這裏是一個鏈接...

Dynamic Search Conditions in T-SQL

一個非常簡短的版本是一個尺寸不適合所有。當優化器爲一個查詢創建一個計劃時,它很慢。因此,解決方案是繼續使用參數化查詢(用於執行計劃緩存),但對於可能發生的不同類型的搜索,有許多查詢。

+0

如果我使用'sp_executesql'從客戶端應用程序執行計劃緩存工作,只要使用參數? – Oliver 2012-01-30 16:11:51

+0

@Oliver - 是的,雖然我不知道你需要直接使用它的原因。大多數框架(ADO.NET等)將允許您動態構建查詢,並使用參數。兩種方法都可以提供計劃緩存。如果正在構建的查詢是在t-sql中構建的,我只能使用sp_executesql。 – MatBailie 2012-01-30 16:15:40

1

也許另一種選擇可能是執行幾個單獨的select語句?

例如

(-- 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) 

等等

然後選擇臨時表返回的最終結果集。

我不知道這將如何適用於優化程序和查詢計劃,但每個單獨的選擇將非常簡單,並且可以利用您將在每列創建的索引,這些索引應該使它們非常快速。

根據您的要求,在臨時表上創建一個主鍵以允許您在每次選擇時加入它(以避免重複)也是有意義的。

0

先看看錶現,就像其他人說的一樣。

如果可能,可以使用IF子句根據提供的參數來簡化查詢。

如果你發現你經常重複,你也可以使用函數或視圖來封裝一些代碼。