2013-01-04 50 views
16

我有一個表:SQL服務器:錯誤的數據類型爲varchar轉換爲數字

Account_Code | Desc 
503100  | account xxx 
503103  | account xxx 
503104  | account xxx 
503102A  | account xxx 
503110B  | account xxx 

Account_Codevarchar

當我創建以下查詢:

Select 
    cast(account_code as numeric(20,0)) as account_code, 
    descr 
from account 
where isnumeric(account_code) = 1 

它通過返回在account_code列一個有效的數值都記錄運行良好。

但是當我嘗試添加另一種選擇,嵌套前SQL:

select account_code,descr 
from 
(
    Select cast(account_code as numeric(20, 0)) as account_code,descr 
    from account 
    where isnumeric(account_code) = 1 
) a 
WHERE account_code between 503100 and 503105 

查詢將返回一個錯誤

錯誤轉換數據類型爲varchar到數字。

這是怎麼回事呢?

我已如果account_code有效已經轉換爲數字,但似乎查詢仍在努力處理非有效的記錄。

我需要使用BETWEEN子句中我的查詢。

+2

什麼版本的SQL Server? – ErikE

回答

8

有沒有保證,SQL Server將不會嘗試之前執行CONVERTnumeric(20,0)它運行WHERE子句中的過濾器。

而且,即使它沒有,ISNUMERIC是不夠的,因爲它承認£1d4爲數字,兩者都不可以轉換爲numeric(20,0)。(*)

拆分成兩個單獨的查詢,第一個過濾結果並將它們放在臨時表或表變量中,其中第二個執行轉換。 (子查詢和CTE不足以防止優化器在過濾器之前嘗試轉換)

對於您的過濾器,可能使用account_code not like '%[^0-9]%'而不是ISNUMERIC


(*)ISNUMERIC回答了這個問題,沒有人(到目前爲止我所知)曾經想要問 - 「可這個字符串轉換爲數字數據類型的任何 - 這不是我不在乎哪個?「 - 很明顯,大多數人想問的是「這個字符串可以轉換爲x嗎?」其中x特定的目標數據類型。

25

的SQL Server 2012及更高版本

只需使用Try_Convert代替:

TRY_CONVERT需要傳遞給它的價值,並嘗試將其轉換爲指定的DATA_TYPE。如果轉換成功,則TRY_CONVERT將返回值作爲指定的data_type;如果發生錯誤,則返回null。但是,如果您請求明確不允許的轉換,則TRY_CONVERT會失敗並顯示錯誤。

Read more about Try_Convert

SQL Server 2008和早期

處理這種傳統的方法是守着一個case語句的每一個表情,以便沒有當其被評估的事情,它不會產生錯誤,即使它在邏輯似乎不應該需要CASE聲明。事情是這樣的:

SELECT 
    Account_Code = 
     Convert(
     bigint, -- only gives up to 18 digits, so use decimal(20, 0) if you must 
     CASE 
     WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL 
     ELSE X.Account_Code 
     END 
    ), 
    A.Descr 
FROM dbo.Account A 
WHERE 
    Convert(
     bigint, 
     CASE 
     WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL 
     ELSE X.Account_Code 
     END 
    ) BETWEEN 503100 AND 503205 

不過,我喜歡用的策略,如這與SQL Server 2005及以上:

SELECT 
    Account_Code = Convert(bigint, X.Account_Code), 
    A.Descr 
FROM 
    dbo.Account A 
    OUTER APPLY (
     SELECT A.Account_Code WHERE A.Account_Code NOT LIKE '%[^0-9]%' 
    ) X 
WHERE 
    Convert(bigint, X.Account_Code) BETWEEN 503100 AND 503205 

這樣做是戰略性切換Account_CodeNULLX表內當他們不是數字時。我最初使用CROSS APPLY,但作爲Mikael Eriksson如此恰當地指出,這導致了相同的錯誤,因爲查詢解析器遇到了優化我的嘗試強制執行表達式順序(謂詞下推擊敗它)完全相同的問題。通過切換到OUTER APPLY它改變了操作的實際意義,使得X.Account_Code包含外部查詢內NULL值,因此需要適當的評價順序。

你可能會有興趣看Erland Sommarskog's Microsoft Connect request這個評估順序問題。他實際上稱之爲錯誤。

這裏還有其他問題,但我現在無法解決它們。

P.S.我今天有頭腦風暴。我建議的「傳統方式」的替代方法是使用外部引用的SELECT表達式,該函數也適用於SQL Server 2000.(我注意到,自從學習CROSS/OUTER APPLY以來,我使用較舊的SQL Server版本改進了查詢功能,太 - !因爲我越來越多功能與SELECTON,並WHERE條款「外引用」功能)

SELECT 
    Account_Code = 
     Convert(
     bigint, 
     (SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%') 
    ), 
    A.Descr 
FROM dbo.Account A 
WHERE 
    Convert(
     bigint, 
     (SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%') 
    ) BETWEEN 503100 AND 503205 

它比CASE語句短了很多。

+0

查詢是否適合您?它不適合我,除非我將'cross apply'改爲'outer apply'。 (SQL Server 2012) –

+0

我的意思是說你的當前版本不起作用。查詢計劃執行表格掃描,檢查每一行。如果您更改爲外部應用程序,則會在轉換之前過濾出錯誤的行。 [SQL Fiddle](http://sqlfiddle.com/#!6/664af/3/0) –

6

如果您正在運行SQL Server 2012,您還可以使用新的TRY_PARSE()功能:

返回一個表達式的結果,轉換爲所請求的數據 類型,或者如果轉換在SQL Server中失敗,返回NULL 2012.僅使用TRY_PARSE 可將字符串轉換爲日期/時間和數字類型。

1

我認爲問題不在子查詢中,而是在外部查詢的WHERE子句中。 當您使用

WHERE account_code between 503100 and 503105 

SQL服務器將嘗試每一個值轉換成你的ACCOUNT_CODE領域爲整數,以測試其在規定的條件。很顯然,如果某些行中會存在非整數字符,將會失敗。

0

感謝, 試試這個,而不是

Select 
    STR(account_code) as account_code_Numeric, 
    descr 
from account 
where STR(account_code) = 1 

我很樂意幫助你

相關問題