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

SQL सर्वर में 'डेटाटाइम 2' संग्रहण आकार को समझना

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

विशेष रूप से, मैं निम्नलिखित को देखता हूं:

  • Microsoft के दस्तावेज़
  • एक चर में संग्रहीत डेटा
    • बाइट्स में लंबाई DATALENGTH() का उपयोग कर रही है
    • बाइट्स में लंबाई DATALENGTH() का उपयोग कर रही है varbinary . में बदलने के बाद
  • डेटाबेस में संग्रहीत डेटा
    • बाइट्स में लंबाई COL_LENGTH() का उपयोग कर रही है
    • बाइट्स में लंबाई DBCC PAGE() का उपयोग कर रही है

उनमें से कुछ एक-दूसरे के विरोधाभासी प्रतीत होते हैं, और आप जहां देखते हैं, उसके आधार पर आपको एक ही मान के लिए दो अलग-अलग भंडारण आकार की मात्रा दिखाई देगी।

एक डेटाटाइम2 मान एक भिन्न संग्रहण आकार दिखा सकता है, यह इस पर निर्भर करता है कि यह डेटाबेस में संग्रहीत है या नहीं, datetime2 के रूप में चर, या varbinary . में परिवर्तित किया गया .

लेकिन इसके लिए एक प्रशंसनीय स्पष्टीकरण है - यह इस बात पर निर्भर करता है कि सटीक . कहां है संग्रहित किया जा रहा है।

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

Microsoft का दस्तावेज़ीकरण

सबसे पहले, आइए देखें कि आधिकारिक दस्तावेज क्या कहते हैं।

datetime2 . पर Microsoft के दस्तावेज़ीकरण डेटा प्रकार बताता है कि इसका संग्रहण आकार इस प्रकार है:

<ब्लॉककोट>

3 से कम सटीकता के लिए 6 बाइट्स।
परिशुद्धता 3 या 4 के लिए 7 बाइट्स।
अन्य सभी परिशुद्धता के लिए 8 बाइट्स की आवश्यकता होती है।

लेकिन यह उपरोक्त तालिका को निम्नलिखित कथन के साथ उत्तीर्ण करता है:

<ब्लॉककोट>

datetime2 . का पहला बाइट value मूल्य की शुद्धता को संग्रहीत करता है, जिसका अर्थ है कि datetime2 . के लिए आवश्यक वास्तविक संग्रहण value ऊपर दी गई तालिका में दर्शाया गया संग्रहण आकार है और सटीकता को संग्रहीत करने के लिए 1 अतिरिक्त बाइट है। यह एक datetime2 . का अधिकतम आकार बनाता है मान 9 बाइट्स – 1 बाइट सटीक रूप से संग्रहीत करता है और डेटा संग्रहण के लिए अधिकतम सटीकता पर 8 बाइट्स।

तो उपरोक्त जानकारी को देखते हुए, स्पष्ट निष्कर्ष यह होगा कि तालिका को निम्नानुसार लिखा जा सकता है/(चाहिए?) <ब्लॉककोट>

3 से कम सटीकता के लिए 7 बाइट्स।
परिशुद्धता 3 या 4 के लिए 8 बाइट्स।
अन्य सभी परिशुद्धता के लिए 9 बाइट्स की आवश्यकता होती है।

इस तरह, उन्हें सटीकता के बारे में अतिरिक्त जानकारी के साथ इसे अर्हता प्राप्त करने की आवश्यकता नहीं होगी।

लेकिन यह इतना आसान नहीं है।

एक चर में संग्रहीत डेटा

सबसे पहले, आइए एक datetime2 . स्टोर करें एक चर में मान और उसके भंडारण आकार की जाँच करें। फिर मैं उस मान को varbinary . में बदल दूंगा और इसे दोबारा जांचें।

DATALENGTH का उपयोग करके बाइट्स में लंबाई

यदि हम DATALENGTH() . का उपयोग करते हैं तो यहां क्या होता है datetime2(7) . के लिए उपयोग किए गए बाइट्स की संख्या वापस करने के लिए फ़ंक्शन मूल्य:

