Database
 sql >> डेटाबेस >  >> RDS >> Database

डेटाटाइम के बजाय दिनांक और समय से निपटना

डेटा पेशेवरों को हमेशा एक इष्टतम डिज़ाइन वाले डेटाबेस का उपयोग करने के लिए नहीं मिलता है। कभी-कभी जो चीजें आपको रुलाती हैं, वे ऐसी चीजें हैं जो हमने खुद से की हैं, क्योंकि वे उस समय अच्छे विचारों की तरह लग रहे थे। कभी-कभी वे तीसरे पक्ष के अनुप्रयोगों के कारण होते हैं। कभी-कभी वे बस आपको भविष्यवाणी करते हैं।

मैं इस पोस्ट के बारे में सोच रहा हूं जब आपका डेटाटाइम (या डेटाटाइम 2, या बेहतर अभी भी, डेटाटाइमऑफसेट) कॉलम वास्तव में दो कॉलम है - एक तिथि के लिए, और एक समय के लिए। (यदि आपके पास ऑफसेट के लिए फिर से एक अलग कॉलम है, तो अगली बार जब मैं आपको देखूंगा तो मैं आपको गले लगाऊंगा, क्योंकि आपको शायद हर तरह की चोट से जूझना पड़ा है।)

मैंने ट्विटर पर एक सर्वेक्षण किया, और पाया कि यह एक बहुत ही वास्तविक समस्या है कि आप में से लगभग आधे लोगों को समय-समय पर तारीख और समय का सामना करना पड़ता है।

एडवेंचरवर्क्स लगभग यही करता है - यदि आप Sales.SalesOrderHeader तालिका में देखते हैं, तो आपको ऑर्डरडेट नामक एक डेटाटाइम कॉलम दिखाई देगा, जिसमें हमेशा सटीक तिथियां होती हैं। मैं शर्त लगाता हूं कि यदि आप एडवेंचरवर्क्स में एक रिपोर्ट डेवलपर हैं, तो आपने शायद ऐसे प्रश्न लिखे हैं जो किसी विशेष दिन पर ऑर्डर की संख्या की तलाश करते हैं, ग्रुप बाय ऑर्डरडेट, या ऐसा कुछ। यहां तक ​​​​कि अगर आप जानते थे कि यह एक डेटाटाइम कॉलम था और इसके लिए गैर-मध्यरात्रि समय भी स्टोर करने की संभावना थी, तब भी आप इंडेक्स को सही तरीके से उपयोग करने के लिए ऑर्डरडेट द्वारा ग्रुप कहेंगे। GROUP BY CAST (आदेश दिनांक के रूप में दिनांक) बस इसे काटता नहीं है।

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

इसलिए मैं समझता हूं कि आपको अपने कॉलम को क्वेरी करने में खुशी क्यों होगी जैसे कि यह एक तारीख है, बस यह जानते हुए कि यदि उस कॉलम का उपयोग बदल जाता है तो आपको दर्द की दुनिया होगी। हो सकता है कि आप इसे टेबल पर बाधा डालकर हल करें। हो सकता है कि आपने अपना सिर रेत में डाल दिया हो।

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

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

लेकिन अक्सर, आप अपने आप को दिनांक और समय के साथ अलग-अलग कॉलम के रूप में पाएंगे, मूल रूप से आप इसके बारे में कुछ भी नहीं कर सकते हैं। आप एक परिकलित कॉलम नहीं जोड़ सकते, क्योंकि यह एक तृतीय पक्ष एप्लिकेशन है और आप नहीं जानते कि क्या टूट सकता है। क्या आप सुनिश्चित हैं कि वे कभी भी चयन * नहीं करते हैं? एक दिन मुझे आशा है कि वे हमें कॉलम जोड़ने और उन्हें छिपाने देंगे, लेकिन कुछ समय के लिए, आप निश्चित रूप से सामान तोड़ने का जोखिम उठाते हैं।

और, आप जानते हैं, एमएसडीबी भी ऐसा करता है। वे दोनों पूर्णांक हैं। और यह पिछड़ी संगतता के कारण है, मुझे लगता है। लेकिन मुझे संदेह है कि आप एमएसडीबी में एक तालिका में एक गणना कॉलम जोड़ने पर विचार कर रहे हैं।

तो हम यह कैसे पूछते हैं? आइए मान लें कि हम उन प्रविष्टियों को ढूंढना चाहते हैं जो किसी विशेष डेटाटाइम सीमा के भीतर थीं?

आइए कुछ प्रयोग करते हैं।

सबसे पहले, आइए 3 मिलियन पंक्तियों वाली एक तालिका बनाएं और उन स्तंभों को अनुक्रमित करें जिनकी हम परवाह करते हैं।

