2011-08-31 25 views
9

मुझे कुछ SQL सर्वर क्वेरीज़ में कोई समस्या है। यह पता चला है कि मेरे पास "Attibute_Name" और "Attibute_Value" फ़ील्ड वाली तालिका है, जो वर्चर में संग्रहीत किसी भी प्रकार का हो सकता है। (हाँ ... मुझे पता है।)डेटाटाइम में रूपांतरण केवल WHERE क्लॉज पर विफल रहता है?

किसी विशेष विशेषता के लिए सभी तिथियां "YYYY-MM-DD hh: mm: ss" प्रारूप को संग्रहीत करती हैं (इसके बारे में 100% निश्चित नहीं है, लाखों हैं यहां रिकॉर्ड) की, तो मैं समस्याओं के बिना इस कोड को निष्पादित कर सकते हैं:

select /*...*/ CONVERT(DATETIME, pa.Attribute_Value) 
from 
    ProductAttributes pa 
    inner join Attributes a on a.Attribute_ID = pa.Attribute_ID 
where 
    a.Attribute_Name = 'SomeDate' 

हालांकि, अगर मैं निम्नलिखित कोड निष्पादित करें:

select /*...*/ CONVERT(DATETIME, pa.Attribute_Value) 
from 
    ProductAttributes pa 
    inner join Attributes a on a.Attribute_ID = pa.Attribute_ID 
where 
    a.Attribute_Name = 'SomeDate' 
    and CONVERT(DATETIME, pa.Attribute_Value) < GETDATE() 

मैं निम्न त्रुटि प्राप्त होगा: जब परिवर्तित रूपांतरण विफल चरित्र स्ट्रिंग से दिनांक और/या समय।

यह कहां से खंड पर विफल रहता है और चयनकर्ता पर नहीं?

एक और सुराग:

बजाय अगर ATTRIBUTE_NAME मैं डेटाबेस (पी) में संग्रहीत वास्तविक Attribute_ID यह समस्या के बिना काम करेंगे का उपयोग के आधार पर फ़िल्टर।

select /*...*/ CONVERT(DATETIME, pa.Attribute_Value) 
from 
    ProductAttributes pa 
    inner join Attributes a on a.Attribute_ID = pa.Attribute_ID 
where 
    a.Attribute_ID = 15 
    and CONVERT(DATETIME, pa.Attribute_Value) < GETDATE() 

अद्यतन जवाब के लिए सभी को धन्यवाद। मुझे वास्तव में सही उत्तर चुनना मुश्किल लगता है क्योंकि हर किसी ने इस मुद्दे को समझने के लिए उपयोगी कुछ बताया। यह निश्चित रूप से निष्पादन के आदेश के साथ करना था। यह पता चला है कि मेरी पहली क्वेरी सही तरीके से काम करती है क्योंकि WHERE क्लॉज पहले निष्पादित किया गया था, फिर चयन करें। मेरी दूसरी क्वेरी उसी कारण से विफल रही (क्योंकि गुण फ़िल्टर नहीं किए गए थे, वही WHERE खंड निष्पादित करते समय रूपांतरण विफल हुआ)। मेरी तीसरी क्वेरी काम करती है क्योंकि आईडी इंडेक्स (पीके) का हिस्सा था, इसलिए इसे प्राथमिकता मिली और इससे पहले उस स्थिति पर परिणाम कम हो गए।

धन्यवाद!

+0

एक और विशेषता 'sOmeDaTe' कहा जाता है है - आप एक केस संवेदी मिलान का उपयोग कर रहे मामले में? शायद यह आपके जुड़ने में गड़बड़ कर रहा है। – YetAnotherUser

+1

आप 'WHERE' खंड में भविष्यवाणियों के किसी प्रकार का संक्षिप्त सर्किट मूल्यांकन या गारंटीकृत आदेश मानते हुए प्रतीत होते हैं। यह गारंटी नहीं है। जब आपके पास कॉलम में मिश्रित डेटाटाइप होते हैं जैसे कि उन्हें निपटने का एकमात्र सुरक्षित तरीका 'केस' अभिव्यक्ति के साथ होता है। –