DECLARE @d datetime2(7);
SET @d = '2025-05-21 10:15:30.1234567';
SELECT 
  @d AS 'Value',
  DATALENGTH(@d) AS 'Length in Bytes';

परिणाम

+-----------------------------+-------------------+
| Value                       | Length in Bytes   |
|-----------------------------+-------------------|
| 2025-05-21 10:15:30.1234567 | 8                 |
+-----------------------------+-------------------+

इस उदाहरण में मान का अधिकतम पैमाना 7 है (क्योंकि मैं वैरिएबल को datetime2(7) घोषित करता हूं) ), और यह 8 बाइट्स की लंबाई देता है।

ऐसा लगता है कि माइक्रोसॉफ्ट ने परिशुद्धता को स्टोर करने के लिए अतिरिक्त बाइट की आवश्यकता के बारे में क्या कहा है। Microsoft को उद्धृत करने के लिए, यह अधिकतम आकार को datetime2 . बनाता है मान 9 बाइट – 1 बाइट सटीक स्टोर करता है और डेटा संग्रहण के लिए 8 बाइट्स अधिकतम सटीकता पर।

हालांकि यह सच है कि हमें डेटा संग्रहण के लिए 8 बाइट्स . मिलते हैं , ऐसा प्रतीत होता है कि हम सटीकता को संग्रहीत करने के लिए उपयोग की जाने वाली 1 बाइट को याद कर रहे हैं।

हालांकि, अगर हम मान को varbinary . में बदलते हैं हमें एक अलग कहानी मिलती है।

'varbinary' में बदलने के बाद बाइट्स में लंबाई

अगर हम अपने डेटाटाइम2 . को रूपांतरित करते हैं तो यहां बताया गया है कि क्या होता है varbinary . का मान :

DECLARE @d datetime2(7);
SET @d = '2025-05-21 10:15:30.1234567';
SELECT 
  CONVERT(VARBINARY(10), @d) AS 'Value',
  DATALENGTH(CONVERT(VARBINARY(10), @d)) AS 'Length in Bytes';

परिणाम

+----------------------+-------------------+
| Value                | Length in Bytes   |
|----------------------+-------------------|
| 0x0787A311FC553F480B | 9                 |
+----------------------+-------------------+

इस मामले में हमें 9 बाइट मिलते हैं।

यह datetime2 . का हेक्साडेसिमल निरूपण है मूल्य। वास्तविक दिनांक समय मान (और इसकी सटीकता) 0x . के बाद सब कुछ है . हेक्स वर्णों की प्रत्येक जोड़ी एक बाइट है। 9 जोड़े हैं, और इसलिए 9 बाइट्स हैं। इसकी पुष्टि तब होती है जब हम DATALENGTH() . का उपयोग करते हैं लंबाई को बाइट्स में वापस करने के लिए।

इस उदाहरण में हम देख सकते हैं कि पहली बाइट 07 . है . यह सटीकता का प्रतिनिधित्व करता है (मैंने 7 के पैमाने का उपयोग किया है और इसलिए यहां प्रदर्शित किया गया है)।

अगर मैं स्केल बदलता हूं, तो हम देख सकते हैं कि स्केल से मेल खाने के लिए पहला बाइट बदलता है:

DECLARE @d datetime2(3);
SET @d = '2025-05-21 10:15:30.1234567';
SELECT 
  CONVERT(VARBINARY(10), @d) AS 'Value',
  DATALENGTH(CONVERT(VARBINARY(10), @d)) AS 'Length in Bytes';

परिणाम

+--------------------+-------------------+
| Value              | Length in Bytes   |
|--------------------+-------------------|
| 0x034B8233023F480B | 8                 |
+--------------------+-------------------+

हम यह भी देख सकते हैं कि लंबाई तदनुसार कम हो गई है।

