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

Memahami Ukuran Penyimpanan 'waktu' di SQL Server

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

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..•¢.ÔY8K‚3.ó..
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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cara menemukan nama kolom untuk semua tabel di semua database di SQL Server

  2. String Format Tanggal/Waktu Standar Didukung oleh FORMAT() di SQL Server

  3. Cara Mengubah Susunan Level Server dari Menjalankan Instance SQL Server

  4. Permintaan di beberapa database di server yang sama

  5. Mengapa SQL Server mengabaikan ruang kosong di akhir secara otomatis?