डेटाइम मान (SQL सर्वर) के समय भाग को कैसे निकालें?


84

यहाँ मेरा उपयोग है:

SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)

मैं सोच रहा हूं कि एक बेहतर और शानदार तरीका हो सकता है।

आवश्यकताएँ:

  • इसे जितना जल्दी हो सके (कम कास्टिंग, बेहतर) होना चाहिए।
  • अंतिम परिणाम एक datetimeप्रकार होना है , न कि एक स्ट्रिंग।

जवाबों:


116

SQL Server 2008 और ऊपर

SQL Server 2008 और ऊपर में, निश्चित रूप से सबसे तेज़ तरीका है Convert(date, @date)। यह एक करने के लिए वापस डाली जा सकती है, datetimeया datetime2यदि आवश्यक हो।

SQL Server 2005 और पुराने में वास्तव में सबसे अच्छा क्या है?

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

फ्लोट संभाषण सटीक नहीं हैं

सबसे पहले, मैं परिवर्तित datetimeकरने से दूर रहूंगा float, क्योंकि यह सही तरीके से परिवर्तित नहीं होता है। हो सकता है कि आप समय निकालने वाली चीज़ को सही तरीके से कर पाएं, लेकिन मुझे लगता है कि इसका उपयोग करना एक बुरा विचार है, क्योंकि यह डेवलपर्स को स्पष्ट रूप से सूचित करता है कि यह एक सुरक्षित संचालन है और यह नहीं है । जरा देखो तो:

declare @d datetime;
set @d = '2010-09-12 00:00:00.003';
select Convert(datetime, Convert(float, @d));
-- result: 2010-09-12 00:00:00.000 -- oops

यह कुछ ऐसा नहीं है जो हमें अपने कोड में या अपने उदाहरणों में लोगों को ऑनलाइन सिखाना चाहिए।

इसके अलावा, यह सबसे तेज़ तरीका भी नहीं है!

प्रमाण - प्रदर्शन परीक्षण

यदि आप कुछ परीक्षण स्वयं करना चाहते हैं, तो यह देखने के लिए कि विभिन्न विधियाँ वास्तव में कैसे स्टैक करती हैं, तो आपको टेस्टर को चलाने के लिए इस सेटअप स्क्रिप्ट की आवश्यकता होगी:

create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED);
declare @d datetime;
set @d = DateDiff(Day, 0, GetDate());
insert AllDay select @d;
while @@ROWCOUNT != 0
   insert AllDay
   select * from (
      select Tm =
         DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm)
      from AllDay
   ) X
   where Tm < DateAdd(Day, 1, @d);
exec sp_spaceused AllDay;  -- 25,920,000 rows

कृपया ध्यान दें कि यह आपके डेटाबेस में 427.57 एमबी तालिका बनाता है और इसे चलाने के लिए 15-30 मिनट की तरह कुछ लगेगा। यदि आपका डेटाबेस छोटा है और 10% की वृद्धि के लिए सेट है तो आपको पहले बड़े आकार की तुलना में अधिक समय लगेगा।

अब वास्तविक प्रदर्शन परीक्षण स्क्रिप्ट के लिए। कृपया ध्यान दें कि ग्राहक को पंक्तियों को वापस नहीं करना उद्देश्यपूर्ण है क्योंकि यह 26 मिलियन पंक्तियों पर महंगा है और विधियों के बीच प्रदर्शन के अंतर को छिपाएगा।

प्रदर्शन के परिणाम

set statistics time on;
-- (All queries are the same on io: logical reads 54712)
GO
declare
    @dd date,
    @d datetime,
    @di int,
    @df float,
    @dv varchar(10);

-- Round trip back to datetime
select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms,  elapsed time = 22301 ms.
select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms.
select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms.
select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms.
select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms.
select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms,  elapsed = 108236 ms.
select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms.

-- Only to another type but not back
select @dd = Tm from AllDay; -- CPU time = 19891 ms,  elapsed time = 20937 ms.
select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms.
select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms
select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms.
select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms.
select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms,  elapsed = 67987 ms.
select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms.
GO
set statistics time off;

कुछ जुआ विश्लेषण

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

select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)

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

अधिक परीक्षा से पता चलता है कि तुलना में Convert(, 112), Convert(, 101)क्वेरी में कुछ अतिरिक्त सीपीयू व्यय होता है (क्योंकि यह लंबे समय तक उपयोग करता है varchar?), क्योंकि दूसरा रूपांतरण वापस dateप्रारंभिक रूपांतरण में उतना खर्च नहीं करता है varchar, लेकिन इसके साथ Convert(, 112)यह 20000 के करीब है। एमएस सीपीयू आधार लागत।