तो इस मामले में हमारे परिणाम पूरी तरह से Microsoft दस्तावेज़ से मेल खाते हैं - सटीकता के लिए एक अतिरिक्त बाइट जोड़ा गया है।

कई डेवलपर्स यह मानते हैं कि SQL सर्वर अपने datetime2 . को इस प्रकार संग्रहीत करता है डेटाबेस में मान। हालांकि, यह धारणा गलत प्रतीत होती है।

डेटाबेस में संग्रहीत डेटा

इस उदाहरण में, मैं एक डेटाबेस बनाता हूं जिसमें विभिन्न datetime2(n) . के साथ एक तालिका होती है स्तंभ। मैं तब COL_LENGTH() . का उपयोग करता हूं प्रत्येक कॉलम की लंबाई, बाइट्स में वापस करने के लिए। उसके बाद, मैं DBCC PAGE . का उपयोग करने से पहले इसमें मान सम्मिलित करता हूं प्रत्येक datetime2 . के संग्रहण आकार की जांच करने के लिए पृष्ठ फ़ाइल पर मान लेता है।

एक डेटाबेस बनाएं:

CREATE DATABASE Test;

एक टेबल बनाएं:

USE Test;

CREATE TABLE Datetime2Test (
    d0 datetime2(0),
    d1 datetime2(1),
    d2 datetime2(2),
    d3 datetime2(3),
    d4 datetime2(4),
    d5 datetime2(5),
    d6 datetime2(6),
    d7 datetime2(7)
    );

इस मामले में मैं आठ कॉलम बनाता हूं - प्रत्येक उपयोगकर्ता परिभाषित पैमाने के लिए एक जिसे हम datetime2(n) के साथ उपयोग कर सकते हैं ।

अब हम प्रत्येक कॉलम के स्टोरेज साइज की जांच कर सकते हैं।

COL_LENGTH() का उपयोग करके बाइट्स में लंबाई

COL_LENGTH() का उपयोग करें प्रत्येक कॉलम की लंबाई (बाइट्स में) जांचने के लिए:

SELECT 
  COL_LENGTH ( 'Datetime2Test' , 'd0' ) AS 'd0',
  COL_LENGTH ( 'Datetime2Test' , 'd1' ) AS 'd1',
  COL_LENGTH ( 'Datetime2Test' , 'd2' ) AS 'd2',
  COL_LENGTH ( 'Datetime2Test' , 'd3' ) AS 'd3',
  COL_LENGTH ( 'Datetime2Test' , 'd4' ) AS 'd4',
  COL_LENGTH ( 'Datetime2Test' , 'd5' ) AS 'd5',
  COL_LENGTH ( 'Datetime2Test' , 'd6' ) AS 'd6',
  COL_LENGTH ( 'Datetime2Test' , 'd7' ) AS 'd7';  

परिणाम:

+------+------+------+------+------+------+------+------+
| d0   | d1   | d2   | d3   | d4   | d5   | d6   | d7   |
|------+------+------+------+------+------+------+------|
| 6    | 6    | 6    | 7    | 7    | 8    | 8    | 8    |
+------+------+------+------+------+------+------+------+

इसलिए एक बार फिर, हमें नहीं लगता कि अतिरिक्त बाइट का उपयोग सटीकता को स्टोर करने के लिए किया जाता है।

संग्रहीत डेटा की जांच के लिए DBCC पृष्ठ का उपयोग करें

आइए अब DBCC PAGE का उपयोग करें इस तालिका में संग्रहीत डेटा का वास्तविक संग्रहण आकार खोजने के लिए।

सबसे पहले, आइए कुछ डेटा डालें:

DECLARE @d datetime2(7) = '2025-05-21 10:15:30.1234567';
INSERT INTO Datetime2Test ( d0, d1, d2, d3, d4, d5, d6, d7 )
SELECT @d, @d, @d, @d, @d, @d, @d, @d;

अब डेटा चुनें (बस इसे जांचने के लिए):

SELECT * FROM Datetime2Test;

परिणाम (ऊर्ध्वाधर आउटपुट का उपयोग करके):

