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

Memahami Ukuran Penyimpanan 'datetimeoffset' di SQL Server

Dalam artikel ini saya melihat bagaimana datetimeoffset tipe data disimpan di SQL Server, dan bagaimana Anda bisa mendapatkan hasil ukuran penyimpanan yang dilaporkan berbeda, tergantung pada apa yang Anda lakukan dengannya.

Ini mirip dengan apa yang saya lakukan dengan datetime2 tipe data.

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()

Dokumentasi Microsoft

Dokumentasi resmi Microsoft tentang datetimeoffset tipe data menunjukkan bahwa ukuran penyimpanannya antara 8 dan 10 byte, tergantung pada presisi yang digunakan.

Mirip dengan datetime2(n) , Anda dapat menggunakan datetimeoffset(n) untuk menentukan presisi, di mana n adalah skala antara 0 dan 7.

Berikut data yang disajikan Microsoft untuk tipe data ini:

Skala yang ditentukan Hasil (presisi, skala) Panjang kolom (byte) Presisi pecahan detik
datetimeoffset (34,7) 10 7
datetimeoffset(0) (26,0) 8 0-2
datetimeoffset(1) (28,1) 8 0-2
datetimeoffset(2) (29,2) 8 0-2
datetimeoffset(3) (30,3) 9 3-4
datetimeoffset(4) (31,4) 9 3-4
datetimeoffset(5) (32,5) 10 5-7
datetimeoffset(6) (33,6) 10 5-7
datetimeoffset(7) (34,7) 10 5-7

Untuk keperluan artikel ini, saya terutama tertarik pada Panjang kolom (byte) kolom. Ini memberitahu kita berapa banyak byte yang digunakan untuk menyimpan tipe data ini dalam database.

Alasan utama saya ingin menulis artikel ini (dan menjalankan eksperimen di bawah), adalah karena dokumentasi Microsoft tidak menjelaskan bahwa byte tambahan digunakan untuk presisi (seperti halnya dalam dokumentasinya untuk datetime2 tipe data). Dalam dokumentasinya untuk datetime2 , menyatakan:

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.

Tetapi dokumentasi untuk datetimeoffset tidak menyertakan teks ini, dan juga waktu dokumentasi.

Ini membuat saya bertanya-tanya apakah ada perbedaan antara bagaimana tipe data ini menyimpan nilainya. Logika memberi tahu saya bahwa mereka harus bekerja sama, karena semuanya memiliki presisi yang ditentukan pengguna, tetapi saya ingin mencari tahu.

Jawaban singkatnya adalah ya, datetimeoffset tampaknya bekerja sama dengan datetime2 (berkenaan dengan byte tambahan), meskipun tidak didokumentasikan seperti itu.

Sisa artikel membahas berbagai contoh di mana saya mengembalikan ukuran penyimpanan datetimeoffset nilai dalam konteks yang berbeda.

Data Disimpan dalam Variabel

Mari kita simpan datetimeoffset 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 datetimeoffset(7) nilai:

DECLARE @d datetimeoffset(7);SET @d ='2025-05-21 10:15:30.1234567 +07:00';PILIH @d SEBAGAI 'Nilai', DATALENGTH(@d) SEBAGAI 'Panjang dalam Bytes ';

Hasil

+------------------------------------+--------- ----------+| Nilai | Panjang dalam Bytes ||------------------------------------+-------- -----------|| 2025-05-21 10:15:30.1234567 +07:00 | 10 |+------------------------------------+---------- ---------+

Nilai dalam contoh ini memiliki skala maksimum 7 (karena saya mendeklarasikan variabel sebagai datetimeoffset(7) ), dan mengembalikan panjang 10 byte.

Tidak ada kejutan di sini, ini adalah ukuran penyimpanan persis yang ditunjukkan oleh dokumentasi Microsoft.

Namun, jika kita mengonversi nilainya menjadi varbinary kita mendapatkan hasil yang berbeda.

Panjang dalam Bytes setelah Mengonversi ke 'varbinary'