select identity(int,1,1) as ID, OrderDate, 
  dateadd(minute, abs(checksum(newid())) % (60 * 24), cast('00:00' as time)) as OrderTime
into dbo.Sales3M
from Sales.SalesOrderHeader
cross apply (select top 100 * from master..spt_values) v;
 
create index ixDateTime on dbo.Sales3M (OrderDate, OrderTime) include (ID);

(मैं इसे एक संकुल सूचकांक बना सकता था, लेकिन मुझे लगता है कि एक गैर-संकुल सूचकांक आपके पर्यावरण के लिए अधिक विशिष्ट है।)

हमारा डेटा इस तरह दिखता है, और मैं 2 अगस्त 2011 को 8:30 बजे और 5 अगस्त 2011 को 21:30 बजे के बीच की पंक्तियों को खोजना चाहता हूं।

डेटा को देखकर, मैं देख सकता हूं कि मुझे 48221 और 50171 के बीच सभी पंक्तियां चाहिए। वह 50171-48221+1=1951 पंक्तियां हैं (+1 इसलिए है क्योंकि यह एक समावेशी श्रेणी है)। यह मुझे आश्वस्त होने में मदद करता है कि मेरे परिणाम सही हैं। आप शायद अपनी मशीन पर समान होंगे, लेकिन सटीक नहीं, क्योंकि मैंने अपनी तालिका बनाते समय यादृच्छिक मानों का उपयोग किया था।

मुझे पता है कि मैं ऐसा कुछ नहीं कर सकता:

select * 
from dbo.Sales3M 
where OrderDate between '20110802' and '20110805'
and OrderTime between '8:30' and '21:30';

... क्योंकि इसमें कुछ ऐसा शामिल नहीं होगा जो 4 तारीख को रातों-रात हुआ हो। यह मुझे 1268 पंक्तियाँ देता है - स्पष्ट रूप से सही नहीं है।

कॉलम को संयोजित करने का एक विकल्प है:

select * 
from dbo.Sales3M 
where dateadd(day,datediff(day,0,OrderDate),cast(OrderTime as datetime2)) 
  between '20110802 8:30' and '20110805 21:30';

इससे सही परिणाम मिलते हैं। ऐसा होता है। यह सिर्फ इतना है कि यह पूरी तरह से गैर-प्रबंधनीय है, और हमें हमारी तालिका में सभी पंक्तियों में एक स्कैन देता है। हमारी 3 मिलियन पंक्तियों पर इसे चलाने में कुछ सेकंड लग सकते हैं।

हमारी समस्या यह है कि हमारे पास एक साधारण मामला है, और दो विशेष मामले हैं। हम जानते हैं कि ऑर्डरडेट> '20110802' और ऑर्डरडेट <'20110805' को संतुष्ट करने वाली हर पंक्ति वह है जिसे हम चाहते हैं। लेकिन हमें हर पंक्ति की भी आवश्यकता है जो 20110802 को 8:30 या उसके बाद और 20110805 को 21:30 बजे या उससे पहले है। और यह हमें इस ओर ले जाता है:

select * 
from dbo.Sales3M 
where (OrderDate > '20110802' and OrderDate < '20110805')
or (OrderDate = '20110802' and OrderTime >= '8:30')
or (OrderDate = '20110805' and OrderTime <= '21:30');

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

हम इस पर UNION ALL का उपयोग कर सकते हैं, जिसका अर्थ होगा कि QO इस बात की परवाह नहीं करेगा कि क्या शर्तें परस्पर अनन्य हैं। इससे हमें तीन सीक्स मिलते हैं जो एक-दूसरे से जुड़े हुए हैं - यह बहुत अच्छा है।

select * 
from dbo.Sales3M 
where (OrderDate > '20110802' and OrderDate < '20110805')
union all 
select * 
from dbo.Sales3M 
where (OrderDate = '20110802' and OrderTime >= '8:30')
union all 
select * 
from dbo.Sales3M 
where (OrderDate = '20110805' and OrderTime <= '21:30');

लेकिन यह अभी भी तीन चाहता है। सांख्यिकी IO मुझे बताता है कि यह मेरी मशीन पर 20 पढ़ता है।

अब, जब मैं सरलता के बारे में सोचता हूं, तो मैं केवल इंडेक्स कॉलम को एक्सप्रेशन के अंदर डालने से बचने के बारे में नहीं सोचता, मैं यह भी सोचता हूं कि क्या कुछ मदद कर सकता है लगता है सारगर्भित

