2010-02-16 55 views
3

我有一個列TypeCode varchar(20),其值爲'1','2','3','FOO','BAR'。我需要找到小於參數值的最大整數。事情是這樣的:阻止SQL Server中的查詢重寫

select max(TypeCode) TypeCode 
    from table1 a 
    left join table2 b on b.table1id = a.id 
     and b.TypeCode not in ('FOO', 'BAR') 
    where b.TypeCode < @MaxType 

其中一期工程的大部分時間,但在某些查詢的SQL Server決定(根據查詢計劃),將其轉換爲這樣的事情。

select max(TypeCode) TypeCode 
    from table1 a 
    left join table2 b on b.table1id = a.id 
     and b.TypeCode < @MaxType 
     and b.TypeCode not in ('FOO', 'BAR') 

該查詢顯然產生以下錯誤:

Conversion failed when converting the varchar value 'FOO' to data type int. 

我想沒有「富」和「酒吧」價值創造表2的視圖,並加入視圖代替,但查詢計劃仍一樣。

您是否知道阻止優化器更改查詢的方法? PS:我知道表格的設計並不是最好的,但這是一個遺留數據庫,我無法改變它。

回答

2

這裏真正的問題是你在同一個查詢中結合了字符和整數語義。

有跡象表明,我能想到的三種可能的解決方案:

  1. 更改@MaxType參數是一個varchar(9)。如果您只是比較單位數字,字母順序是好的。否則,這不起作用。

  2. WHERE聲明中使用CASEISNULL(NULLIF(...))構造。這會起作用,但它不可靠,並會導致優化器忽略您在類型代碼中的任何索引。不是很好。

  3. 創建一個持久的,可計算的可空的整數列(即TypeCodeID)並將其分別編入索引。將CASE作爲列的表達式。這將需要一些額外的數據/索引空間,但是如果你想要很好的性能,那是最好的方法。然後代替編寫NOT IN ('Foo', 'Bar'),您可以只寫第一個條件(TypeCode < @MaxType),因爲在TypeCodeID列中,FooBar列在TypeCode列中的行將具有NULL

我想還有第四個答案,就是要改變你的設計,而這將是最好的主意,如果可能的話。如果一列可以包含字符數據,那麼你真的不應該試圖執行數字比較。我強烈懷疑字符數據存在於這一列中,因爲它來自用戶,並且沒有正確驗證/消毒,所以它有垃圾值,如N/AUnknown。如果是這樣的話,你的數據庫確實應該強化數據完整性,這就是DBMS的用途。我知道你說你「不能改變它」,但如果我不建議反對這種危險的做法,我不認爲這個答案是完整的。

0

沒有辦法關閉優化器,而且你可能真的不想反正。更好的解決方案可能是修改查詢。

你可以可能使一個CASE語句這項工作 - 是這樣的:

SELECT MAX(TypeCode) TypeCode 
FROM table1 a 
    LEFT JOIN table2 b ON b.table1id = a.id 
WHERE CASE WHEN b.TypeCode IN ('FOO', 'BAR') THEN 99999999999 ELSE CAST(b.TypeCode AS int) END < @MaxType 

你需要檢查這個性能,看它是否是可以接受的,但它應該工作。