Beberapa pengembang suka mengonversi datetimeoffset dan datetime2 variabel ke varbinary , karena ini lebih mewakili cara SQL Server menyimpannya di database. Meskipun ini sebagian benar, hasilnya tidak persis sama dengan nilai yang disimpan (seperti yang akan Anda lihat nanti).

Inilah yang terjadi jika kita mengonversi datetimeoffset nilai ke varbinary :

DECLARE @d datetimeoffset(7);SET @d ='2025-05-21 10:15:30.1234567 +07:00';SELECT CONVERT(VARBINARY(16), @d) SEBAGAI 'Nilai', DATALENGTH( CONVERT(VARBINARY(16), @d)) SEBAGAI 'Panjang dalam Bytes';

Hasil

+--------------------------+------------------- +| Nilai | Panjang dalam Bytes ||--------------------------+------------------ -|| 0x0787CBB24F1B3F480BA401 | 11 |+--------------------------+-------------------+ 

Dalam hal ini kita mendapatkan 11 byte.

Ini adalah representasi heksadesimal dari datetimeoffset nilai. Nilai offset waktu tanggal aktual (dan presisinya) adalah segalanya setelah 0x . Setiap pasangan karakter hex adalah satu byte. Ada 11 pasang, dan karenanya 11 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 datetimeoffset(3);SET @d ='2025-05-21 10:15:30.1234567 +07:00';SELECT CONVERT(VARBINARY(16), @d) SEBAGAI 'Nilai', DATALENGTH( CONVERT(VARBINARY(16), @d)) SEBAGAI 'Panjang dalam Bytes';

Hasil

+-------------------------+-------------------+| Nilai | Panjang dalam Bytes ||------------------------+-------------------| | 0x03CBFCB2003F480BA401 | 10 |+-------------------------+-------------------+ 

Kita juga dapat melihat bahwa panjangnya berkurang.

Data Tersimpan dalam Basis Data

Dalam contoh ini, saya membuat database dengan berbagai datetimeoffset(n) kolom, lalu gunakan COL_LENGTH() untuk mengembalikan panjang setiap kolom, dalam byte. Saya kemudian memasukkan nilai ke dalam kolom, sebelum menggunakan DBCC PAGE untuk memeriksa ukuran penyimpanan yang setiap datetimeoffset nilai mengambil file halaman.

Buat basis data:

CREATE DATABASE Test;

Buat tabel:

Uji PENGGUNAAN;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) );

Dalam hal ini saya membuat delapan kolom – satu untuk setiap skala yang ditentukan pengguna yang dapat kita gunakan dengan datetimeoffset(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 ( 'DatetimeoffsetTest' , 'd0' ) AS 'd0', COL_LENGTH ( 'DatetimeoffsetTest' , 'd1' ) AS 'd1', COL_LENGTH ( 'DatetimeoffsetTest' , 'd2' ) ( 'DatetimeoffsetTest' , 'd3' ) AS 'd3', COL_LENGTH ( 'DatetimeoffsetTest' , 'd4' ) AS 'd4', COL_LENGTH ( 'DatetimeoffsetTest' , 'd5' ) SEBAGAI 'd5', COL_LENGTH ( 'DatetimeoffsetTest' , 'd5' ) SEBAGAI 'd5', COL_LENGTH 'd6' ) SEBAGAI 'd6', COL_LENGTH ( 'DatetimeoffsetTest' , 'd7' ) SEBAGAI 'd7'; 

Hasil:

+------+------+------+------+------+------+---- --+------+| d0 | d1 | d2 | d3 | d4 | d5 | d6 | d7 ||------+------+------+------+------+------+----- -+------|| 8 | 8 | 8 | 9 | 9 | 10 | 10 | 10 |+------+------+------+------+------+------+----- -+------+

Jadi sekali lagi, kita mendapatkan hasil yang sama dengan status dokumentasi yang akan kita dapatkan. Hal ini diharapkan, karena dokumentasi secara eksplisit menyatakan “Panjang kolom (byte)”, yang persis seperti yang kami ukur di sini.

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 datetimeoffset(7) ='2025-05-21 10:15:30.1234567 +07:00';INSERT INTO DatetimeoffsetTest ( d0, d1, d2, d3, d4, d5, d6, d7 )PILIH @ d, @d, @d, @d, @d, @d, @d, @d;