उदाहरण के लिए 'सुदूर%' की तरह अंतिम नाम कहां लें। जब मैं इसके लिए योजना को देखता हूं, तो मैं देखता हूं कि एक सीक, सीक प्रेडिकेट के साथ दूर से (लेकिन शामिल नहीं) एफएएस तक किसी भी नाम की तलाश कर रहा है। और फिर LIKE स्थिति की जाँच करने वाला एक अवशिष्ट विधेय है। ऐसा इसलिए नहीं है क्योंकि QO मानता है कि LIKE सारगर्भित है। यदि ऐसा होता, तो यह सीक प्रेडिकेट में LIKE का उपयोग करने में सक्षम होता। ऐसा इसलिए है क्योंकि यह जानता है कि उस LIKE शर्त से संतुष्ट होने वाली हर चीज उस सीमा के भीतर होनी चाहिए।

जहां कास्ट करें (तारीख के अनुसार ऑर्डर की तारीख) ='20110805'

यहां हम एक सीक प्रेडिकेट देखते हैं जो दो मानों के बीच ऑर्डरडेट मानों की तलाश करता है जिन्हें योजना में कहीं और काम किया गया है, लेकिन एक ऐसी सीमा बनाते हैं जिसमें सही मान मौजूद होना चाहिए। यह नहीं है>=20110805 00:00 और <20110806 00:00 (जो मैंने इसे बनाया होगा), यह कुछ और है। इस श्रेणी के प्रारंभ का मान 20110805 00:00 से छोटा होना चाहिए, क्योंकि यह>, नहीं>=है। हम वास्तव में केवल इतना कह सकते हैं कि जब Microsoft के भीतर किसी ने लागू किया कि QO को इस तरह के विधेय पर कैसे प्रतिक्रिया देनी चाहिए, तो उन्होंने इसे "सहायक विधेय" कहने के लिए पर्याप्त जानकारी दी।

अब, मैं माइक्रोसॉफ्ट को अधिक कार्यों को सुगम बनाने के लिए पसंद करूंगा, लेकिन उस विशेष अनुरोध को उनके सेवानिवृत्त होने से बहुत पहले बंद कर दिया गया था।

लेकिन शायद मेरा मतलब यह है कि उनके लिए और अधिक सहायक भविष्यवाणी करना है।

सहायक विधेय के साथ समस्या यह है कि वे लगभग निश्चित रूप से आपकी इच्छा से अधिक पंक्तियों को पढ़ते हैं। लेकिन यह अभी भी पूरे सूचकांक को देखने से बेहतर है।

मुझे पता है कि जिन सभी पंक्तियों को मैं वापस करना चाहता हूं, उनमें 20110802 और 20110805 के बीच ऑर्डरडेट होगा। यह सिर्फ इतना है कि कुछ ऐसे हैं जो मुझे नहीं चाहिए।

मैं बस उन्हें हटा सकता था, और यह मान्य होगा:

select *
from dbo.Sales3M
where OrderDate between '20110802' and '20110805'
and not (OrderDate = '20110802' and OrderTime < '8:30')
and not (OrderDate = '20110805' and OrderTime > '21:30');

लेकिन मुझे लगता है कि यह एक ऐसा समाधान है जिसके बारे में सोचने के लिए कुछ प्रयास करने की आवश्यकता है। डेवलपर की ओर से कम प्रयास केवल हमारे सही-लेकिन-धीमे संस्करण के लिए एक सहायक विधेय प्रदान करना है।

select * 
from dbo.Sales3M 
where dateadd(day,datediff(day,0,OrderDate),cast(OrderTime as datetime2)) 
between '20110802 8:30' and '20110805 21:30'
and OrderDate between '20110802' and '20110805';

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

कुछ मायनों में, इस अंतिम उदाहरण के साथ सबसे बड़ी समस्या यह है कि कोई नेक अर्थ वाला व्यक्ति यह देखेगा कि सहायक विधेय निरर्थक था और वह इसे हटा सकता है। सभी सहायक विधेय के साथ यही स्थिति है। तो एक टिप्पणी डालें।

select * 
from dbo.Sales3M 
where dateadd(day,datediff(day,0,OrderDate),cast(OrderTime as datetime2)) 
  between '20110802 8:30' and '20110805 21:30'
/* This next predicate is just a helper to improve performance */
and OrderDate between '20110802' and '20110805';

यदि आपके पास कुछ ऐसा है जो एक अच्छे सारगर्भित विधेय में फिट नहीं होता है, तो उस पर काम करें, और फिर पता करें कि आपको इससे बाहर करने की क्या आवश्यकता है। आप शायद एक बेहतर समाधान के साथ आ सकते हैं।

@rob_farley


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. वह क्रम जो 15,000 के स्तर तक फैलता है

  2. अपाचे स्पार्क ओडीबीसी चालक

  3. एसक्यूएल जहां एकाधिक शर्तें

  4. डिजिटल परिवर्तन:यह सब डेटा सोच से शुरू होता है

  5. SQL में वर्णानुक्रम में ऑर्डर कैसे करें