d0 | 2025-05-21 10:15:30
d1 | 2025-05-21 10:15:30.1
d2 | 2025-05-21 10:15:30.12
d3 | 2025-05-21 10:15:30.123
d4 | 2025-05-21 10:15:30.1235
d5 | 2025-05-21 10:15:30.12346
d6 | 2025-05-21 10:15:30.123457
d7 | 2025-05-21 10:15:30.1234567

जैसा कि अपेक्षित था, मान उस सटीकता का उपयोग करते हैं जो पहले स्तंभ स्तर पर निर्दिष्ट की गई थी।

अब, इससे पहले कि हम DBCC PAGE() का उपयोग करें , हमें यह जानने की जरूरत है कि इसे कौन सा पेजपीआईडी ​​पास करना है। हम उपयोग कर सकते हैं DBCC IND() उसे खोजने के लिए।

पेजपीआईडी ​​ढूंढें:

DBCC IND('Test', 'dbo.Datetime2Test', 0);

परिणाम (ऊर्ध्वाधर आउटपुट का उपयोग करके):

-[ RECORD 1 ]-------------------------
PageFID         | 1
PagePID         | 306
IAMFID          | NULL
IAMPID          | NULL
ObjectID        | 1205579333
IndexID         | 0
PartitionNumber | 1
PartitionID     | 72057594043039744
iam_chain_type  | In-row data
PageType        | 10
IndexLevel      | NULL
NextPageFID     | 0
NextPagePID     | 0
PrevPageFID     | 0
PrevPagePID     | 0
-[ RECORD 2 ]-------------------------
PageFID         | 1
PagePID         | 360
IAMFID          | 1
IAMPID          | 306
ObjectID        | 1205579333
IndexID         | 0
PartitionNumber | 1
PartitionID     | 72057594043039744
iam_chain_type  | In-row data
PageType        | 1
IndexLevel      | 0
NextPageFID     | 0
NextPagePID     | 0
PrevPageFID     | 0
PrevPagePID     | 0

यह दो रिकॉर्ड देता है। हम 1 के पेज टाइप (दूसरा रिकॉर्ड) में रुचि रखते हैं। हम उस रिकॉर्ड से PagePID चाहते हैं। इस मामले में पेजपीआईडी ​​ 360 . है ।

अब हम उस पेजपीआईडी ​​को ले सकते हैं और इसे निम्नलिखित में उपयोग कर सकते हैं:

DBCC TRACEON(3604, -1);
DBCC PAGE(Test, 1, 360, 3);

यह बहुत अधिक डेटा उत्पन्न करता है, लेकिन हम मुख्य रूप से निम्नलिखित भाग में रुचि रखते हैं:

Slot 0 Column 1 Offset 0x4 Length 6 Length (physical) 6

d0 = 2025-05-21 10:15:30            

Slot 0 Column 2 Offset 0xa Length 6 Length (physical) 6

d1 = 2025-05-21 10:15:30.1          

Slot 0 Column 3 Offset 0x10 Length 6 Length (physical) 6

d2 = 2025-05-21 10:15:30.12         

Slot 0 Column 4 Offset 0x16 Length 7 Length (physical) 7

d3 = 2025-05-21 10:15:30.123   

Slot 0 Column 5 Offset 0x1d Length 7 Length (physical) 7

d4 = 2025-05-21 10:15:30.1235       

Slot 0 Column 6 Offset 0x24 Length 8 Length (physical) 8

d5 = 2025-05-21 10:15:30.12346      

Slot 0 Column 7 Offset 0x2c Length 8 Length (physical) 8

d6 = 2025-05-21 10:15:30.123457     

Slot 0 Column 8 Offset 0x34 Length 8 Length (physical) 8

d7 = 2025-05-21 10:15:30.1234567                                   

तो ऐसा लगता है कि यह सटीकता के लिए अतिरिक्त बाइट का उपयोग नहीं करता है।