+0

पीएचए कौन सी तालिका है? यदि पीएचए पीए की तुलना में एक अलग तालिका है तो ऐसा लगता है कि पीएचए के डेटा में कुछ अपरिवर्तनीय रिकॉर्ड हैं, जहां पीए की नहीं है। – N0Alias

उत्तर

2

यदि रूपांतरण WHERE क्लॉज में है तो इसका मूल्यांकन कई और रिकॉर्ड्स (मानों) के लिए किया जा सकता है, अगर यह प्रक्षेपण में दिखाई देता है सूची। मैंने अलग-अलग संदर्भ में इससे पहले इस बारे में बात की है, T-SQL functions do no imply a certain order of execution और On SQL Server boolean operator short-circuit देखें। आपका मामला भी आसान है, लेकिन समान है, और आखिरकार मूल कारण समान है: एसक्यूएल जैसी घोषणात्मक भाषा से निपटने के दौरान एक अनिवार्य निष्पादन आदेश न मानें।

आपके सबसे अच्छे समाधान, दूर और बड़े मार्जिन द्वारा, डेटा को स्वच्छ करने और कॉलम प्रकार को DATETIME या DATETIME2 प्रकार में बदलने के लिए है। सभी अन्य कामकाजों में एक कमी या अन्य होगा, इसलिए आप सही काम करने के लिए बेहतर हो सकते हैं।

अद्यतन

नज़दीक से देखने के बाद (माफ करना, मैं @VLDB हूँ और केवल सत्र के बीच इतना देखना) मुझे पता है आप निहित प्रकार से मुक्त अर्थ विज्ञान के साथ एक EAV दुकान (attribute_value स्ट्रिंग BEA कर सकते हैं, एक तारीख, एक int आदि)। मेरी राय यह है कि आपकी सबसे अच्छी शर्त भंडारण में sql_variant और क्लाइंट तक सभी तरह से उपयोग करना है (यानी प्रोजेक्ट sql_variant)। आप क्लाइंट में प्रकार को समेकित कर सकते हैं, सभी क्लाइंट एपीआई में sql_variant से आंतरिक प्रकार निकालने के तरीके हैं, Using sql_variant Data देखें (ठीक है, लगभग सभी क्लाइंट एपीआई ... Using the sql_variant datatype in CLR)। sql_variant के साथ आप एक स्ट्रिंग प्रस्तुतियों के माध्यम से जाने की समस्याओं के कई प्रकारों को स्टोर कर सकते हैं, आप संग्रहीत मूल्यों में BaseType जैसी चीजों का निरीक्षण करने के लिए SQL_VARIANT_PROPERTY का उपयोग कर सकते हैं, और आप डेटा प्रकार की शुद्धता को लागू करने के लिए चेक बाधाओं जैसे सोच भी सकते हैं।

+0

मैं 'SQL_VARIANT' * का उपयोग करने में बहुत संकोच करता हूं * जब तक आप क्लाइंट पर सभी प्रस्तुति, फ़िल्टरिंग और तुलना नहीं कर रहे हैं। हमारे ईएवी सिस्टम में हम प्रत्येक प्रकार के लिए समर्पित कॉलम के पक्ष में जल्दी से 'SQL_VARIANT' से दूर चले गए। ठीक है, तो आपके पास प्रत्येक पंक्ति में दो एनयूएलएल हैं, लेकिन आपको इसके साथ आने वाली सभी अन्य ग़लत चीजों से निपटने की ज़रूरत नहीं है। बस दोनों तरफ एक उचित हिला देने के लिए, मैंने यहां सीमाओं के बारे में थोड़ा सा ब्लॉग किया: http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/12/bad-habits-to-kick-using-the- गलत-डेटा-टाइप.aspx ... यदि कॉलम 'SQL_VARIANT' था तो क्या आप अपनी क्वेरी दिखा सकते हैं? –

+0