यहाँ सीपीयू समय पर उन गणनाओं को बताया गया है जो मैंने उपरोक्त विश्लेषण के लिए उपयोग की हैं:

     method   round  single   base
-----------  ------  ------  -----
       date   21324   19891  18458
        int   23031   21453  19875
   datediff   23782   23218  22654
      float   36891   29312  21733
varchar-112  102984   64016  25048
varchar-101  123375   65609   7843
  • दौर एक दौर यात्रा के लिए सीपीयू समय है datetime

  • एकल , वैकल्पिक डेटा प्रकार के लिए एक रूपांतरण के लिए सीपीयू समय है (वह जो समय भाग को हटाने का दुष्प्रभाव है)।

  • आधारsingle दो इनवोकेशन के बीच के अंतर से घटाव की गणना है single - (round - single):। यह एक बॉलपार्क आंकड़ा है जो उस डेटा प्रकार से और उसके लिए रूपांतरण को मानता है और datetimeदोनों दिशाओं में लगभग समान है। ऐसा प्रतीत होता है कि यह धारणा सही नहीं है, लेकिन करीब है क्योंकि मूल्य केवल एक अपवाद के साथ 20000 एमएस के करीब हैं।

एक और दिलचस्प बात यह है कि आधार लागत लगभग एकल Convert(date)विधि के बराबर है (जिसमें लगभग 0 लागत होनी चाहिए, क्योंकि सर्वर आंतरिक रूप से पूर्णांक दिन भाग को datetimeडेटा प्रकार के पहले चार बाइट्स से बाहर निकाल सकता है )।

निष्कर्ष

तो यह कैसा दिखता है, एकल-दिशा varcharरूपांतरण विधि में लगभग 1.8 μs और एकल-दिशा DateDiffविधि में लगभग 0.18 μs लगते हैं। मैं 254520,000 पंक्तियों के लिए 18458 एमएस कुल के मेरे परीक्षण में सबसे अधिक रूढ़िवादी "बेस सीपीयू" समय पर यह आधार कर रहा हूं, इसलिए 23218 एमएस / 25920000 = 0.18 μs। स्पष्ट 10x सुधार बहुत कुछ लगता है, लेकिन यह स्पष्ट रूप से बहुत छोटा है जब तक आप सैकड़ों हजारों पंक्तियों (617k पंक्तियों = 1 सेकंड की बचत) के साथ काम कर रहे हैं।

यहां तक ​​कि इस छोटे से पूर्ण सुधार को देखते हुए, मेरी राय में, DateAddविधि जीतती है क्योंकि यह प्रदर्शन और स्पष्टता का सबसे अच्छा संयोजन है। जवाब है कि एक "जादू नंबर" की आवश्यकता होती है 0.50000004किसी दिन (पांच शून्य या छह ???) किसी को काटने के लिए जा रहा है, साथ ही इसे समझना मुश्किल है।

अतिरिक्त नोट्स

जब मैं कुछ समय मैं परिवर्तन करने जा रहा हूँ मिल 0.50000004करने के लिए '12:00:00.003'और देखें कि यह कैसे करता है। यह उसी datetimeमूल्य में परिवर्तित हो जाता है और मुझे याद रखना बहुत आसान लगता है।

रुचि रखने वालों के लिए, उपरोक्त परीक्षण एक सर्वर पर चलाए गए थे, जहां @@ संस्करण निम्नलिखित देता है:

Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) Jul 9 2008 14:43:34 कॉपीराइट (c) 1988-2008 Microsoft NT मानक संस्करण Windows NT 5.2 पर (बिल्ड 3790: सर्विस पैक 2)


1
+1 SQL सर्वर के किस संस्करण का आपने इस तरीके से परीक्षण किया?
मार्टिन स्मिथ

1
ऐसा लगता है कि आप अपनी मेज में पीछे की ओर सिंगल और गोल हैं। इसके अलावा, यदि आप charइसके बजाय उपयोग करते हैं तो क्या समय में कोई अंतर है varchar?
गाबे

1
@Gabe धन्यवाद, निश्चित। चार बिल्कुल वैरचर के समान प्रतीत होता है।
एरिके सेप

Oracle में है select round(sysdate) from dualऔर हमें Sql Server में निश्चित रूप से इसकी आवश्यकता है।
डेनिस वलेव