लेकिन किसी निष्कर्ष पर पहुंचने से पहले आइए वास्तविक डेटा की जांच करें।

वास्तविक डेटा पृष्ठ फ़ाइल के इस भाग में संग्रहीत किया जाता है:

Memory Dump @0x000000041883A060

0000000000000000:   10003c00 4290003f 480b95a2 053f480b d459383f  ..<.B..?H.•¢.?H.ÔY8?
0000000000000014:   480b4b82 33023f48 0bf31603 163f480b 7ae51edc  H.K‚3.?H.ó...?H.zå.Ü
0000000000000028:   003f480b c1f63499 083f480b 87a311fc 553f480b  .?H.Áö4..?H.‡£.üU?H.
000000000000003C:   080000                                        ...                                           ...   

जैसा कि आप देख सकते हैं, इनमें से कोई भी datetime2 को परिवर्तित करके हमें प्राप्त होने वाले परिणामों जैसा नहीं दिखता है varbinary . का मान . लेकिन यह काफी करीब है।

अगर मैं कुछ चीज़ें हटा दूं तो यह कैसा दिखता है:

4290003f 480b95a2 053f480b d459383f
480b4b82 33023f48 0bf31603 163f480b 7ae51edc
003f480b c1f63499 083f480b 87a311fc 553f480b

शेष हेक्स अंकों में हमारे सभी दिनांक और समय डेटा होते हैं, लेकिन सटीकता नहीं . हालांकि, हमें प्रत्येक पंक्ति के लिए वास्तविक मान प्राप्त करने के लिए रिक्त स्थान को पुनर्व्यवस्थित करना होगा।

यहाँ अंतिम परिणाम है। मैंने बेहतर पठनीयता के लिए प्रत्येक दिनांक/समय मान को एक नई पंक्ति पर रखा है।

4290003f480b 
95a2053f480b 
d459383f480b 
4b8233023f480b
f31603163f480b 
7ae51edc003f480b 
c1f63499083f480b 
87a311fc553f480b

वे वास्तविक हेक्साडेसिमल मान हैं (सटीक घटाकर ) अगर हम datetime2 . को रूपांतरित करते हैं तो हमें मिलेगा varbinary . का मान . यह सुनिश्चित करने के लिए, चलिए ठीक आगे बढ़ते हैं और बस यही करते हैं:

SELECT 
  CONVERT(VARBINARY(10), d0) AS 'd0',
  CONVERT(VARBINARY(10), d1) AS 'd1',
  CONVERT(VARBINARY(10), d2) AS 'd2',
  CONVERT(VARBINARY(10), d3) AS 'd3',
  CONVERT(VARBINARY(10), d4) AS 'd4',
  CONVERT(VARBINARY(10), d5) AS 'd5',
  CONVERT(VARBINARY(10), d6) AS 'd6',
  CONVERT(VARBINARY(10), d7) AS 'd7'
FROM Datetime2Test;

परिणाम (ऊर्ध्वाधर आउटपुट का उपयोग करके):

d0 | 0x004290003F480B
d1 | 0x0195A2053F480B
d2 | 0x02D459383F480B
d3 | 0x034B8233023F480B
d4 | 0x04F31603163F480B
d5 | 0x057AE51EDC003F480B
d6 | 0x06C1F63499083F480B
d7 | 0x0787A311FC553F480B

तो हमें वही परिणाम मिलता है - सिवाय इसके कि इसे सटीकता के साथ तैयार किया गया है।

लेकिन चीजों को स्पष्ट करने के लिए, यहां एक तालिका है जो वास्तविक पृष्ठ फ़ाइल डेटा की तुलना CONVERT() के परिणामों से करती है ऑपरेशन।

पृष्ठ फ़ाइल डेटा कन्वर्ट () डेटा
4290003f480b 004290003F480B
95a2053f480b 0195A2053F480B
d459383f480b 02D459383F480B
4b8233023f480b 034B8233023F480B
f31603163f480b 04F31603163F480B
7ae51edc003f480b 057AE51EDC003F480B
c1f63499083f480b 06C1F63499083F480B
87a311fc553f480b 0787A311FC553F480B

