2013-01-04 16 views
16

डेटा प्रकार varchar परिवर्तित मैं एक मेज है:एसक्यूएल सर्वर: त्रुटि के संख्यात्मक

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

कहाँ Account_Code एक varchar है।

जब मैं नीचे एक प्रश्न बनाने के लिए:

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

यह सब रिकॉर्ड account_code कॉलम में मान्य संख्या मान है कि वापस जाकर अच्छी तरह से चलाता है।

लेकिन जब मैं एक और, चयन से पहले एसक्यूएल को नेस्ट जोड़ने का प्रयास:

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

एसक्यूएल सर्वर के किन संस्करणों मदद करने की कोशिश? – ErikE

उत्तर

8

इसकी कोई गारंटी नहीं है कि SQL सर्वर से पहले CONVERTnumeric(20,0)को प्रदर्शन करने के लिए प्रयास नहीं करेंगे यह WHERE खंड में फिल्टर चलाता है।

और, भले ही यह किया, ISNUMERIC नहीं पर्याप्त है, क्योंकि यह £ और 1d4 पहचानता सांख्यिक किया जा रहा है, जिनमें से न तो numeric(20,0) में बदला जा सकता के रूप में दो अलग-अलग क्वेरी में विभाजित है। (*)

, जिनमें से पहला परिणाम फ़िल्टर करता है और उन्हें एक अस्थायी तालिका या तालिका चर में रखता है, जिसमें से दूसरा रूपांतरण करता है। (फ़िल्टरिंग से पहले रूपांतरण का प्रयास करने से ऑप्टिमाइज़र को रोकने के लिए सबक्वायरीज़ और सीटीई अपर्याप्त हैं)

अपने फ़िल्टर के लिए, शायद के बजाय account_code not like '%[^0-9]%' का उपयोग करें।


(*) ISNUMERIC सवाल का जवाब है कि कोई (जहां तक ​​मुझे पता है हूँ) कभी पूछना चाहते है - "इस स्ट्रिंग सांख्यिक डेटाटाइप्स के किसी भी में बदला जा सकता है - मैं डॉन परवाह नहीं है? " - जाहिर है, ज्यादातर लोग क्या पूछना चाहते हैं "क्या यह स्ट्रिंग x में परिवर्तित हो सकती है?" जहां xविशिष्ट लक्ष्य डेटाटाइप है।

25

एसक्यूएल सर्वर 2012 और बाद में

बस बजाय Try_Convert का उपयोग करें:

TRY_CONVERT यह को भेजे गए मान लेता है और यह निर्दिष्ट DATA_TYPE में बदलने की कोशिश करता है।यदि कलाकार सफल होता है, तो TRY_CONVERT मान को निर्दिष्ट डेटा_ प्रकार के रूप में देता है; अगर कोई त्रुटि होती है, तो शून्य वापस आ जाता है। हालांकि अगर आप ऐसे रूपांतरण का अनुरोध करते हैं जिसे स्पष्ट रूप से अनुमति नहीं है, तो TRY_CONVERT किसी त्रुटि के साथ विफल हो जाता है।

Read more about Try_Convert

एसक्यूएल सर्वर 2008 और इससे पहले

इस से निपटने के पारंपरिक तरीके से एक मामले बयान के साथ हर अभिव्यक्ति की रखवाली ताकि वह चाहे जब भी मूल्यांकन किया जाता है, यह एक त्रुटि नहीं बनाएगा कर रहा है, यहां तक ​​कि यह अगर तार्किक ऐसा लगता है कि केस स्टेटमेंट की आवश्यकता नहीं है। कुछ इस तरह:

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 सर्वर 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 

क्या इस रणनीतिक X तालिका के अंदर NULL करने के लिए Account_Code मूल्यों स्विच है करता है जब वे संख्यात्मक नहीं हैं। मैंने शुरुआत में CROSS APPLY का उपयोग किया था, लेकिन Mikael Eriksson के रूप में उपयुक्त रूप से इंगित किया गया, इसके परिणामस्वरूप एक ही त्रुटि हुई क्योंकि क्वेरी पार्सर अभिव्यक्ति आदेश को बल देने के मेरे प्रयास को दूर करने की सटीक समस्या में भाग गया (भविष्यवाणी पुशडाउन इसे हराया)। OUTER APPLY पर स्विच करके यह ऑपरेशन के वास्तविक अर्थ को बदल दिया ताकि X.Account_Code में बाहरी क्वेरी के भीतर NULL मान हो, इस प्रकार उचित मूल्यांकन आदेश की आवश्यकता हो।

आप इस मूल्यांकन आदेश मुद्दे के बारे में Erland Sommarskog's Microsoft Connect request पढ़ने में रुचि रखते हैं। वह वास्तव में इसे एक बग कहते हैं।

यहां अतिरिक्त समस्याएं हैं लेकिन मैं अब उन्हें संबोधित नहीं कर सकता।

पीएस मैं आज एक brainstorm था। मैंने सुझाए गए "पारंपरिक तरीके" के लिए एक वैकल्पिक एक बाहरी संदर्भ के साथ SELECT अभिव्यक्ति है, जो SQL Server 2000 में भी काम करता है। (मैंने देखा है कि CROSS/OUTER APPLY सीखने के बाद से मैंने पुराने SQL सर्वर संस्करणों के साथ अपनी क्वेरी क्षमता में सुधार किया है, भी - जैसा कि मैंने SELECT, ON, और 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

क्या क्वेरी आपके लिए काम करती है? यह मेरे लिए तब तक नहीं है जब तक कि मैं 'बाहरी लागू 'पर' क्रॉस लागू 'नहीं बदलता। (एसक्यूएल सर्वर 2012) –

+0

मेरा मतलब था कि आपका वर्तमान संस्करण काम नहीं करता है। क्वेरी प्लान एक टेबल स्कैन करता है जो प्रत्येक पंक्ति को जांचता है। यदि आप बाहरी आवेदन में बदल जाते हैं तो यह रूपांतरण से पहले खराब पंक्तियों को फ़िल्टर करेगा। [एसक्यूएल फिडल] (http://sqlfiddle.com/#!6/664af/3/0) –

6

आप SQL Server 2012 चला रहे हैं कि आपको नई TRY_PARSE() फ़ंक्शन का उपयोग कर सकते हैं:

एक अभिव्यक्ति का परिणाम देता है, का अनुरोध किया डेटा प्रकार, या अशक्त कलाकारों एसक्यूएल सर्वर में विफल रहता है के लिए अनुवाद 2012. TRY_PARSE का उपयोग केवल स्ट्रिंग से दिनांक/समय और संख्या प्रकारों में कनवर्ट करने के लिए करें।

1

मुझे लगता है कि समस्या उप-क्वेरी में नहीं है लेकिन बाहरी क्वेरी के WHERE खंड में है। जब आप

WHERE account_code between 503100 and 503105 

एसक्यूएल सर्वर का उपयोग अपने ACCOUNT_CODE क्षेत्र में प्रत्येक मान परिवर्तित करने के लिए प्रदान की हालत में यह परीक्षण करने के लिए पूर्णांक की कोशिश करेंगे। स्पष्ट रूप से यह ऐसा करने में विफल रहेगा यदि कुछ पंक्तियों में गैर-पूर्णांक वर्ण होंगे।

0

धन्यवाद, इस बजाय

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

मैं खुश हूँ आप

संबंधित मुद्दे