3
@Roman यदि आप SQL Server 2008 और ऊपर के साथ काम कर रहे हैं, तो हाँ, dateडेटा प्रकार में परिवर्तित करना सबसे तेज़ है, जैसा कि मेरे परीक्षणों में दिखाया गया है।
एरिक

30

SQL Server 2008 में एक नई तिथि डेटा प्रकार है और यह इस समस्या को सरल करता है:

SELECT CAST(CAST(GETDATE() AS date) AS datetime)

1
मैंने वर्ष 2018 के बजाय गलती से 0218 दर्ज कर लिया था और DATEADD(DATEDIFF())समय भाग को काटने की विधि एक अपवाद को फेंक देती है। जब मैं परिणाम वापस datetime2अपनी विधि के लिए अच्छी तरह से काम करता हैselect cast(CAST(convert(datetime2(0), '0218-09-12', 120) AS date) as datetime2)
बर्नहार्ड डब्लर

18

DATETIME गणना में इट्ज़िक बेन-गण , भाग 1 (एसक्यूएल सर्वर मैगज़ीन, फरवरी 2007) इस तरह के रूपांतरण को करने के तीन तरीके दिखाता है ( सबसे तेज़ सबसे तेज़ , दूसरी और तीसरी विधि के बीच का अंतर छोटा है):

SELECT CAST(CONVERT(char(8), GETDATE(), 112) AS datetime)

SELECT DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0)

SELECT CAST(CAST(GETDATE() - 0.50000004 AS int) AS datetime)

पत्रिका के अप्रैल अंक में एक पाठक द्वारा आपकी तकनीक ( फ्लोटिंग के लिए कास्टिंग ) का सुझाव दिया गया है। उनके अनुसार, इसमें ऊपर प्रस्तुत दूसरी तकनीक की तुलना में प्रदर्शन है।


1
मेरी राय में तैरने के लिए कास्टिंग करना सबसे अच्छा नहीं है। कृपया मेरा जवाब देखें
ErikE

1
@Emtucifor मैं मानता हूं कि 0.50000004 मूल्य के कारण 3 विधि बहुत अस्पष्ट है , लेकिन यह सबसे तेज़ है और आपके परीक्षण इसकी पुष्टि करते हैं । इस प्रकार, यह यथासंभव आवश्यकता के रूप में उपवास को संतुष्ट करता है
मारेक ग्रेजेनकोविज़

1
@Emtucifor इसके अलावा, यहाँ मैंने जो लेख जोड़ा है वह 0.50000004 मूल्य के बारे में कहता है : हालाँकि यह अभिव्यक्ति छोटी है (और कुशल है, जैसा कि मैं जल्द ही प्रदर्शित करूँगा), मेरा कहना है कि मुझे इससे असहजता महसूस हो रही है । मुझे यकीन नहीं है कि मैं अपनी उंगली ठीक-ठीक क्यों रख सकता हूं — शायद इसलिए कि यह बहुत तकनीकी है, और आप इसमें डेटाइम-संबंधी तर्क नहीं देख सकते।
मारेक ग्रेजेनकोविज़

2
यदि हम इस पद्धति का उपयोग करने जा रहे हैं, तो मैं SELECT CAST(CAST(GETDATE() - '12:00:00.003' AS int) AS datetime)इसके बजाय पसंद करूंगा , क्योंकि यह मेरे लिए कुछ है और याद रखने में बहुत आसान है।
एरिके सेप

6
यह अब SQL 2008 में सबसे तेज़ है Convert(date, GetDate()):।
एरिक

12

आपका CAST- FLOOR- CASTपहले से ही इष्टतम तरीका लगता है, कम से कम एमएस SQL ​​सर्वर 2005 पर।

कुछ अन्य समाधान जो मैंने देखे हैं उनमें एक स्ट्रिंग-रूपांतरण है, जैसे Select Convert(varchar(11), getdate(),101)उनमें, जो 10 के कारक से धीमा है।


1
हम अपने एक उत्पाद में माइकल स्टम द्वारा सुझाई गई विधि का उपयोग करते हैं और यह एक आकर्षण की तरह काम करता है।
क्रिस रॉबर्ट्स

3
यह काफी हद तक इष्टतम तरीका नहीं है। कृपया इस पृष्ठ पर मेरा उत्तर देखें ।
एरिके सेप


1

SQL2005: मैं dateadd के बजाय कास्ट की सलाह देता हूं। उदाहरण के लिए,

select cast(DATEDIFF(DAY, 0, datetimefield) as datetime)

मेरे डेटासेट पर, की तुलना में लगभग 10% तेजी से औसतन

select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0)

(और smalldatetime में कास्टिंग अभी भी तेज थी)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.