Sekarang pilih data (hanya untuk memeriksanya):

SELECT * FROM DatetimeoffsetTest;

Hasil (menggunakan keluaran vertikal):

h0 | 2025-05-21 10:15:30.0000000 +07:00d1 | 2025-05-21 10:15:30.1000000 +07:00d2 | 2025-05-21 10:15:30.1200000 +07:00d3 | 2025-05-21 10:15:30.1230000 +07:00d4 | 2025-05-21 10:15:30.1235000 +07:00d5 | 2025-05-21 10:15:30.1234600 +07:00h6 | 2025-05-21 10:15:30.1234570 +07:00h7 | 2025-05-21 10:15:30.1234567 +07:00

Seperti yang diharapkan, nilai menggunakan presisi yang telah ditentukan sebelumnya di tingkat kolom.

Perhatikan bahwa sistem saya menampilkan angka nol. Anda mungkin atau mungkin tidak melakukannya. Bagaimanapun, ini tidak mempengaruhi presisi atau akurasi yang sebenarnya.

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('Test', 'dbo.DatetimeoffsetTest', 0);

Hasil (menggunakan keluaran vertikal):

-[ REKAM 1 ]-------------------------HalamanFID | 1HalamanPID | 307IAMFID | NULLIAMPID | NULLObjectID | 1525580473IndexID | 0PartitionNumber | 1PartitionID | 72057594043170816iam_chain_type | Data dalam barisPageType | 10Tingkat Indeks | NULLHalaman BerikutnyaFID | 0HalamanBerikutnyaPID | 0PrevPageFID | 0PrevPagePID | 0-[ REKAM 2 ]-------------------------PageFID | 1HalamanPID | 376IAMFID | 1IAMPID | 307ObjectID | 1525580473IndexID | 0PartitionNumber | 1PartitionID | 72057594043170816iam_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 376 .

Sekarang kita dapat mengambil PagePID itu dan menggunakannya sebagai berikut:

DBCC TRACEON(3604, -1); DBCC PAGE(Uji, 1, 376, 3);

Saat ini kami terutama tertarik pada bagian berikut:

Slot 0 Kolom 1 Offset 0x4 Panjang 8 Panjang (fisik) 8d0 =2025-05-21 10:15:30 +07:00 Slot 0 Kolom 2 Offset 0xc Panjang 8 Panjang (fisik) 8d1 =2025-05-21 10:15:30.1 +07:00 Slot 0 Kolom 3 Offset 0x14 Panjang 8 Panjang (fisik) 8d2 =2025-05-21 10:15:30.12 +07:00 Slot 0 Kolom 4 Offset 0x1c Panjang 9 Panjang (fisik) 9d3 =2025-05-21 10:15:30.123 +07:00 Slot 0 Kolom 5 Offset 0x25 Panjang 9 Panjang (fisik) 9d4 =2025-05-21 10:15:30.1235 +07:00Slot 0 Kolom 6 Offset 0x2e Panjang 10 Panjang (fisik) 10d5 =2025-05-21 10:15:30.12346 +07:00 Slot 0 Kolom 7 Offset 0x38 Panjang 10 Panjang (fisik) 10d6 =2025-05-21 10:15:30.123457 +07:00 Slot 0 Kolom 8 Offset 0x42 Panjang 10 Panjang (fisik) 10d7 =2025-05-21 10:15:30.1234567 +07:00 

Jadi kita mendapatkan hasil yang sama lagi. Persis seperti yang dinyatakan dalam dokumentasi.

Sementara kita di sini, mari kita periksa data – nilai tanggal/waktu aktual saat disimpan di SQL Server.

Nilai sebenarnya disimpan di bagian file halaman ini:

