Dalam artikel ini saya membagikan beberapa pengamatan yang saya miliki mengenai datetime2 ukuran penyimpanan tipe data di SQL Server. Mungkin saya akan mengklarifikasi beberapa poin tentang ukuran penyimpanan sebenarnya yang digunakan oleh tipe data ini ketika disimpan dalam database.
Secara khusus, saya melihat yang berikut:
- Dokumentasi Microsoft
- Data disimpan dalam variabel
- Panjang dalam byte menggunakan
DATALENGTH()
- Panjang dalam byte menggunakan
DATALENGTH()
setelah mengonversi ke varbinary
- Panjang dalam byte menggunakan
- Data disimpan dalam database
- Panjang dalam byte menggunakan
COL_LENGTH()
- Panjang dalam byte menggunakan
DBCC PAGE()
- Panjang dalam byte menggunakan
Beberapa di antaranya tampaknya bertentangan satu sama lain, dan Anda akan melihat dua jumlah ukuran penyimpanan yang berbeda untuk nilai yang sama, tergantung di mana Anda melihatnya.
datetime2 nilai dapat menunjukkan ukuran penyimpanan yang berbeda, bergantung pada apakah disimpan dalam database, sebagai datetime2 variabel, atau dikonversi ke varbinary .
Tapi ada penjelasan yang masuk akal untuk ini – itu tergantung di mana presisi sedang disimpan.
Selama penelitian saya tentang masalah ini, saya menemukan artikel mendalam Ronen Ariely tentang bagaimana datetime2 disimpan dalam file data yang sangat informatif, dan itu mendorong saya untuk menjalankan beberapa tes serupa di lingkungan pengembangan saya sendiri dan menyajikannya di sini.
Dokumentasi Microsoft
Pertama, mari kita lihat apa yang dikatakan oleh dokumentasi resmi.
Dokumentasi Microsoft pada datetime2 tipe data menyatakan bahwa ukuran penyimpanannya adalah sebagai berikut:
6 byte untuk presisi kurang dari 3.
7 byte untuk presisi 3 atau 4.
Semua presisi lainnya memerlukan 8 byte.
Tetapi memenuhi tabel di atas dengan pernyataan berikut:
Byte pertama dari datetime2 nilai menyimpan ketepatan nilai, yang berarti penyimpanan aktual yang diperlukan untuk datetime2 nilai adalah ukuran penyimpanan yang ditunjukkan pada tabel di atas ditambah 1 byte tambahan untuk menyimpan presisi. Ini membuat ukuran maksimum datetime2 nilai 9 byte – 1 byte menyimpan presisi ditambah 8 byte untuk penyimpanan data dengan presisi maksimum.
Jadi dengan informasi di atas, kesimpulan yang jelas untuk ditarik adalah bahwa tabel dapat/(harus?) ditulis sebagai berikut:
7 byte untuk presisi kurang dari 3.
8 byte untuk presisi 3 atau 4.
Semua presisi lainnya memerlukan 9 byte.
Dengan begitu, mereka tidak perlu memenuhi syarat dengan informasi tambahan tentang presisi.
Tapi tidak sesederhana itu.
Data Disimpan dalam Variabel
Pertama, mari kita simpan datetime2 nilai dalam variabel dan periksa ukuran penyimpanannya. Lalu saya akan mengonversi nilai itu menjadi varbinary dan periksa lagi.
Panjang dalam Bytes menggunakan DATALENGTH
Inilah yang terjadi jika kita menggunakan DATALENGTH()
fungsi untuk mengembalikan jumlah byte yang digunakan untuk datetime2(7) nilai:
DECLARE @d datetime2(7);SET @d ='2025-05-21 10:15:30.1234567';PILIH @d SEBAGAI 'Nilai', DATALENGTH(@d) SEBAGAI 'Panjang dalam Bytes';Hasil
+-----------------------------+---------------- ---+| Nilai | Panjang dalam Bytes ||-----------------------------+--------------- ----|| 2025-05-21 10:15:30.1234567 | 8 |+----------------------------+------------------ --+Nilai dalam contoh ini memiliki skala maksimum 7 (karena saya mendeklarasikan variabel sebagai datetime2(7) ), dan mengembalikan panjang 8 byte.
Ini tampaknya bertentangan dengan apa yang dinyatakan Microsoft tentang perlunya byte tambahan untuk menyimpan presisi. Mengutip Microsoft,
Ini membuat ukuran maksimum datetime2 nilai 9 byte – 1 byte menyimpan presisi ditambah 8 byte untuk penyimpanan data dengan presisi maksimum..Meskipun benar bahwa kami tampaknya mendapatkan
8 byte untuk penyimpanan data, kami tampaknya kehilangan 1 byte yang digunakan untuk menyimpan presisi.Namun, jika kita mengonversi nilainya menjadi varbinary kita mendapatkan cerita yang berbeda.
Panjang dalam Bytes setelah Mengonversi ke 'varbinary'
Inilah yang terjadi jika kita mengonversi datetime2 nilai ke 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)) SEBAGAI 'Panjang dalam Bytes';Hasil
+----------------------+-------------------+| Nilai | Panjang dalam Bytes ||----------------------+-------------------|| 0x0787A311FC553F480B | 9 |+----------------------+-------------------+Dalam hal ini kita mendapatkan 9 byte.
Ini adalah representasi heksadesimal dari datetime2 nilai. Nilai waktu tanggal aktual (dan ketepatannya) adalah segalanya setelah
0x
. Setiap pasangan karakter hex adalah satu byte. Ada 9 pasang, dan karenanya 9 byte. Ini dikonfirmasi ketika kami menggunakanDATALENGTH()
untuk mengembalikan panjangnya dalam byte.Dalam contoh ini kita dapat melihat bahwa byte pertama adalah
07
. Ini mewakili presisi (saya menggunakan skala 7 dan itulah yang ditampilkan di sini).Jika saya mengubah skala, kita dapat melihat bahwa byte pertama berubah agar sesuai dengan skala:
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)) SEBAGAI 'Panjang dalam Bytes';Hasil
+--------------------+-------------------+| Nilai | Panjang dalam Bytes ||--------------------+-------------------|| 0x034B8233023F480B | 8 |+--------------------+-------------------+Kita juga dapat melihat bahwa panjangnya berkurang.
Jadi dalam hal ini hasil kami sangat cocok dengan dokumentasi Microsoft – byte tambahan telah ditambahkan untuk presisi.
Banyak pengembang berasumsi bahwa ini adalah cara SQL Server menyimpan datetime2 nilai-nilai dalam database. Namun, anggapan itu tampaknya salah.
Data Tersimpan dalam Basis Data
Dalam contoh ini, saya membuat database yang berisi tabel dengan berbagai datetime2(n) kolom. Saya kemudian menggunakan
COL_LENGTH()
untuk mengembalikan panjang setiap kolom, dalam byte. Setelah itu, saya masukkan nilai ke dalamnya, sebelum menggunakanDBCC PAGE
untuk memeriksa ukuran penyimpanan yang setiap datetime2 nilai mengambil file halaman.Buat basis data:
CREATE DATABASE Test;Buat tabel:
Uji PENGGUNAAN;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) );Dalam hal ini saya membuat delapan kolom – satu untuk setiap skala yang ditentukan pengguna yang dapat kita gunakan dengan datetime2(n) .
Sekarang kita dapat memeriksa ukuran penyimpanan setiap kolom.
Panjang dalam Bytes menggunakan COL_LENGTH()
Gunakan
COL_LENGTH()
untuk memeriksa panjang (dalam byte) setiap kolom:SELECT COL_LENGTH ( 'Datetime2Test' , 'd0' ) AS 'd0', COL_LENGTH ( 'Datetime2Test' , 'd1' ) AS 'd1', COL_LENGTH ( 'Datetime2Test' , 'd2' ) AS ' ( 'Datetime2Test' , 'd3' ) AS 'd3', COL_LENGTH ( 'Datetime2Test' , 'd4' ) AS 'd4', COL_LENGTH ( 'Datetime2Test' , 'd5' ) SEBAGAI 'd5', COL_LENGTH 'd6' ) SEBAGAI 'd6', COL_LENGTH ( 'Datetime2Test' , 'd7' ) SEBAGAI 'd7';Hasil:
+------+------+------+------+------+------+---- --+------+| d0 | d1 | d2 | d3 | d4 | d5 | d6 | d7 ||------+------+------+------+------+------+----- -+------|| 6 | 6 | 6 | 7 | 7 | 8 | 8 | 8 |+------+------+------+------+------+------+----- -+------+Jadi sekali lagi, sepertinya kita tidak mendapatkan byte tambahan yang digunakan untuk menyimpan presisi.
Gunakan DBCC PAGE untuk Memeriksa Data yang Disimpan
Sekarang mari kita gunakan
DBCC PAGE
untuk menemukan ukuran penyimpanan sebenarnya dari data yang kami simpan di tabel ini.Pertama, mari kita masukkan beberapa data:
DECLARE @d datetime2(7) ='2025-05-21 10:15:30.1234567';INSERT INTO Datetime2Test ( d0, d1, d2, d3, d4, d5, d6, d7 )PILIH @d, @d , @d, @d, @d, @d, @d, @d;Sekarang pilih data (hanya untuk memeriksanya):
PILIH * DARI Datetime2Test;Hasil (menggunakan keluaran vertikal):
h0 | 2025-05-21 10:15:30d1 | 2025-05-21 10:15:30.1d2 | 25-05-21 10:15:30.12d3 | 2025-05-21 10:15:30.123d4 | 2025-05-21 10:15:30.1235d5 | 2025-05-21 10:15:30.12346d6 | 2025-05-21 10:15:30.123457d7 | 25-05-21 10:15:30.1234567Seperti yang diharapkan, nilai menggunakan presisi yang telah ditentukan sebelumnya di tingkat kolom.
Sekarang, sebelum kita menggunakan
DBCC PAGE()
, kita perlu tahu PagePID mana yang akan diteruskan. Kita dapat menggunakanDBCC IND()
untuk menemukannya.Temukan PagePID:
DBCC IND('Tes', 'dbo.Datetime2Test', 0);Hasil (menggunakan keluaran vertikal):
-[ REKAM 1 ]-------------------------HalamanFID | 1HalamanPID | 306IAMFID | NULLIAMPID | NULLObjectID | 1205579333IndexID | 0PartitionNumber | 1PartitionID | 72057594043039744iam_chain_type | Data dalam barisPageType | 10Tingkat Indeks | NULLHalaman BerikutnyaFID | 0HalamanBerikutnyaPID | 0PrevPageFID | 0PrevPagePID | 0-[ REKAM 2 ]-------------------------PageFID | 1HalamanPID | 360IAMFID | 1IAMPID | 306ObjectID | 1205579333IndexID | 0PartitionNumber | 1PartitionID | 72057594043039744iam_chain_type | Data dalam barisPageType | 1Tingkat Indeks | 0Halaman BerikutnyaFID | 0HalamanBerikutnyaPID | 0PrevPageFID | 0PrevPagePID | 0Ini mengembalikan dua catatan. Kami tertarik dengan PageType dari 1 (catatan ke-2). Kami ingin PagePID dari catatan itu. Dalam hal ini PagePID adalah 360 .
Sekarang kita dapat mengambil PagePID itu dan menggunakannya sebagai berikut:
DBCC TRACEON(3604, -1); DBCC PAGE(Uji, 1, 360, 3);Ini menghasilkan banyak data, tetapi kami terutama tertarik pada bagian berikut:
Slot 0 Kolom 1 Offset 0x4 Panjang 6 Panjang (fisik) 6d0 =2025-05-21 10:15:30 Slot 0 Kolom 2 Offset 0xa Panjang 6 Panjang (fisik) 6d1 =2025-05-21 10:15:30.1 Slot 0 Kolom 3 Offset 0x10 Panjang 6 Panjang (fisik) 6d2 =2025-05-21 10:15:30.12 Slot 0 Kolom 4 Offset 0x16 Panjang 7 Panjang (fisik) 7d3 =2025-05-21 10:15:30.123 Slot 0 Kolom 5 Offset 0x1d Panjang 7 Panjang (fisik) 7d4 =2025-05-21 10:15:30.1235 Slot 0 Kolom 6 Offset 0x24 Panjang 8 Panjang (fisik) 8d5 =2025-05-21 10:15:30.12346 Slot 0 Kolom 7 Offset 0x2c Panjang 8 Panjang (fisik) 8d6 =2025-05-21 10:15:30.123457 Slot 0 Kolom 8 Offset 0x34 Panjang 8 Panjang (fisik) 8d7 =2025-05-21 10:15:30.1234567Jadi tampaknya tidak menggunakan byte ekstra untuk presisi.
Tapi mari kita periksa data sebenarnya sebelum kita mencapai kesimpulan apa pun.
Data aktual disimpan di bagian file halaman ini:
Memory Dump @0x000000041883A060000000000000000000:10003c00 4290003f 480b95a2 053f480b d459383f ..<.B..?H.•¢.?H.ÔY8?0000000000000014:480b4b82 33023f48 0bf31603 163?f480b H.zå.Ü00000000000000028:003f480b c1f63499 083f480b 87a311fc 553f480b .?H.Áö4..?H.‡£.üU?H.000000000000003C:080000 ... ...Seperti yang Anda lihat, tidak ada satu pun yang terlihat seperti hasil yang akan kami peroleh dengan mengonversi datetime2 nilai ke varbinary . Tapi itu cukup dekat.
Begini tampilannya jika saya menghapus beberapa hal:
4290003f 480b95a2 053f480b d459383f480b4b82 33023f48 0bf31603 163f480b 7ae51edc003f480b c1f63499 083f480b 87a311fc 553f480bDigit heksagonal yang tersisa berisi semua data tanggal dan waktu kami, tetapi tidak presisi . Namun, kita harus mengatur ulang spasi untuk mendapatkan nilai sebenarnya untuk setiap baris.
Inilah hasil akhirnya. Saya telah menempatkan setiap nilai tanggal/waktu pada baris baru agar lebih mudah dibaca.
4290003f480b 95a2053f480b d459383f480b 4b8233023f480bf31603163f480b 7ae51edc003f480b c1f63499083f480b 87a311fc553f480bItu adalah nilai heksadesimal sebenarnya (dikurangi presisi ) yang akan kita dapatkan jika kita mengonversi datetime2 nilai ke varbinary . Yang pasti, mari kita lanjutkan dan lakukan hal itu:
SELECT CONVERT(VARBINARY(10), d0) SEBAGAI 'd0', CONVERT(VARBINARY(10), d1) SEBAGAI 'd1', CONVERT(VARBINARY(10), d2) SEBAGAI 'd2', CONVERT(VARBINARY( 10), d3) SEBAGAI 'd3', CONVERT(VARBINARY(10), d4) SEBAGAI 'd4', CONVERT(VARBINARY(10), d5) SEBAGAI 'd5', CONVERT(VARBINARY(10), d6) SEBAGAI 'd6 ', CONVERT(VARBINARY(10), d7) SEBAGAI 'd7'FROM Datetime2Test;Hasil (menggunakan keluaran vertikal):
h0 | 0x004290003F480Bd1 | 0x0195A2053F480Bd2 | 0x02D459383F480Bd3 | 0x034B8233023F480Bd4 | 0x04F31603163F480Bd5 | 0x057AE51EDC003F480Bd6 | 0x06C1F63499083F480Bd7 | 0x0787A311FC553F480BJadi kita mendapatkan hasil yang sama – kecuali bahwa itu telah didahului dengan presisi.
Namun untuk memperjelasnya, berikut adalah tabel yang membandingkan data file halaman sebenarnya dengan hasil
CONVERT()
operasi.
Data File Halaman | CONVERT() Data |
---|---|
4290003f480b | 004290003F480B |
95a2053f480b | 0195A2053F480B |
d459383f480b | 02D459383F480B |
4b8233023f480b | 034B8233023F480B |
f31603163f480b | 04F31603163F480B |
7ae51edc003f480b | 057AE51EDC003F480B |
c1f63499083f480b | 06C1F63499083F480B |
87a311fc553f480b | 0787A311FC553F480B |
Jadi kita dapat dengan jelas melihat bahwa file halaman tidak menyimpan presisi, tetapi hasil yang dikonversi menyimpannya.
Saya menyoroti bagian tanggal dan waktu yang sebenarnya dengan warna merah. Saya juga menghapus 0x
awalan dari hasil yang dikonversi, sehingga hanya data tanggal/waktu aktual yang ditampilkan (bersama dengan presisi).
Perhatikan juga bahwa heksadesimal tidak peka huruf besar/kecil, jadi fakta bahwa yang satu menggunakan huruf kecil dan yang lain menggunakan huruf besar tidak menjadi masalah.
Kesimpulan
Saat mengonversi datetime2 nilai ke varbinary , dibutuhkan byte ekstra untuk menyimpan presisi. Dibutuhkan presisi untuk menginterpretasikan porsi waktu (karena ini disimpan sebagai interval waktu, nilai eksaknya akan bergantung pada presisi).
Ketika disimpan dalam database, presisi ditentukan sekali di tingkat kolom. Ini akan tampak logis, karena tidak perlu menyimpan byte tambahan dengan setiap baris jika dapat ditentukan pada tingkat kolom. Jadi jika Anda menentukan, katakan, datetime2(7) di tingkat kolom, maka setiap baris akan menjadi datetime2(7) . Tidak perlu mengulangi ini di setiap baris.
Ronen Ariely mencapai kesimpulan yang sama dalam artikelnya yang disebutkan di atas.
Jika Anda memiliki sejuta baris dengan datetime2(7) menyimpan presisi dengan setiap baris akan membutuhkan 9.000.000 byte, dibandingkan dengan hanya 8.000.001 jika presisi disimpan sekali untuk seluruh kolom.
Ini juga memperkuat datetime2 kasus ketika membandingkannya dengan datetime . Bahkan saat menggunakan jumlah tempat desimal yang sama dengan datetime (yaitu 3), datetime2 tipe data menggunakan lebih sedikit penyimpanan (setidaknya jika disimpan dalam tabel dengan lebih dari satu baris). Dan ia melakukan ini dengan akurasi yang lebih tinggi. waktu kencan nilai menggunakan 8 byte, sedangkan datetime2(3) menggunakan 7 byte (ditambah 1 byte "presisi" yang digunakan bersama di semua baris).