Trong bài viết này, tôi xem xét kích thước bộ nhớ của thời gian kiểu dữ liệu trong SQL Server.
Cụ thể, tôi xem xét những điều sau:
- Tài liệu của Microsoft
- Dữ liệu được lưu trữ trong một biến
- Độ dài tính bằng byte sử dụng
DATALENGTH()
- Độ dài tính bằng byte sử dụng
DATALENGTH()
sau khi chuyển đổi thành varbinary
- Độ dài tính bằng byte sử dụng
- Dữ liệu được lưu trữ trong cơ sở dữ liệu
- Độ dài tính bằng byte sử dụng
COL_LENGTH()
- Độ dài tính bằng byte sử dụng
DBCC PAGE()
- Độ dài tính bằng byte sử dụng
Tài liệu của Microsoft
Tài liệu chính thức của Microsoft về thời gian kiểu dữ liệu cho biết kích thước lưu trữ của nó là từ 3 đến 5 byte, tùy thuộc vào độ chính xác đang được sử dụng.
Kiểu dữ liệu này cho phép độ chính xác do người dùng xác định. Bạn có thể sử dụng time (n) để chỉ định độ chính xác, trong đó n là thang điểm từ 0 đến 7.
Đây là dữ liệu mà Microsoft trình bày trong thời gian kiểu dữ liệu:
Thang đo cụ thể | Kết quả (độ chính xác, tỷ lệ) | Độ dài cột (byte) | Độ chính xác của phân số giây |
---|---|---|---|
time | (16,7) | 5 | 7 |
thời gian (0) | (8,0) | 3 | 0-2 |
thời gian (1) | (10,1) | 3 | 0-2 |
thời gian (2) | (11,2) | 3 | 0-2 |
thời gian (3) | (12,3) | 4 | 3-4 |
thời gian (4) | (13,4) | 4 | 3-4 |
thời gian (5) | (14,5) | 5 | 5-7 |
thời gian (6) | (15,6) | 5 | 5-7 |
thời gian (7) | (16,7) | 5 | 5-7 |
Đối với mục đích của bài viết này, tôi chủ yếu quan tâm đến Độ dài cột (byte) cột. Điều này cho chúng ta biết có bao nhiêu byte được sử dụng để lưu trữ kiểu dữ liệu này trong cơ sở dữ liệu.
Từ quan điểm của người dùng, thời gian kiểu dữ liệu hoạt động giống như phần thời gian của datetime2 . Nó có độ chính xác theo phân số giây do người dùng xác định và nó chấp nhận thang điểm từ 0 đến 7.
Phần còn lại của bài viết này chạy qua các ví dụ khác nhau trong đó tôi trả về kích thước bộ nhớ là thời gian giá trị trong các ngữ cảnh khác nhau.
Dữ liệu được lưu trữ trong một biến
Đầu tiên, tôi sẽ lưu trữ một thời gian giá trị trong một biến và kiểm tra kích thước lưu trữ của nó. Sau đó, tôi sẽ chuyển đổi giá trị đó thành varbinary và kiểm tra lại.
Độ dài tính bằng byte sử dụng DATALENGTH
Đây là điều sẽ xảy ra nếu chúng tôi sử dụng DATALENGTH()
hàm để trả về số byte được sử dụng trong một thời gian (7) giá trị:
DECLARE @t time(7); SET @t = '10:15:30.1234567'; SELECT @t AS 'Value', DATALENGTH(@t) AS 'Length in Bytes';
Kết quả
+------------------+-------------------+ | Value | Length in Bytes | |------------------+-------------------| | 10:15:30.1234567 | 5 | +------------------+-------------------+
Giá trị trong ví dụ này có tỷ lệ tối đa là 7 (vì tôi khai báo biến là time (7) ), và nó trả về độ dài 5 byte.
Điều này được mong đợi, vì nó phù hợp với kích thước bộ nhớ được nêu trong bảng của Microsoft.
Tuy nhiên, nếu chúng tôi chuyển đổi giá trị thành varbinary chúng tôi nhận được một kết quả khác.
Độ dài tính bằng byte sau khi chuyển đổi thành 'varbinary'
Một số nhà phát triển muốn chuyển đổi thời gian hoặc datetime2 biến thành varbinary vì nó đại diện hơn về cách SQL Server lưu trữ nó trong cơ sở dữ liệu. Mặc dù điều này đúng một phần, nhưng kết quả không hoàn toàn giống với giá trị được lưu trữ (thêm thông tin về điều đó bên dưới).
Dưới đây là những gì sẽ xảy ra nếu chúng tôi chuyển đổi thời gian của mình giá trị thành 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';
Kết quả
+----------------+-------------------+ | Value | Length in Bytes | |----------------+-------------------| | 0x0787A311FC55 | 6 | +----------------+-------------------+
Trong trường hợp này, chúng tôi nhận được 6 byte. Giá trị của chúng tôi hiện sử dụng nhiều hơn 1 byte so với giá trị được nêu trong tài liệu.
Đó là bởi vì nó cần thêm một byte để lưu trữ độ chính xác.
Đây là đại diện hệ thập lục phân của thời gian giá trị. Giá trị thời gian thực tế (và độ chính xác của nó) là mọi thứ sau 0x
. Mỗi cặp ký tự hex là một byte. Có 6 cặp, và do đó 6 byte. Điều này được xác nhận khi chúng tôi sử dụng DATALENGTH()
để trả về độ dài tính bằng byte.
Trong ví dụ này, chúng ta có thể thấy rằng byte đầu tiên là 07
. Điều này thể hiện độ chính xác (tôi đã sử dụng thang điểm 7 và vì vậy đó là những gì được hiển thị ở đây).
Nếu tôi thay đổi tỷ lệ, chúng ta có thể thấy rằng byte đầu tiên sẽ thay đổi để phù hợp với tỷ lệ:
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';
Kết quả
+--------------+-------------------+ | Value | Length in Bytes | |--------------+-------------------| | 0x034B823302 | 5 | +--------------+-------------------+
Chúng ta cũng có thể thấy rằng chiều dài được giảm tương ứng. Nhưng một lần nữa, nó nhiều hơn một byte so với những gì tài liệu cho biết nó nên sử dụng.
Mặc dù tài liệu của Microsoft về thời gian không đề cập rõ ràng điều này, tài liệu cho datetime2 hiện như sau:
Byte đầu tiên của datetime2 value lưu trữ độ chính xác của giá trị, có nghĩa là bộ nhớ thực tế cần thiết cho datetime2 giá trị là kích thước lưu trữ được chỉ ra trong bảng trên cộng với 1 byte bổ sung để lưu trữ độ chính xác. Điều này làm cho kích thước tối đa của datetime2 giá trị 9 byte - 1 byte lưu trữ độ chính xác cộng với 8 byte để lưu trữ dữ liệu ở độ chính xác tối đa.
Và datetime2 kiểu dữ liệu hoạt động theo cùng một cách đối với các ví dụ trên. Nói cách khác, nó chỉ báo cáo byte thừa khi nó được chuyển đổi thành varbinary .
Vì vậy, byte bổ sung được đề cập trong tài liệu của Microsoft dường như cũng áp dụng cho thời gian .
Tuy nhiên, kích thước bộ nhớ thực tế trong thời gian của bạn giá trị sẽ ở nơi dữ liệu được lưu trữ.
Dữ liệu được lưu trữ trong cơ sở dữ liệu
Khi cột cơ sở dữ liệu có loại thời gian , độ chính xác của nó được chỉ định ở cấp cột - không ở cấp độ dữ liệu. Nói cách khác, nó được chỉ định một lần cho toàn bộ cột. Điều này có ý nghĩa, bởi vì khi bạn xác định một cột là thời gian (7) , bạn biết rằng tất cả các hàng sẽ là thời gian (7) . Không cần sử dụng hết các byte quý giá để trình bày lại dữ kiện đó trên mỗi hàng.
Khi bạn kiểm tra thời gian vì nó được lưu trữ trong SQL Server, bạn sẽ thấy rằng nó giống với varbinary kết quả, nhưng không có độ chính xác.
Dưới đây là các ví dụ cho thấy cách thời gian giá trị được lưu trữ trong SQL Server.
Trong các ví dụ này, tôi tạo cơ sở dữ liệu với time (n) khác nhau và sau đó sử dụng COL_LENGTH()
để trả về độ dài của mỗi cột, tính bằng byte. Sau đó, tôi chèn các giá trị vào các cột đó, trước khi sử dụng DBCC PAGE
để kiểm tra kích thước bộ nhớ mà mỗi lần giá trị chiếm trên tệp trang.
Tạo cơ sở dữ liệu:
CREATE DATABASE Test;
Tạo bảng:
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) );
Trong trường hợp này, tôi tạo tám cột - một cột cho mỗi thang đo do người dùng xác định mà chúng tôi có thể sử dụng với time (n) .
Bây giờ chúng ta có thể kiểm tra kích thước lưu trữ của mỗi cột.
Độ dài tính bằng byte sử dụng COL_LENGTH ()
Sử dụng COL_LENGTH()
để kiểm tra độ dài (tính bằng byte) của mỗi cột:
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';
Kết quả:
+------+------+------+------+------+------+------+------+ | t0 | t1 | t2 | t3 | t4 | t5 | t6 | t7 | |------+------+------+------+------+------+------+------| | 3 | 3 | 3 | 4 | 4 | 5 | 5 | 5 | +------+------+------+------+------+------+------+------+
Vì vậy, một lần nữa, chúng tôi nhận được cùng một kết quả mà tài liệu nói rằng chúng tôi sẽ nhận được. Điều này được mong đợi, bởi vì tài liệu nêu rõ ràng "Độ dài cột (byte)", chính xác là những gì chúng tôi đang đo lường ở đây.
Hãy nhớ rằng đây là trước đây chúng tôi chèn bất kỳ dữ liệu nào. Bản thân các cột xác định độ chính xác (và do đó kích thước lưu trữ) của bất kỳ dữ liệu nào được chèn vào - chứ không phải ngược lại.
Sử dụng DBCC PAGE để kiểm tra dữ liệu đã lưu trữ
Bây giờ, hãy chèn dữ liệu, sau đó sử dụng DBCC PAGE
để tìm kích thước lưu trữ thực tế của dữ liệu mà chúng tôi lưu trữ trong mỗi cột.
Chèn dữ liệu:
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;
Bây giờ hãy chọn dữ liệu (chỉ để kiểm tra nó):
SELECT * FROM TimeTest;
Kết quả (sử dụng đầu ra dọc):
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
Như mong đợi, các giá trị sử dụng độ chính xác đã được chỉ định trước đó ở cấp cột.
Lưu ý rằng hệ thống của tôi hiển thị các số không ở cuối. Của bạn có thể làm như vậy hoặc không. Dù vậy, điều này không ảnh hưởng đến độ chính xác hoặc độ chính xác thực tế.
Bây giờ, trước khi chúng ta sử dụng DBCC PAGE()
, chúng tôi cần biết PagePID nào để chuyển cho nó. Chúng ta có thể sử dụng DBCC IND()
để tìm ra điều đó.
Tìm PagePID:
DBCC IND('Test', 'dbo.TimeTest', 0);
Kết quả (sử dụng đầu ra dọc):
-[ 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
Điều này trả về hai bản ghi. Chúng tôi quan tâm đến Loại trang 1 (bản ghi thứ 2). Chúng tôi muốn có PagePID từ bản ghi đó. Trong trường hợp này, PagePID là 384 .
Bây giờ chúng ta có thể lấy PagePID đó và sử dụng nó như sau:
DBCC TRACEON(3604, -1); DBCC PAGE(Test, 1, 384, 3);
Ngay bây giờ, chúng tôi chủ yếu quan tâm đến phần sau:
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
Vì vậy, chúng tôi nhận được kết quả tương tự như các trạng thái tài liệu. Điều này cho thấy rằng độ chính xác không được lưu trữ cùng với các giá trị.
Chúng tôi có thể xác nhận điều đó bằng cách kiểm tra dữ liệu thực tế.
Giá trị thời gian thực được lưu trữ trong phần này của tệp trang:
Memory Dump @0x0000000423ADA060 0000000000000000: 10002400 42900095 a205d459 384b8233 02f31603 ..$.B..¢.ÔY8K3.ó.. 0000000000000014: 167ae51e dc00c1f6 34990887 a311fc55 080000 .zå.Ü.Áö4..£.üU...
Chúng tôi có thể trích xuất các giá trị thời gian thực tế bằng cách loại bỏ một vài thứ. Sau khi loại bỏ, những thứ sau sẽ vẫn còn:
42900095 a205d459 384b8233 02f31603 167ae51e dc00c1f6 34990887 a311fc55
Các chữ số hex này chứa tất cả dữ liệu thời gian của chúng tôi, nhưng không chứa độ chính xác . Tuy nhiên, chúng được sắp xếp thành các phần 4 byte, vì vậy chúng tôi phải sắp xếp lại các khoảng trống để nhận các giá trị riêng lẻ.
Đây là kết quả cuối cùng. Tôi đã đặt mỗi giá trị ngày / giờ trên một dòng mới để dễ đọc hơn.
429000 95a205 d45938 4b823302 f3160316 7ae51edc00 c1f6349908 87a311fc55
Đó là các giá trị thập lục phân thực tế ( trừ đi độ chính xác ) mà chúng tôi sẽ nhận được nếu chúng tôi chuyển đổi thời gian giá trị thành varbinary . Như thế này:
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;
Kết quả (sử dụng đầu ra dọc):
t0 | 0x00429000 t1 | 0x0195A205 t2 | 0x02D45938 t3 | 0x034B823302 t4 | 0x04F3160316 t5 | 0x057AE51EDC00 t6 | 0x06C1F6349908 t7 | 0x0787A311FC55
Truy vấn đó tạo ra cùng một kết quả - ngoại trừ mỗi giá trị đã được thêm vào trước với độ chính xác.
Đây là bảng so sánh dữ liệu tệp trang thực tế với kết quả của CONVERT()
hoạt động.
Dữ liệu tệp trang | Dữ liệuCONVERT () |
---|---|
429000 | 00429000 |
95a205 | 0195A205 |
d45938 | 02D45938 |
4b823302 | 034B823302 |
f3160316 | 04F3160316 |
7ae51edc00 | 057AE51EDC00 |
c1f6349908 | 06C1F6349908 |
87a311fc55 | 0787A311FC55 |
Vì vậy, chúng ta có thể thấy rằng tệp trang không lưu trữ độ chính xác, nhưng kết quả được chuyển đổi thì có.
Tôi đã đánh dấu phần ngày và giờ thực tế bằng màu đỏ. Tôi cũng đã xóa 0x
tiền tố từ các kết quả được chuyển đổi, để chỉ dữ liệu ngày / giờ thực tế được hiển thị (cùng với độ chính xác).
Cũng xin lưu ý rằng hệ thập lục phân không phân biệt chữ hoa chữ thường, vì vậy thực tế là một cái sử dụng chữ thường và cái kia sử dụng chữ hoa không phải là một vấn đề.
Kết luận
Khi chuyển đổi thời gian giá trị thành varbinary , nó cần thêm một byte để lưu trữ độ chính xác. Nó cần độ chính xác để diễn giải phần thời gian (vì nó được lưu trữ dưới dạng khoảng thời gian, giá trị chính xác của nó sẽ phụ thuộc vào độ chính xác).
Khi được lưu trữ trong cơ sở dữ liệu, độ chính xác được chỉ định một lần ở cấp cột. Điều này có vẻ hợp lý, vì không cần thêm độ chính xác vào từng hàng khi tất cả các hàng đều có cùng độ chính xác. Điều đó sẽ yêu cầu thêm một byte cho mỗi hàng, điều này sẽ làm tăng yêu cầu lưu trữ một cách không cần thiết.