Memory Dump @0x000000041951A06000000000000000000:10004c00 d22d003f 480ba401 35ca013f 480ba401 ..L.Ò-.?H.¤.5Ê.?H.¤.0000000000000014:14e6113f 480ba401 cbfcb200 3f3480ba? H.¤.óßý000000000000000028:063f480b a4017abf ea45003f 480ba401 c17a2bbb .?H.¤.z¿êE.?H.¤.Áz+»000000000000003C:023f480b a40187cb b24f1b3f 480ba401 080000 .?H.¤. ..

Itu masih termasuk beberapa bit tambahan. Mari kita hapus beberapa hal, sehingga hanya nilai tanggal dan waktu kita yang tersisa:

d22d003f 480ba401 35ca013f 480ba40114e6113f 480ba401 cbfcb200 3f480ba4 01f3dffd063f480b a4017abf ea45003f 480ba401 c17a2bbb023f480b a40187cb b24ba401b 

Digit heksagonal yang tersisa berisi semua data tanggal dan waktu kami, tetapi tidak presisi . Namun, mereka disusun menjadi potongan 4 byte, jadi kita harus mengatur ulang spasi untuk mendapatkan nilai individual.

Inilah hasil akhirnya. Saya telah menempatkan setiap nilai tanggal/waktu pada baris baru agar lebih mudah dibaca.

d22d003f480ba401 35ca013f480ba40114e6113f480ba401 cbfcb2003f480ba401f3dffd063f480ba4017abfea45003f480ba401 c17a2bbb023f480ba40187cbb24f1b3f480ba401

Itu adalah nilai heksadesimal sebenarnya (dikurangi presisi ) yang akan kita dapatkan jika kita mengonversi datetimeoffset nilai ke varbinary . Seperti ini:

SELECT CONVERT(VARBINARY(16), d0) SEBAGAI 'd0', CONVERT(VARBINARY(16), d1) SEBAGAI 'd1', CONVERT(VARBINARY(16), d2) SEBAGAI 'd2', CONVERT(VARBINARY( 16), d3) SEBAGAI 'd3', CONVERT(VARBINARY(16), d4) SEBAGAI 'd4', CONVERT(VARBINARY(16), d5) SEBAGAI 'd5', CONVERT(VARBINARY(16), d6) SEBAGAI 'd6 ', CONVERT(VARBINARY(16), d7) SEBAGAI 'd7'FROM DatetimeoffsetTest;

Hasil (menggunakan keluaran vertikal):

h0 | 0x00D22D003F480BA401d1 | 0x0135CA013F480BA401d2 | 0x0214E6113F480BA401d3 | 0x03CBFCB2003F480BA401d4 | 0x04F3DFFD063F480BA401d5 | 0x057ABFEA45003F480BA401d6 | 0x06C17A2BBB023F480BA401d7 | 0x0787CBB24F1B3F480BA401

Jadi kita mendapatkan hasil yang sama – kecuali bahwa itu telah didahului dengan presisi.

Berikut adalah tabel yang membandingkan data file halaman sebenarnya dengan hasil CONVERT() operasi.

Data File Halaman CONVERT() Data
d22d003f480ba401 00D22D003F480BA401
35ca013f480ba401 0135CA013F480BA401
14e6113f480ba401 0214E6113F480BA401
cbfcb2003f480ba401 03CBFCB2003F480BA401
f3dffd063f480ba401 04F3DFFD063F480BA401
7abfea45003f480ba401 057ABFEA45003F480BA401
c17a2bbb023f480ba401 06C17A2BBB023F480BA401
87cbb24f1b3f480ba401 0787CBB24F1B3F480BA401

Jadi kita dapat 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 datetimeoffset 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 tampaknya logis, karena tidak perlu menambahkan presisi ke setiap baris saat semua baris menggunakan presisi yang sama. Itu akan membutuhkan byte tambahan untuk setiap baris, yang tidak perlu meningkatkan persyaratan penyimpanan.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Validasi Email TSQL (tanpa ekspresi reguler)

  2. Urutan sebagai nilai default untuk kolom

  3. Bagaimana cara mengeluarkan DateTime yang dapat dibatalkan dari database

  4. Tutorial Basis Data SQL Server (MSSQL DBA) untuk Administrator Basis Data Pemula

  5. sql server pilih baris pertama dari grup