इसलिए हम स्पष्ट रूप से देख सकते हैं कि पृष्ठ फ़ाइल सटीकता को संग्रहीत नहीं करती है, लेकिन परिवर्तित परिणाम करता है।

मैंने वास्तविक दिनांक और समय भागों को लाल रंग में हाइलाइट किया। मैंने 0x . भी हटा दिया है परिवर्तित परिणामों से उपसर्ग, ताकि केवल वास्तविक दिनांक/समय डेटा प्रदर्शित हो (सटीकता के साथ)।

यह भी ध्यान दें कि हेक्साडेसिमल केस-असंवेदनशील है, इसलिए यह तथ्य कि एक लोअरकेस का उपयोग करता है और दूसरा अपरकेस का उपयोग करता है, कोई समस्या नहीं है।

निष्कर्ष

डेटाटाइम2 . को रूपांतरित करते समय varbinary . का मान , इसे सटीक स्टोर करने के लिए एक अतिरिक्त बाइट की आवश्यकता होती है। समय भाग की व्याख्या करने के लिए इसे सटीकता की आवश्यकता होती है (क्योंकि यह एक समय अंतराल के रूप में संग्रहीत होता है, जिसका सटीक मान सटीकता पर निर्भर करेगा)।

डेटाबेस में संग्रहीत होने पर, स्तंभ स्तर पर एक बार सटीकता निर्दिष्ट की जाती है। यह तार्किक प्रतीत होगा, क्योंकि यदि कॉलम स्तर पर निर्दिष्ट किया जा सकता है तो प्रत्येक पंक्ति के साथ अतिरिक्त बाइट स्टोर करने की कोई आवश्यकता नहीं है। इसलिए यदि आप निर्दिष्ट करते हैं, तो कहें, datetime2(7) स्तंभ स्तर पर, तब प्रत्येक पंक्ति datetime2(7) . होगी . इसे हर पंक्ति में दोहराने की आवश्यकता नहीं है।

रोनेन एरीली ऊपर वर्णित अपने लेख में इसी निष्कर्ष पर पहुंचे।

अगर आपके पास datetime2(7) . के साथ एक लाख पंक्तियां हैं मान, प्रत्येक पंक्ति के साथ सटीकता को संग्रहीत करने के लिए 9,000,000 बाइट्स की आवश्यकता होगी, जबकि केवल 8,000,001 की तुलना में यदि सटीकता पूरे कॉलम के लिए एक बार संग्रहीत की जाती है।

यह datetime2 . को भी मजबूत करता है मामला डेटाटाइम . से तुलना करते समय . डेटाटाइम . के समान दशमलव स्थानों का उपयोग करने पर भी (अर्थात 3), datetime2 डेटा प्रकार कम संग्रहण का उपयोग करता है (कम से कम जब एक से अधिक पंक्तियों वाली तालिका में संग्रहीत किया जाता है)। और यह उच्च सटीकता के साथ करता है। एक डेटाटाइम मान 8 बाइट्स का उपयोग करता है, जबकि datetime2(3) 7 बाइट्स का उपयोग करता है (प्लस 1 "सटीक" बाइट जो सभी पंक्तियों में साझा किया जाता है)।


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. लिनक्स - पीएचपी 7.0 और एमएसएसक्यूएल (माइक्रोसॉफ्ट एसक्यूएल)

  2. SQL सर्वर 2008 खाली स्ट्रिंग बनाम स्पेस

  3. उदाहरण पर हमेशा उपलब्धता समूह में SQL सर्वर में पारदर्शी डेटा एन्क्रिप्शन (TDE)

  4. SQL सर्वर में टेक्स्ट क्वालीफायर के साथ बल्क इंसर्ट

  5. अंतर जानने के लिए लिनक्स बनाम विंडोज परफॉर्मेंस टेस्ट पर एमएस एसक्यूएल सर्वर