मैं आपका पॉइंट देखता हूं। Sql_variant ईएवी संरचना पर करना और एकत्र करना कास्ट मुद्दों से पीड़ित होगा, जबकि एक समर्पित कॉलम/प्रकार आसानी से मानों को जोड़ सकता है क्योंकि यह पता है कि वे सभी उस प्रकार के क्षेत्र में हैं, और इस प्रकार के लिए कोई CAST की आवश्यकता नहीं है। वैध आपत्ति –

0

मुझे डेटा समस्या की तरह लगता है। जब आप दो अलग-अलग तरीकों का उपयोग करके इसे चुनते हैं तो डेटा पर एक नज़र डालें, अलग-अलग लंबाई की तलाश करने का प्रयास करें और फिर विभिन्न सेटों में आइटम का चयन करें और उन्हें नजरअंदाज करें। नल के लिए भी जाँच करें? (मुझे यकीन नहीं है कि यदि आप शून्य से डेटाटाइम में कनवर्ट करने का प्रयास करते हैं तो क्या होता है)

+0

नल में एक डेटाटाइम परिणामों को शून्य में परिवर्तित करना। –

1

यह आदेश के साथ करना है कि SELECT क्वेरी संसाधित की गई है। WHERE खंड SELECT से पहले संसाधित किया जाता है। यह निर्धारित करना है कि कौन सी पंक्तियों को शामिल/बहिष्कृत करना है। नाम का उपयोग करने वाले खंड में एक स्कैन का उपयोग करना चाहिए जो सभी पंक्तियों की जांच करता है, जिनमें से कुछ में वैध दिनांक/समय डेटा नहीं होता है, जबकि कुंजी संभवतः एक खोज की ओर ले जाती है और इस बिंदु पर अवैध पंक्तियों में से कोई भी शामिल नहीं होता है। SELECT सूची में कनवर्ट अंतिम बार किया जाता है, और स्पष्ट रूप से इस समय तक यह अवैध पंक्तियों को परिवर्तित करने का प्रयास नहीं करेगा। चूंकि आप अन्य डेटा के साथ दिनांक/समय डेटा मिश्रण कर रहे हैं, इसलिए आप सही डेटा प्रकारों के साथ समर्पित कॉलम में दिनांक या संख्यात्मक डेटा संग्रहीत करने पर विचार कर सकते हैं। इस दौरान, आप निम्नलिखित तरीके से जांच को स्थगित कर सकते हैं:

SELECT /* ... */ 
FROM 
(
    SELECT /* ... */ 
    FROM ProductAttributes AS pa 
    INNER JOIN dbo.Attributes AS a 
    ON a.Attribute_ID = pa.Attribute_ID 
    WHERE a.Attribute_Name = 'SomeDate' 
    AND ISDATE (pa.Attribute_Value) = 1 
) AS z 
WHERE CONVERT(CHAR(8), AttributeValue, 112) < CONVERT(CHAR(8), GETDATE(), 112); 

लेकिन बेहतर जवाब शायद नाम यदि संभव हो तो के बजाय Attribute_ID कुंजी का उपयोग करने के लिए है।

+1

