Dalam artikel ini saya melihat ukuran penyimpanan waktu tipe data di SQL Server.
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
Dokumentasi Microsoft
Dokumentasi resmi Microsoft pada waktu tipe data menunjukkan bahwa ukuran penyimpanannya antara 3 dan 5 byte, tergantung pada presisi yang digunakan.
Tipe data ini memungkinkan presisi yang ditentukan pengguna. Anda dapat menggunakan waktu(n) untuk menentukan presisi, di mana n adalah skala antara 0 dan 7.
Berikut data yang disajikan Microsoft untuk waktu tipe data:
Skala yang ditentukan | Hasil (presisi, skala) | Panjang kolom (byte) | Presisi pecahan detik |
---|---|---|---|
waktu | (16,7) | 5 | 7 |
waktu(0) | (8,0) | 3 | 0-2 |
waktu(1) | (10,1) | 3 | 0-2 |
waktu(2) | (11,2) | 3 | 0-2 |
waktu(3) | (12,3) | 4 | 3-4 |
waktu(4) | (13,4) | 4 | 3-4 |
waktu(5) | (14,5) | 5 | 5-7 |
waktu(6) | (15,6) | 5 | 5-7 |
waktu(7) | (16,7) | 5 | 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.
Dari sudut pandang pengguna, waktu tipe data bekerja dengan cara yang sama seperti bagian waktu datetime2 . Ini memiliki presisi pecahan detik yang ditentukan pengguna, dan menerima skala 0 hingga 7.
Sisa artikel ini membahas berbagai contoh di mana saya mengembalikan ukuran penyimpanan waktu nilai dalam konteks yang berbeda.
Data Disimpan dalam Variabel
Pertama, saya akan menyimpan waktu 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 waktu(7) nilai:
DECLARE @t time(7); SET @t = '10:15:30.1234567'; SELECT @t AS 'Value', DATALENGTH(@t) AS 'Length in Bytes';
Hasil
+------------------+-------------------+ | Value | Length in Bytes | |------------------+-------------------| | 10:15:30.1234567 | 5 | +------------------+-------------------+
Nilai dalam contoh ini memiliki skala maksimum 7 (karena saya mendeklarasikan variabel sebagai waktu(7) ), dan mengembalikan panjang 5 byte.
Hal ini diharapkan, karena cocok dengan ukuran penyimpanan yang diuraikan dalam tabel Microsoft.
Namun, jika kita mengonversi nilainya menjadi varbinary kita mendapatkan hasil yang berbeda.
Panjang dalam Bytes setelah Mengonversi ke 'varbinary'
Beberapa pengembang suka mengonversi waktu atau datetime2 variabel ke varbinary karena lebih mewakili bagaimana SQL Server menyimpannya di database. Meskipun ini sebagian benar, hasilnya tidak persis sama dengan nilai yang disimpan (lebih lanjut tentang itu di bawah).
Inilah yang terjadi jika kita mengonversi waktu our nilai ke varbinary :
DECLARE @t time(7); SET @t = '10:15:30.1234567'; SELECT CONVERT(VARBINARY(16), @t) AS 'Value', DATALENGTH(CONVERT(VARBINARY(16), @t)) AS 'Length in Bytes';
Hasil
+----------------+-------------------+ | Value | Length in Bytes | |----------------+-------------------| | 0x0787A311FC55 | 6 | +----------------+-------------------+
Dalam hal ini kita mendapatkan 6 byte. Nilai kami sekarang menggunakan 1 byte lebih banyak dari yang dinyatakan dalam dokumentasi.
Itu karena membutuhkan byte ekstra untuk menyimpan presisi.
Ini adalah representasi heksadesimal dari waktu nilai. Nilai waktu sebenarnya (dan presisinya) adalah segalanya setelah 0x
. Setiap pasangan karakter hex adalah satu byte. Ada 6 pasang, dan karena itu 6 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 @t time(3); SET @t = '10:15:30.1234567'; SELECT CONVERT(VARBINARY(16), @t) AS 'Value', DATALENGTH(CONVERT(VARBINARY(16), @t)) AS 'Length in Bytes';
Hasil
+--------------+-------------------+ | Value | Length in Bytes | |--------------+-------------------| | 0x034B823302 | 5 | +--------------+-------------------+
Kita juga dapat melihat bahwa panjangnya berkurang. Namun sekali lagi, ini satu byte lebih banyak dari yang dikatakan dokumentasi yang seharusnya digunakan.
Meskipun dokumentasi Microsoft untuk waktu tidak secara eksplisit menyebutkan ini, dokumentasi untuk datetime2 menyatakan sebagai 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.
Dan datetime2 tipe data bekerja dengan cara yang persis sama dengan contoh di atas. Dengan kata lain, itu hanya melaporkan byte ekstra ketika dikonversi ke varbinary .
Jadi byte tambahan yang disebutkan dalam dokumentasi Microsoft tampaknya juga berlaku untuk waktu .
Namun, ukuran penyimpanan sebenarnya dari waktu your Anda nilai akan di mana data disimpan.
Data Tersimpan dalam Basis Data
Ketika kolom database memiliki tipe waktu , presisinya ditentukan pada tingkat kolom – bukan di tingkat datanya. Dengan kata lain, ini ditentukan sekali untuk seluruh kolom. Ini masuk akal, karena ketika Anda mendefinisikan kolom sebagai waktu(7) , Anda tahu bahwa semua baris adalah waktu(7) . Tidak perlu menggunakan byte berharga yang menyatakan kembali fakta itu di setiap baris.
Saat Anda memeriksa waktu nilai seperti yang disimpan di SQL Server, Anda akan melihat bahwa itu sama dengan varbinary hasil, tapi tanpa presisi.
Di bawah ini adalah contoh yang menunjukkan bagaimana waktu nilai disimpan di SQL Server.
Dalam contoh ini, saya membuat database dengan berbagai waktu(n) kolom, lalu gunakan COL_LENGTH()
untuk mengembalikan panjang setiap kolom, dalam byte. Saya kemudian memasukkan nilai ke dalam kolom tersebut, sebelum menggunakan DBCC PAGE
untuk memeriksa ukuran penyimpanan yang setiap waktu nilai mengambil file halaman.
Buat basis data:
CREATE DATABASE Test;
Buat tabel:
USE Test; CREATE TABLE TimeTest ( t0 time(0), t1 time(1), t2 time(2), t3 time(3), t4 time(4), t5 time(5), t6 time(6), t7 time(7) );
Dalam hal ini saya membuat delapan kolom – satu untuk setiap skala yang ditentukan pengguna yang dapat kita gunakan dengan waktu(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 ( 'TimeTest' , 't0' ) AS 't0', COL_LENGTH ( 'TimeTest' , 't1' ) AS 't1', COL_LENGTH ( 'TimeTest' , 't2' ) AS 't2', COL_LENGTH ( 'TimeTest' , 't3' ) AS 't3', COL_LENGTH ( 'TimeTest' , 't4' ) AS 't4', COL_LENGTH ( 'TimeTest' , 't5' ) AS 't5', COL_LENGTH ( 'TimeTest' , 't6' ) AS 't6', COL_LENGTH ( 'TimeTest' , 't7' ) AS 't7';
Hasil:
+------+------+------+------+------+------+------+------+ | t0 | t1 | t2 | t3 | t4 | t5 | t6 | t7 | |------+------+------+------+------+------+------+------| | 3 | 3 | 3 | 4 | 4 | 5 | 5 | 5 | +------+------+------+------+------+------+------+------+
Jadi sekali lagi, kita mendapatkan hasil yang sama dengan yang dinyatakan oleh dokumentasi yang akan kita dapatkan. Hal ini diharapkan, karena dokumentasi secara eksplisit menyatakan “Panjang kolom (byte)”, yang persis seperti yang kami ukur di sini.
Ingat, ini sebelum kami memasukkan data apa pun. Kolom itu sendiri menentukan presisi (dan karena itu ukuran penyimpanan) dari setiap data yang dimasukkan – bukan sebaliknya.
Gunakan DBCC PAGE untuk Memeriksa Data yang Disimpan
Sekarang mari kita masukkan data, lalu gunakan DBCC PAGE
untuk menemukan ukuran penyimpanan sebenarnya dari data yang kita simpan di setiap kolom.
Masukkan data:
DECLARE @t time(7) = '10:15:30.1234567'; INSERT INTO TimeTest ( t0, t1, t2, t3, t4, t5, t6, t7 ) SELECT @t, @t, @t, @t, @t, @t, @t, @t;
Sekarang pilih data (hanya untuk memeriksanya):
SELECT * FROM TimeTest;
Hasil (menggunakan keluaran vertikal):
t0 | 10:15:30 t1 | 10:15:30.1000000 t2 | 10:15:30.1200000 t3 | 10:15:30.1230000 t4 | 10:15:30.1235000 t5 | 10:15:30.1234600 t6 | 10:15:30.1234570 t7 | 10:15:30.1234567
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.TimeTest', 0);
Hasil (menggunakan keluaran vertikal):
-[ RECORD 1 ]------------------------- PageFID | 1 PagePID | 308 IAMFID | NULL IAMPID | NULL ObjectID | 1541580530 IndexID | 0 PartitionNumber | 1 PartitionID | 72057594043236352 iam_chain_type | In-row data PageType | 10 IndexLevel | NULL NextPageFID | 0 NextPagePID | 0 PrevPageFID | 0 PrevPagePID | 0 -[ RECORD 2 ]------------------------- PageFID | 1 PagePID | 384 IAMFID | 1 IAMPID | 308 ObjectID | 1541580530 IndexID | 0 PartitionNumber | 1 PartitionID | 72057594043236352 iam_chain_type | In-row data PageType | 1 IndexLevel | 0 NextPageFID | 0 NextPagePID | 0 PrevPageFID | 0 PrevPagePID | 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 384 .
Sekarang kita dapat mengambil PagePID itu dan menggunakannya sebagai berikut:
DBCC TRACEON(3604, -1); DBCC PAGE(Test, 1, 384, 3);
Saat ini kami terutama tertarik pada bagian berikut:
Slot 0 Column 1 Offset 0x4 Length 3 Length (physical) 3 t0 = 10:15:30 Slot 0 Column 2 Offset 0x7 Length 3 Length (physical) 3 t1 = 10:15:30.1 Slot 0 Column 3 Offset 0xa Length 3 Length (physical) 3 t2 = 10:15:30.12 Slot 0 Column 4 Offset 0xd Length 4 Length (physical) 4 t3 = 10:15:30.123 Slot 0 Column 5 Offset 0x11 Length 4 Length (physical) 4 t4 = 10:15:30.1235 Slot 0 Column 6 Offset 0x15 Length 5 Length (physical) 5 t5 = 10:15:30.12346 Slot 0 Column 7 Offset 0x1a Length 5 Length (physical) 5 t6 = 10:15:30.123457 Slot 0 Column 8 Offset 0x1f Length 5 Length (physical) 5 t7 = 10:15:30.1234567
Jadi kami mendapatkan hasil yang sama dengan status dokumentasi. Ini menunjukkan bahwa presisi tidak disimpan dengan nilai.
Kami dapat mengonfirmasinya dengan memeriksa data sebenarnya.
Nilai waktu sebenarnya disimpan di bagian file halaman ini:
Memory Dump @0x0000000423ADA060 0000000000000000: 10002400 42900095 a205d459 384b8233 02f31603 ..$.B..¢.ÔY8K3.ó.. 0000000000000014: 167ae51e dc00c1f6 34990887 a311fc55 080000 .zå.Ü.Áö4..£.üU...
Kita dapat mengekstrak nilai waktu aktual dengan menghapus beberapa hal. Setelah dihapus, berikut ini akan tetap ada:
42900095 a205d459 384b8233 02f31603 167ae51e dc00c1f6 34990887 a311fc55
Digit heksagonal ini berisi semua data 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.
429000 95a205 d45938 4b823302 f3160316 7ae51edc00 c1f6349908 87a311fc55
Itu adalah nilai heksadesimal sebenarnya (dikurangi presisi ) yang akan kita dapatkan jika kita mengonversi waktu nilai ke varbinary . Seperti ini:
SELECT CONVERT(VARBINARY(16), t0) AS 't0', CONVERT(VARBINARY(16), t1) AS 't1', CONVERT(VARBINARY(16), t2) AS 't2', CONVERT(VARBINARY(16), t3) AS 't3', CONVERT(VARBINARY(16), t4) AS 't4', CONVERT(VARBINARY(16), t5) AS 't5', CONVERT(VARBINARY(16), t6) AS 't6', CONVERT(VARBINARY(16), t7) AS 't7' FROM TimeTest;
Hasil (menggunakan keluaran vertikal):
t0 | 0x00429000 t1 | 0x0195A205 t2 | 0x02D45938 t3 | 0x034B823302 t4 | 0x04F3160316 t5 | 0x057AE51EDC00 t6 | 0x06C1F6349908 t7 | 0x0787A311FC55
Kueri tersebut menghasilkan hasil yang sama – kecuali bahwa setiap nilai telah didahului dengan presisi.
Berikut adalah tabel yang membandingkan data file halaman sebenarnya dengan hasil CONVERT()
operasi.
Data File Halaman | CONVERT() Data |
---|---|
429.000 | 00429.000 |
95a205 | 0195A205 |
d45938 | 02D45938 |
4b823302 | 034B823302 |
f3160316 | 04F3160316 |
7ae51edc00 | 057AE51EDC00 |
c1f6349908 | 06C1F6349908 |
87a311fc55 | 0787A311FC55 |
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 waktu 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 memiliki presisi yang sama. Itu akan membutuhkan byte tambahan untuk setiap baris, yang tidak perlu meningkatkan persyaratan penyimpanan.