इस लेख में मैं देखता हूं कि कैसे डेटाटाइमऑफ़सेट डेटा प्रकार SQL सर्वर में संग्रहीत किया जाता है, और आप इसके साथ क्या कर रहे हैं, इसके आधार पर आप विभिन्न रिपोर्ट किए गए संग्रहण आकार के परिणाम कैसे प्राप्त कर सकते हैं।
यह वैसा ही है जैसा मैंने datetime2 . के साथ किया था डेटा प्रकार।
विशेष रूप से, मैं निम्नलिखित को देखता हूं:
- Microsoft के दस्तावेज़
- एक चर में संग्रहीत डेटा
- बाइट्स में लंबाई
DATALENGTH()
का उपयोग कर रही है - बाइट्स में लंबाई
DATALENGTH()
का उपयोग कर रही है varbinary . में बदलने के बाद
- बाइट्स में लंबाई
- डेटाबेस में संग्रहीत डेटा
- बाइट्स में लंबाई
COL_LENGTH()
का उपयोग कर रही है - बाइट्स में लंबाई
DBCC PAGE()
का उपयोग कर रही है
- बाइट्स में लंबाई
Microsoft का दस्तावेज़ीकरण
डेटाटाइमऑफ़सेट . पर Microsoft का आधिकारिक दस्तावेज़ीकरण डेटा प्रकार इंगित करता है कि उपयोग की जा रही सटीकता के आधार पर इसका भंडारण आकार 8 और 10 बाइट्स के बीच है।
datetime2(n) . के समान , आप डेटाटाइमऑफ़सेट(n) . का उपयोग कर सकते हैं सटीकता निर्दिष्ट करने के लिए, जहां n 0 और 7 के बीच का पैमाना है।
यहाँ वह डेटा है जो Microsoft इस डेटा प्रकार के लिए प्रस्तुत करता है:
निर्दिष्ट पैमाना | परिणाम (सटीक, पैमाना) | स्तंभ की लंबाई (बाइट्स) | आंशिक सेकंड की सटीकता |
---|---|---|---|
डेटाटाइम ऑफ़सेट | (34,7) | 10 | 7 |
डेटाटाइमऑफ़सेट(0) | (26,0) | 8 | 0-2 |
डेटाटाइमऑफ़सेट(1) | (28,1) | 8 | 0-2 |
डेटाटाइमऑफ़सेट(2) | (29,2) | 8 | 0-2 |
डेटाटाइमऑफ़सेट(3) | (30,3) | 9 | 3-4 |
डेटाटाइमऑफ़सेट(4) | (31,4) | 9 | 3-4 |
डेटाटाइमऑफ़सेट(5) | (32,5) | 10 | 5-7 |
डेटाटाइमऑफ़सेट(6) | (33,6) | 10 | 5-7 |
डेटाटाइमऑफ़सेट(7) | (34,7) | 10 | 5-7 |
इस लेख के प्रयोजनों के लिए, मुझे मुख्य रूप से स्तंभ लंबाई (बाइट्स) . में दिलचस्पी है कॉलम। यह हमें बताता है कि इस डेटा प्रकार को डेटाबेस में संग्रहीत करने के लिए कितने बाइट्स का उपयोग किया जाता है।
मुख्य कारण मैं इस लेख को लिखना चाहता था (और नीचे प्रयोग चलाएँ), यह है कि Microsoft दस्तावेज़ यह नहीं समझाता है कि एक अतिरिक्त बाइट का उपयोग सटीकता के लिए किया जाता है (जैसा कि इसके दस्तावेज़ में datetime2 के लिए होता है। मजबूत> डेटा प्रकार)। datetime2 . के लिए इसके दस्तावेज़ीकरण में , यह बताता है:
<ब्लॉकक्वॉट>datetime2 . का पहला बाइट value मूल्य की शुद्धता को संग्रहीत करता है, जिसका अर्थ है कि datetime2 . के लिए आवश्यक वास्तविक संग्रहण value ऊपर दी गई तालिका में दर्शाया गया संग्रहण आकार है और सटीकता को संग्रहीत करने के लिए 1 अतिरिक्त बाइट है। यह एक datetime2 . का अधिकतम आकार बनाता है मान 9 बाइट्स – 1 बाइट सटीक रूप से संग्रहीत करता है और डेटा संग्रहण के लिए अधिकतम सटीकता पर 8 बाइट्स।
लेकिन डेटाटाइमऑफ़सेट . के लिए दस्तावेज़ीकरण इसमें यह पाठ शामिल नहीं है, और न ही समय दस्तावेज़ीकरण।
इससे मुझे आश्चर्य हुआ कि क्या इन डेटा प्रकारों के बीच उनके मूल्यों को संग्रहीत करने में कोई अंतर है। लॉजिक ने मुझे बताया कि उन्हें उसी तरह काम करना चाहिए, क्योंकि उन सभी के पास उपयोगकर्ता द्वारा परिभाषित सटीकता है, लेकिन मैं इसका पता लगाना चाहता था।
संक्षिप्त उत्तर हां है, डेटाटाइमऑफ़सेट ऐसा प्रतीत होता है कि datetime2 . के समान कार्य करता है (अतिरिक्त बाइट के संबंध में), भले ही इसे इस तरह से प्रलेखित नहीं किया गया हो।
शेष लेख विभिन्न उदाहरणों के माध्यम से चलता है जहां मैं डेटाटाइमऑफ़सेट . का संग्रहण आकार लौटाता हूं विभिन्न संदर्भों में मूल्य।
एक चर में संग्रहीत डेटा
आइए एक डेटाटाइमऑफ़सेट संग्रहित करें एक चर में मान और उसके भंडारण आकार की जाँच करें। फिर मैं उस मान को varbinary . में बदल दूंगा और इसे दोबारा जांचें।
DATALENGTH का उपयोग करके बाइट्स में लंबाई
यदि हम DATALENGTH()
. का उपयोग करते हैं तो यहां क्या होता है datetimeoffset(7) . के लिए उपयोग किए गए बाइट्स की संख्या वापस करने के लिए फ़ंक्शन मूल्य:
DECLARE @d datetimeoffset(7); SET @d = '2025-05-21 10:15:30.1234567 +07:00'; SELECT @d AS 'Value', DATALENGTH(@d) AS 'Length in Bytes';
परिणाम
+------------------------------------+-------------------+ | Value | Length in Bytes | |------------------------------------+-------------------| | 2025-05-21 10:15:30.1234567 +07:00 | 10 | +------------------------------------+-------------------+
इस उदाहरण में मान का अधिकतम पैमाना 7 है (क्योंकि मैं चर को datetimeoffset(7) के रूप में घोषित करता हूं) ), और यह 10 बाइट्स की लंबाई लौटाता है।
यहाँ कोई आश्चर्य की बात नहीं है, यह सटीक संग्रहण आकार है जो Microsoft दस्तावेज़ इंगित करता है कि यह होना चाहिए।
हालांकि, अगर हम मान को varbinary . में बदलते हैं हमें एक अलग परिणाम मिलता है।
'varbinary' में बदलने के बाद बाइट्स में लंबाई
कुछ डेवलपर डेटाटाइमऑफ़सेट को कनवर्ट करना पसंद करते हैं और डेटाटाइम2 varbinary . के चर , क्योंकि यह इस बात का अधिक प्रतिनिधि है कि SQL सर्वर इसे डेटाबेस में कैसे संग्रहीत करता है। हालांकि यह आंशिक रूप से सच है, परिणाम बिल्कुल संग्रहीत मूल्य के समान नहीं हैं (जैसा कि आप बाद में देखेंगे)।
अगर हम अपने डेटाटाइमऑफ़सेट . को रूपांतरित करते हैं, तो यहां क्या होता है? varbinary . का मान :
DECLARE @d datetimeoffset(7); SET @d = '2025-05-21 10:15:30.1234567 +07:00'; SELECT CONVERT(VARBINARY(16), @d) AS 'Value', DATALENGTH(CONVERT(VARBINARY(16), @d)) AS 'Length in Bytes';
परिणाम
+--------------------------+-------------------+ | Value | Length in Bytes | |--------------------------+-------------------| | 0x0787CBB24F1B3F480BA401 | 11 | +--------------------------+-------------------+
इस मामले में हमें 11 बाइट्स मिलते हैं।
यह डेटाटाइमऑफ़सेट . का हेक्साडेसिमल प्रतिनिधित्व है मूल्य। वास्तविक दिनांक समय ऑफ़सेट मान (और इसकी सटीकता) 0x
. के बाद सब कुछ है . हेक्स वर्णों की प्रत्येक जोड़ी एक बाइट है। 11 जोड़े हैं, और इसलिए 11 बाइट्स हैं। इसकी पुष्टि तब होती है जब हम DATALENGTH()
. का उपयोग करते हैं लंबाई को बाइट्स में वापस करने के लिए।
इस उदाहरण में हम देख सकते हैं कि पहली बाइट 07
. है . यह सटीकता का प्रतिनिधित्व करता है (मैंने 7 के पैमाने का उपयोग किया है और इसलिए यहां प्रदर्शित किया गया है)।
अगर मैं स्केल बदलता हूं, तो हम देख सकते हैं कि स्केल से मेल खाने के लिए पहला बाइट बदलता है:
DECLARE @d datetimeoffset(3); SET @d = '2025-05-21 10:15:30.1234567 +07:00'; SELECT CONVERT(VARBINARY(16), @d) AS 'Value', DATALENGTH(CONVERT(VARBINARY(16), @d)) AS 'Length in Bytes';
परिणाम
+------------------------+-------------------+ | Value | Length in Bytes | |------------------------+-------------------| | 0x03CBFCB2003F480BA401 | 10 | +------------------------+-------------------+
हम यह भी देख सकते हैं कि लंबाई तदनुसार कम हो गई है।
डेटाबेस में संग्रहीत डेटा
इस उदाहरण में, मैं विभिन्न datetimeoffset(n) . के साथ एक डेटाबेस बनाता हूं कॉलम, और फिर COL_LENGTH()
. का उपयोग करें प्रत्येक कॉलम की लंबाई, बाइट्स में वापस करने के लिए। इसके बाद मैं DBCC PAGE
. का उपयोग करने से पहले, कॉलम में मान सम्मिलित करता हूं प्रत्येक डेटाटाइमऑफ़सेट . के संग्रहण आकार की जांच करने के लिए पृष्ठ फ़ाइल पर मान लेता है।
एक डेटाबेस बनाएं:
CREATE DATABASE Test;
एक टेबल बनाएं:
USE Test; CREATE TABLE DatetimeoffsetTest ( d0 datetimeoffset(0), d1 datetimeoffset(1), d2 datetimeoffset(2), d3 datetimeoffset(3), d4 datetimeoffset(4), d5 datetimeoffset(5), d6 datetimeoffset(6), d7 datetimeoffset(7) );
इस मामले में मैं आठ कॉलम बनाता हूं - प्रत्येक उपयोगकर्ता परिभाषित पैमाने के लिए एक जिसे हम datetimeoffset(n) के साथ उपयोग कर सकते हैं ।
अब हम प्रत्येक कॉलम के स्टोरेज साइज की जांच कर सकते हैं।
COL_LENGTH() का उपयोग करके बाइट्स में लंबाई
COL_LENGTH()
का उपयोग करें प्रत्येक कॉलम की लंबाई (बाइट्स में) जांचने के लिए:
SELECT COL_LENGTH ( 'DatetimeoffsetTest' , 'd0' ) AS 'd0', COL_LENGTH ( 'DatetimeoffsetTest' , 'd1' ) AS 'd1', COL_LENGTH ( 'DatetimeoffsetTest' , 'd2' ) AS 'd2', COL_LENGTH ( 'DatetimeoffsetTest' , 'd3' ) AS 'd3', COL_LENGTH ( 'DatetimeoffsetTest' , 'd4' ) AS 'd4', COL_LENGTH ( 'DatetimeoffsetTest' , 'd5' ) AS 'd5', COL_LENGTH ( 'DatetimeoffsetTest' , 'd6' ) AS 'd6', COL_LENGTH ( 'DatetimeoffsetTest' , 'd7' ) AS 'd7';
परिणाम:
+------+------+------+------+------+------+------+------+ | d0 | d1 | d2 | d3 | d4 | d5 | d6 | d7 | |------+------+------+------+------+------+------+------| | 8 | 8 | 8 | 9 | 9 | 10 | 10 | 10 | +------+------+------+------+------+------+------+------+
तो एक बार फिर, हमें वही परिणाम मिलता है जो दस्तावेज़ीकरण बताता है कि हम प्राप्त करेंगे। इसकी उम्मीद की जानी चाहिए, क्योंकि दस्तावेज़ीकरण स्पष्ट रूप से "कॉलम की लंबाई (बाइट्स)" बताता है, जिसे हम यहां माप रहे हैं।
संग्रहीत डेटा की जांच के लिए DBCC पृष्ठ का उपयोग करें
आइए अब DBCC PAGE
का उपयोग करें इस तालिका में संग्रहीत डेटा का वास्तविक संग्रहण आकार खोजने के लिए।
सबसे पहले, आइए कुछ डेटा डालें:
DECLARE @d datetimeoffset(7) = '2025-05-21 10:15:30.1234567 +07:00'; INSERT INTO DatetimeoffsetTest ( d0, d1, d2, d3, d4, d5, d6, d7 ) SELECT @d, @d, @d, @d, @d, @d, @d, @d;
अब डेटा चुनें (बस इसे जांचने के लिए):
SELECT * FROM DatetimeoffsetTest;
परिणाम (ऊर्ध्वाधर आउटपुट का उपयोग करके):
d0 | 2025-05-21 10:15:30.0000000 +07:00 d1 | 2025-05-21 10:15:30.1000000 +07:00 d2 | 2025-05-21 10:15:30.1200000 +07:00 d3 | 2025-05-21 10:15:30.1230000 +07:00 d4 | 2025-05-21 10:15:30.1235000 +07:00 d5 | 2025-05-21 10:15:30.1234600 +07:00 d6 | 2025-05-21 10:15:30.1234570 +07:00 d7 | 2025-05-21 10:15:30.1234567 +07:00
जैसा कि अपेक्षित था, मान उस सटीकता का उपयोग करते हैं जो पहले स्तंभ स्तर पर निर्दिष्ट की गई थी।
ध्यान दें कि मेरा सिस्टम अनुगामी शून्य प्रदर्शित करता है। आपका ऐसा हो भी सकता है और नहीं भी। भले ही, यह वास्तविक सटीकता या सटीकता को प्रभावित नहीं करता है।
अब, इससे पहले कि हम DBCC PAGE()
का उपयोग करें , हमें यह जानने की जरूरत है कि इसे कौन सा पेजपीआईडी पास करना है। हम उपयोग कर सकते हैं DBCC IND()
उसे खोजने के लिए।
पेजपीआईडी ढूंढें:
DBCC IND('Test', 'dbo.DatetimeoffsetTest', 0);
परिणाम (ऊर्ध्वाधर आउटपुट का उपयोग करके):
-[ RECORD 1 ]------------------------- PageFID | 1 PagePID | 307 IAMFID | NULL IAMPID | NULL ObjectID | 1525580473 IndexID | 0 PartitionNumber | 1 PartitionID | 72057594043170816 iam_chain_type | In-row data PageType | 10 IndexLevel | NULL NextPageFID | 0 NextPagePID | 0 PrevPageFID | 0 PrevPagePID | 0 -[ RECORD 2 ]------------------------- PageFID | 1 PagePID | 376 IAMFID | 1 IAMPID | 307 ObjectID | 1525580473 IndexID | 0 PartitionNumber | 1 PartitionID | 72057594043170816 iam_chain_type | In-row data PageType | 1 IndexLevel | 0 NextPageFID | 0 NextPagePID | 0 PrevPageFID | 0 PrevPagePID | 0
यह दो रिकॉर्ड देता है। हम 1 के पेज टाइप (दूसरा रिकॉर्ड) में रुचि रखते हैं। हम उस रिकॉर्ड से PagePID चाहते हैं। इस मामले में पेजपीआईडी 376 . है ।
अब हम उस पेजपीआईडी को ले सकते हैं और इसे निम्नलिखित में उपयोग कर सकते हैं:
DBCC TRACEON(3604, -1); DBCC PAGE(Test, 1, 376, 3);
अभी हम मुख्य रूप से निम्नलिखित भाग में रुचि रखते हैं:
Slot 0 Column 1 Offset 0x4 Length 8 Length (physical) 8 d0 = 2025-05-21 10:15:30 +07:00 Slot 0 Column 2 Offset 0xc Length 8 Length (physical) 8 d1 = 2025-05-21 10:15:30.1 +07:00 Slot 0 Column 3 Offset 0x14 Length 8 Length (physical) 8 d2 = 2025-05-21 10:15:30.12 +07:00 Slot 0 Column 4 Offset 0x1c Length 9 Length (physical) 9 d3 = 2025-05-21 10:15:30.123 +07:00 Slot 0 Column 5 Offset 0x25 Length 9 Length (physical) 9 d4 = 2025-05-21 10:15:30.1235 +07:00 Slot 0 Column 6 Offset 0x2e Length 10 Length (physical) 10 d5 = 2025-05-21 10:15:30.12346 +07:00 Slot 0 Column 7 Offset 0x38 Length 10 Length (physical) 10 d6 = 2025-05-21 10:15:30.123457 +07:00 Slot 0 Column 8 Offset 0x42 Length 10 Length (physical) 10 d7 = 2025-05-21 10:15:30.1234567 +07:00
तो हमें फिर से वही परिणाम मिलता है। बिल्कुल वैसा ही जैसा कि दस्तावेज़ीकरण बताता है।
जब हम यहां हैं, तो डेटा की जांच करें - वास्तविक दिनांक/समय मान जैसे वे SQL सर्वर में संग्रहीत हैं।
वास्तविक मान पृष्ठ फ़ाइल के इस भाग में संग्रहीत होते हैं:
Memory Dump @0x000000041951A060 0000000000000000: 10004c00 d22d003f 480ba401 35ca013f 480ba401 ..L.Ò-.?H.¤.5Ê.?H.¤. 0000000000000014: 14e6113f 480ba401 cbfcb200 3f480ba4 01f3dffd .æ.?H.¤.Ëü².?H.¤.óßý 0000000000000028: 063f480b a4017abf ea45003f 480ba401 c17a2bbb .?H.¤.z¿êE.?H.¤.Áz+» 000000000000003C: 023f480b a40187cb b24f1b3f 480ba401 080000 .?H.¤.˲O.?H.¤....
इसमें अभी भी कुछ अतिरिक्त बिट्स शामिल हैं। आइए कुछ चीजें हटा दें, ताकि केवल हमारे दिनांक और समय के मान बने रहें:
d22d003f 480ba401 35ca013f 480ba401 14e6113f 480ba401 cbfcb200 3f480ba4 01f3dffd 063f480b a4017abf ea45003f 480ba401 c17a2bbb 023f480b a40187cb b24f1b3f 480ba401
शेष हेक्स अंकों में हमारे सभी दिनांक और समय डेटा होते हैं, लेकिन सटीकता नहीं . हालांकि, उन्हें 4 बाइट विखंडू में व्यवस्थित किया गया है, इसलिए हमें अलग-अलग मान प्राप्त करने के लिए रिक्त स्थान को पुनर्व्यवस्थित करना होगा।
यहाँ अंतिम परिणाम है। मैंने बेहतर पठनीयता के लिए प्रत्येक दिनांक/समय मान को एक नई पंक्ति पर रखा है।
d22d003f480ba401 35ca013f480ba401 14e6113f480ba401 cbfcb2003f480ba401 f3dffd063f480ba401 7abfea45003f480ba401 c17a2bbb023f480ba401 87cbb24f1b3f480ba401
वे वास्तविक हेक्साडेसिमल मान हैं (सटीक घटाकर ) अगर हम डेटाटाइमऑफ़सेट . को रूपांतरित करते हैं तो हमें मिलेगा varbinary . का मान . इस तरह:
SELECT CONVERT(VARBINARY(16), d0) AS 'd0', CONVERT(VARBINARY(16), d1) AS 'd1', CONVERT(VARBINARY(16), d2) AS 'd2', CONVERT(VARBINARY(16), d3) AS 'd3', CONVERT(VARBINARY(16), d4) AS 'd4', CONVERT(VARBINARY(16), d5) AS 'd5', CONVERT(VARBINARY(16), d6) AS 'd6', CONVERT(VARBINARY(16), d7) AS 'd7' FROM DatetimeoffsetTest;
परिणाम (ऊर्ध्वाधर आउटपुट का उपयोग करके):
d0 | 0x00D22D003F480BA401 d1 | 0x0135CA013F480BA401 d2 | 0x0214E6113F480BA401 d3 | 0x03CBFCB2003F480BA401 d4 | 0x04F3DFFD063F480BA401 d5 | 0x057ABFEA45003F480BA401 d6 | 0x06C17A2BBB023F480BA401 d7 | 0x0787CBB24F1B3F480BA401
तो हमें वही परिणाम मिलता है - सिवाय इसके कि इसे सटीकता के साथ तैयार किया गया है।
यहां एक तालिका है जो वास्तविक पृष्ठ फ़ाइल डेटा की तुलना CONVERT()
के परिणामों से करती है ऑपरेशन।
पृष्ठ फ़ाइल डेटा | कन्वर्ट () डेटा |
---|---|
d22d003f480ba401 | 00D22D003F480BA401 |
35ca013f480ba401 | 0135CA013F480BA401 |
14e6113f480ba401 | 0214E6113F480BA401 |
cbfcb2003f480ba401 | 03CBFCB2003F480BA401 |
f3dffd063f480ba401 | 04F3DFFD063F480BA401 |
7abfea45003f480ba401 | 057ABFEA45003F480BA401 |
c17a2bbb023f480ba401 | 06C17A2BBB023F480BA401 |
87cbb24f1b3f480ba401 | 0787CBB24F1B3F480BA401 |
इसलिए हम देख सकते हैं कि पृष्ठ फ़ाइल सटीकता को संग्रहीत नहीं करती है, लेकिन परिवर्तित परिणाम करता है।
मैंने वास्तविक दिनांक और समय भागों को लाल रंग में हाइलाइट किया। मैंने 0x
. भी हटा दिया है परिवर्तित परिणामों से उपसर्ग, ताकि केवल वास्तविक दिनांक/समय डेटा प्रदर्शित हो (सटीकता के साथ)।
यह भी ध्यान दें कि हेक्साडेसिमल केस-असंवेदनशील है, इसलिए यह तथ्य कि एक लोअरकेस का उपयोग करता है और दूसरा अपरकेस का उपयोग करता है, कोई समस्या नहीं है।
निष्कर्ष
डेटाटाइमऑफ़सेट . को परिवर्तित करते समय varbinary . का मान , इसे सटीक स्टोर करने के लिए एक अतिरिक्त बाइट की आवश्यकता होती है। समय भाग की व्याख्या करने के लिए इसे सटीकता की आवश्यकता होती है (क्योंकि यह एक समय अंतराल के रूप में संग्रहीत होता है, जिसका सटीक मान सटीकता पर निर्भर करेगा)।
जब एक डेटाबेस में संग्रहीत किया जाता है, तो कॉलम स्तर पर एक बार सटीकता निर्दिष्ट की जाती है। यह तार्किक लगता है, क्योंकि प्रत्येक पंक्ति में सटीकता जोड़ने की कोई आवश्यकता नहीं है जब सभी पंक्तियाँ वैसे भी समान सटीकता का उपयोग करती हैं। इसके लिए प्रत्येक पंक्ति के लिए एक अतिरिक्त बाइट की आवश्यकता होगी, जो अनावश्यक रूप से भंडारण आवश्यकताओं को बढ़ाएगी।