Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Memahami Ukuran Penyimpanan 'datetime2' di SQL Server

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
  • Data disimpan dalam database
    • Panjang dalam byte menggunakan COL_LENGTH()
    • Panjang dalam byte menggunakan DBCC PAGE()

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 menggunakan DATALENGTH() 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 menggunakan DBCC 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.1234567

Seperti 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 menggunakan DBCC 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 | 0

Ini 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.1234567 

Jadi 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 553f480b

Digit 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 87a311fc553f480b

Itu 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 | 0x0787A311FC553F480B

Jadi 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).


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. OPSI (REKOMPILASI) Selalu Lebih Cepat; Mengapa?

  2. Apa metode terbaik untuk meneruskan parameter ke SQLCommand?

  3. Apakah urutan Sql JOIN mempengaruhi kinerja?

  4. Pemulihan SQL Server 2017

  5. Apa tujuan menggunakan OPTION(MAXDOP 1) di SQL Server?