SQL सर्वर का DateTime
डोमेन है 1753-01-01 00:00:00.000 ≤ x ≤ 9999-12-31 23:59:59.97। वर्ष 210 सीई उस डोमेन से बाहर है। इसलिए समस्या।
यदि आप SQL Server 2008 या बाद के संस्करण का उपयोग कर रहे थे, तो आप इसे DateTime2
. पर डाल सकते हैं डेटाटाइप और आप सुनहरे होंगे (इसका डोमेन 0001-01-01 00:00:00.0000000 &le x है 9999-12-31 23:59:59.99999999। लेकिन SQL सर्वर 2005 के साथ, आप काफी हद तक SOL हैं।
यह वास्तव में डेटा सफाई की समस्या है। इस तरह के मामलों में मेरा झुकाव तीसरे पक्ष के डेटा को एक स्टेजिंग टेबल में प्रत्येक फ़ील्ड के साथ चरित्र स्ट्रिंग के रूप में लोड करना है। फिर डेटा को जगह में साफ करें, उदाहरण के लिए, NULL के साथ अमान्य तिथियों की जगह। एक बार साफ हो जाने के बाद, इसे अपने अंतिम गंतव्य तक ले जाने के लिए आवश्यक रूपांतरण कार्य करें।
दूसरा तरीका यह है कि पैटर्न मिलान का उपयोग करें और datetime
. में कुछ भी परिवर्तित किए बिना दिनांक फ़िल्टरिंग करें . आईएसओ 8601 दिनांक/समय मान वर्ण स्ट्रिंग हैं जिनमें (ए) मानव-पठनीय और (बी) ठीक से मिलान और तुलना करने की प्रशंसनीय संपत्ति है।
मैंने अतीत में जो कुछ किया है, वह कुछ विश्लेषणात्मक कार्य है जो दशमलव अंकों को 'd' से बदलकर और फिर group by
चलाकर डेटाटाइम फ़ील्ड में सभी पैटर्न की पहचान करने के लिए है। पाए गए प्रत्येक अलग पैटर्न की गणना करने के लिए। एक बार आपके पास यह हो जाने के बाद आप मार्गदर्शन करने के लिए कुछ पैटर्न टेबल बना सकते हैं। कुछ इस तरह:
create table #datePattern
(
pattern varchar(64) not null primary key clustered ,
monPos int not null ,
monLen int not null ,
dayPos int not null ,
dayLen int not null ,
yearPos int not null ,
yearLen int not null ,
)
insert #datePattern values ( '[0-9]/[0-9]/[0-9] %' ,1,1,3,1,5,1)
insert #datePattern values ( '[0-9]/[0-9]/[0-9][0-9] %' ,1,1,3,1,5,2)
insert #datePattern values ( '[0-9]/[0-9]/[0-9][0-9][0-9] %' ,1,1,3,1,5,3)
insert #datePattern values ( '[0-9]/[0-9]/[0-9][0-9][0-9][0-9] %' ,1,1,3,1,5,4)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9] %' ,1,1,3,2,6,1)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9][0-9] %' ,1,1,3,2,6,2)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9][0-9][0-9] %' ,1,1,3,2,6,3)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9] %' ,1,1,3,2,6,4)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9] %' ,1,2,4,1,6,1)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9][0-9] %' ,1,2,4,1,6,2)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9][0-9][0-9] %' ,1,2,4,1,6,3)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9][0-9][0-9][0-9] %' ,1,2,4,1,6,4)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9] %' ,1,2,4,2,7,1)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9][0-9] %' ,1,2,4,2,7,2)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9] %' ,1,2,4,2,7,3)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9] %' ,1,2,4,2,7,4)
create table #timePattern
(
pattern varchar(64) not null primary key clustered ,
hhPos int not null ,
hhLen int not null ,
mmPos int not null ,
mmLen int not null ,
ssPos int not null ,
ssLen int not null ,
)
insert #timePattern values ( '[0-9]:[0-9]:[0-9]' ,1,1,3,1,5,1 )
insert #timePattern values ( '[0-9]:[0-9]:[0-9][0-9]' ,1,1,3,1,5,2 )
insert #timePattern values ( '[0-9]:[0-9][0-9]:[0-9]' ,1,1,3,2,6,1 )
insert #timePattern values ( '[0-9]:[0-9][0-9]:[0-9][0-9]' ,1,1,3,2,6,2 )
insert #timePattern values ( '[0-9][0-9]:[0-9]:[0-9]' ,1,2,4,1,6,1 )
insert #timePattern values ( '[0-9][0-9]:[0-9]:[0-9][0-9]' ,1,2,4,1,6,2 )
insert #timePattern values ( '[0-9][0-9]:[0-9][0-9]:[0-9]' ,1,2,4,2,7,1 )
insert #timePattern values ( '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' ,1,2,4,2,7,2 )
आप इन दो तालिकाओं को 1 में जोड़ सकते हैं लेकिन संयोजनों की संख्या चीजों को विस्फोट कर देती है, हालांकि यह तब क्वेरी को बहुत सरल बनाती है।
एक बार आपके पास यह हो जाने के बाद, क्वेरी [काफी] आसान है, यह देखते हुए कि SQL स्ट्रिंग प्रोसेसिंग के लिए दुनिया की सबसे अच्छी भाषा पसंद नहीं है:
---------------------------------------------------------------------
-- first, get your lower bound in ISO 8601 format yyyy-mm-dd hh:mm:ss
-- This will compare/collate properly
---------------------------------------------------------------------
declare @dtLowerBound varchar(255)
set @dtLowerBound = convert(varchar,dateadd(year,-1,current_timestamp),121)
-----------------------------------------------------------------
-- select rows with a start date more recent than the lower bound
-----------------------------------------------------------------
select isoDate = + right( '0000' + substring( t.startDate , coalesce(dt.yearPos,1) , coalesce(dt.YearLen,0) ) , 4 )
+ '-' + right( '00' + substring( t.startDate , coalesce(dt.monPos,1) , coalesce(dt.MonLen,0) ) , 2 )
+ '-' + right( '00' + substring( t.startDate , coalesce(dt.dayPos,1) , coalesce(dt.dayLen,0) ) , 2 )
+ case
when tm.pattern is not null then
' ' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.hhPos , tm.hhLen ) , 2 )
+ ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.mmPos , tm.mmLen ) , 2 )
+ ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.ssPos , tm.ssLen ) , 2 )
else ''
end
,*
from someTableWithBadData t
left join #datePattern dt on t.startDate like dt.pattern
left join #timePattern tm on ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) )
like tm.pattern
where @lowBound <= + right( '0000' + substring( t.startDate , coalesce(dt.yearPos,1) , coalesce(dt.YearLen,0) ) , 4 )
+ '-' + right( '00' + substring( t.startDate , coalesce(dt.monPos,1) , coalesce(dt.MonLen,0) ) , 2 )
+ '-' + right( '00' + substring( t.startDate , coalesce(dt.dayPos,1) , coalesce(dt.dayLen,0) ) , 2 )
+ case
when tm.pattern is not null then
' ' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.hhPos , tm.hhLen ) , 2 )
+ ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.mmPos , tm.mmLen ) , 2 )
+ ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.ssPos , tm.ssLen ) , 2 )
else ''
end
जैसा मैंने कहा, SQL स्ट्रिंग्स को मंगने के लिए सबसे अच्छा विकल्प नहीं है।
यह आपको मिलना चाहिए ... 90% वहाँ। अनुभव मुझे बताता है कि आपको अभी भी अधिक खराब तिथियां मिलेंगी:महीने 1 से कम या 12 से अधिक, 1 से कम या 31 से अधिक दिन, या उस महीने के लिए सीमा से बाहर के दिन (कंप्यूटर को बेहतर बनाने के लिए 31 फरवरी जैसा कुछ नहीं) , आदि। विशेष रूप से पुराने कोबोल प्रोग्राम, लापता डेटा को इंगित करने के लिए सभी 9s के क्षेत्र का उपयोग करना पसंद करते थे, उदाहरण के लिए (हालांकि यह एक आसान मामला है)।
मेरी पसंदीदा तकनीक डेटा को साफ़ करने के लिए एक पर्ल स्क्रिप्ट लिखना है और इसे पर्ल की बीसीपी सुविधाओं का उपयोग करके SQL सर्वर में थोक लोड करना है। ठीक इसी तरह की समस्या स्पेस पर्ल के लिए डिज़ाइन की गई है।