यह काम करने की गारंटी नहीं है। 'SELECT' सूची में गणना स्केलर का मूल्यांकन 'WHERE' फ़िल्टर से पहले किया जा सकता है। उदाहरण के लिए देखें [यह उत्तर] (http://stackoverflow.com/questions/5191701/tsql-divide-by-zero-encountered-despite-no-columns-containing-0/5203211#5203211) या [यह कनेक्ट आइटम] (http://connect.microsoft।कॉम/एसक्यूएल सर्वर/फीडबैक/विवरण/537419/एसक्यूएल-सर्वर-चाहिए-नहीं-raise-अनौपचारिक त्रुटियों) –

+0

नहीं, यह काम नहीं करेगा। आप धारणा बना रहे हैं कि घोषणा का क्रम (सबक्वायरी) मूल्यांकन के क्रम का अर्थ है http://rusanu.com/2011/08/10/t-sql-functions-do-no-imply-a-certain- ऑर्डर-ऑफ-निष्पादन/क्यूओ एक ऐसी योजना का चयन कर सकता है जो CONVERT * से पहले * विशेषता_नाम तुलना का मूल्यांकन करता है और रूपांतरण त्रुटि को ट्रिगर करता है। –

+0

नहीं, मुझे यह कहना चाहिए था, "आप स्थगित करने का प्रयास कर सकते हैं" ... बेहतर उत्तर डेटा को वर्चुअल कॉलम में सबकुछ भरने के बजाय सही डेटा प्रकार के समर्पित कॉलम में संग्रहीत करना है। –

7

आप WHERE खंड में भविष्यवाणियों के किसी प्रकार का संक्षिप्त सर्किट मूल्यांकन या गारंटीकृत आदेश मानते हैं। यह गारंटी नहीं है। जब आपके पास कॉलम में मिश्रित डेटाटाइप होते हैं जैसे कि उन्हें निपटने का एकमात्र सुरक्षित तरीका CASE अभिव्यक्ति के साथ होता है।

उपयोग (जैसे)

CONVERT(DATETIME, 
     CASE WHEN ISDATE(pa.Attribute_Value) = 1 THEN pa.Attribute_Value END) 

नहीं

CONVERT(DATETIME, pa.Attribute_Value) 
0

मुझे लगता है कि समस्या आप अपने डेटाबेस (जाहिर है) में एक बुरा तारीख है।

अपने पहले उदाहरण में, जहां आप WHERE खंड में दिनांक की जांच नहीं कर रहे हैं, तो सभी तिथियां जहां a.attribute.Name = 'SomeDate' मान्य हैं, इसलिए यह कभी भी खराब तिथि को बदलने का प्रयास नहीं करता है।

आपके दूसरे उदाहरण में, WHERE खंड के अतिरिक्त क्वेरी योजना वास्तव में उन सभी तिथियों को परिवर्तित करने और खराब खोजने और फिर विशेषता नाम को देखने का कारण बन रही है।

अपने तीसरे उदाहरण में, Attribute_Id का उपयोग करने के लिए बदलना संभवतः क्वेरी प्लान को बदलता है ताकि यह केवल उन लोगों को देख सके जहां आईडी = 15 पहले, और फिर यह जांचने के लिए जांच की जाती है कि क्या उनके रिकॉर्ड की वैध तिथि है, जो वे करते हैं। (शायद Attribute_Id अनुक्रमणित है और Attribute_name नहीं है)

तो, आप कहीं एक बुरा तारीख है, लेकिन यह Arttribute_id के साथ किसी भी रिकॉर्ड पर नहीं है = 15.

0

आप निष्पादन योजनाओं देख सकते हैं।यह हो सकता है कि पहली क्वेरी के साथ दूसरे मानदंड (CONVERT(DATETIME, pa.Attribute_Value) < GETDATE()) को पहले पंक्तियों (दिनांक नहीं) वाले सभी पंक्तियों पर पहले मूल्यांकन किया जाता है, जबकि दूसरे के मामले में - a.Attribute_ID = 15 पहले मूल्यांकन किया जाता है। इस प्रकार गैर-तारीख मानों वाली पंक्तियों को छोड़कर।

बीटीडब्ल्यू, दूसरा एक तेज़ भी हो सकता है, और यदि आपके पास चयनित सूची में Attributes से कुछ भी नहीं है, तो आप inner join Attributes a on a.Attribute_ID = pa.Attribute_ID से छुटकारा पा सकते हैं।

कि नोट पर, यह EAV से छुटकारा पाने के उचित होगा, जबकि यह कोई बहुत देर हो चुकी :)

+0

आप तालिका आंकड़ों को पुन: गणना करने का प्रयास कर सकते हैं। यदि 'ProductAttributes' में लाखों पंक्तियां हैं, तो' CONVERT (DATETIME, pa.Attribute_Value) का मूल्यांकन nad2000

+0

'विश्लेषण तालिका' SQL सर्वर के लिए सही नहीं है। –

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