Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Hiểu kích thước lưu trữ ‘datetimeoffset’ trong SQL Server

Trong bài viết này, tôi xem xét cách datetimeoffset loại dữ liệu được lưu trữ trong SQL Server và cách bạn có thể nhận được các kết quả về kích thước lưu trữ được báo cáo khác nhau, tùy thuộc vào những gì bạn đang làm với nó.

Điều này tương tự như những gì tôi đã làm với datetime2 kiểu dữ liệu.

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

Tài liệu của Microsoft

Tài liệu chính thức của Microsoft về datetimeoffset kiểu dữ liệu cho biết kích thước lưu trữ của nó là từ 8 đến 10 byte, tùy thuộc vào độ chính xác đang được sử dụng.

Tương tự với datetime2 (n) , bạn có thể sử dụng datetimeoffset (n) để chỉ định độ chính xác, trong đó n là thang điểm từ 0 đến 7.

Dưới đây là dữ liệu mà Microsoft trình bày cho loại dữ liệu này:

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

Đố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.

Lý do chính mà tôi muốn viết bài viết này (và chạy các thử nghiệm bên dưới), là tài liệu của Microsoft không giải thích rằng một byte bổ sung được sử dụng cho độ chính xác (như trong tài liệu của nó cho datetime2 loại dữ liệu). Trong tài liệu của nó cho datetime2 , nó nói:

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.

Nhưng tài liệu cho datetimeoffset không bao gồm văn bản này và thời gian cũng không tài liệu.

Điều này khiến tôi tự hỏi liệu có sự khác biệt giữa cách các loại dữ liệu này lưu trữ giá trị của chúng hay không. Logic nói với tôi rằng chúng phải hoạt động giống nhau, vì chúng đều có độ chính xác do người dùng xác định, nhưng tôi muốn tìm hiểu.

Câu trả lời ngắn gọn là có, datetimeoffset dường như hoạt động giống như datetime2 (liên quan đến byte bổ sung), mặc dù nó không được ghi lại như vậy.

Phần còn lại của bài viết chạy qua các ví dụ khác nhau trong đó tôi trả về kích thước lưu trữ của datetimeoffset giá trị trong các ngữ cảnh khác nhau.

Dữ liệu được lưu trữ trong một biến

Hãy lưu trữ một bộ datetimeoffset 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 cho datetimeoffset (7) giá trị:

 DECLARE @d datetimeoffset (7); SET @d ='2025-05-21 10:15:30.1234567 +07:00'; CHỌN @d AS 'Giá trị', DATALENGTH (@d) AS 'Độ dài tính bằng byte '; 

Kết quả

 + ------------------------------------ + --------- ---------- + | Giá trị | Độ dài tính bằng byte || ------------------------------------ + -------- ----------- || 2025-05-21 10:15:30.1234567 +07:00 | 10 | + ------------------------------------ + ---------- --------- + 

Giá trị trong ví dụ này có quy mô tối đa là 7 (vì tôi khai báo biến là datetimeoffset (7) ), và nó trả về độ dài 10 byte.

Không có gì ngạc nhiên ở đây, đây là kích thước lưu trữ chính xác mà tài liệu của Microsoft chỉ ra rằng nó phải có.

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 datetimeoffset 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ữ (như bạn sẽ thấy sau).

Đây là những gì sẽ xảy ra nếu chúng tôi chuyển đổi bộ datetimeoffs của mình giá trị thành varbinary :

 DECLARE @d datetimeoffset (7); SET @d ='2025-05-21 10:15:30.1234567 +07:00'; CHỌN CHUYỂN ĐỔI (VARBINARY (16), @d) NHƯ 'Giá trị', DATALENGTH ( CHUYỂN ĐỔI (VARBINARY (16), @d)) NHƯ 'Độ dài tính bằng byte'; 

Kết quả

 + -------------------------- + ------------------- + | Giá trị | Độ dài tính bằng byte || -------------------------- + ------------------ - || 0x0787CBB24F1B3F480BA401 | 11 | + -------------------------- + ------------------- + 

Trong trường hợp này, chúng tôi nhận được 11 byte.

Đây là phần biểu diễn hệ thập lục phân của bộ datetimeoffs giá trị. Giá trị bù đắp ngày giờ 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ó 11 cặp, và do đó là 11 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 @d datetimeoffset (3); SET @d ='2025-05-21 10:15:30.1234567 +07:00'; CHỌN CHUYỂN ĐỔI (VARBINARY (16), @d) NHƯ 'Giá trị', DATALENGTH ( CHUYỂN ĐỔI (VARBINARY (16), @d)) NHƯ 'Độ dài tính bằng byte'; 

Kết quả

 + ------------------------ + ------------------- + | Giá trị | Độ dài tính bằng byte || ------------------------ + ------------------- | | 0x03CBFCB2003F480BA401 | 10 | + ------------------------ + ------------------- +  

Chúng tôi cũng có thể thấy rằng độ dài được giảm tương ứng.

Dữ liệu được lưu trữ trong cơ sở dữ liệu

Trong ví dụ này, tôi tạo cơ sở dữ liệu với nhiều datetimeoffset (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 datetimeoffset giá trị chiếm trên tệp trang.

Tạo cơ sở dữ liệu:

 TẠO KIỂM TRA CƠ SỞ DỮ LIỆU; 

Tạo bảng:

 SỬ DỤNG Kiểm tra; TẠO BẢNG 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)); 

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 datetimeoffset (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 ('DatetimeoffsetTest', 'd0') AS 'd0', COL_LENGTH ('DatetimeoffsetTest', 'd1') AS 'd1', COL_LENGTH ('DatetimeoffsetTest', 'd2') AS 'd2', COL_LENGTH ('DatetimeoffsetTest', 'd3') AS 'd3', COL_LENGTH ('DatetimeoffsetTest', 'd4') AS 'd4', COL_LENGTH ('DatetimeoffsetTest', 'd5') AS 'd5', COL_LENGTH ('DatetimeoffsetTest', 'd6') AS 'd6', COL_LENGTH ('DatetimeoffsetTest', 'd7') AS 'd7'; 

Kết quả:

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

Vì vậy, một lần nữa, chúng tôi nhận được kết quả tương tự như các trạng thái tài liệu mà 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.

Sử dụng DBCC PAGE để kiểm tra dữ liệu đã lưu trữ

Bây giờ hãy 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 bảng này.

Đầu tiên, hãy chèn một số dữ liệu:

 DECLARE @d datetimeoffset (7) ='2025-05-21 10:15:30.1234567 +07:00'; CHÈN VÀO DatetimeoffsetTest (d0, d1, d2, d3, d4, d5, d6, d7) SELECT @ d, @d, @d, @d, @d, @d, @d, @d; 

Bây giờ hãy chọn dữ liệu (chỉ để kiểm tra nó):

 CHỌN * TỪ DatetimeoffsetTest; 

Kết quả (sử dụng đầu ra dọc):

 d0 | 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:00d6 | 2025-05-21 10:15:30.1234570 +07:00d7 | 2025-05-21 10:15:30.1234567 + 07:00 

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 ('Kiểm tra', 'dbo.DatetimeoffsetTest', 0); 

Kết quả (sử dụng đầu ra dọc):

 - [GHI 1] ------------------------- PageFID | 1PagePID | 307IAMFID | NULLIAMPID | NULLObjectID | 1525580473IndexID | 0PartitionNumber | 1PartitionID | 72057594043170816iam_chain_type | Dữ liệu trong hàngPageType | 10IndexLevel | NULLNextPageFID | 0NextPagePID | 0PrevPageFID | 0PrevPagePID | 0- [GHI 2] ------------------------- PageFID | 1PagePID | 376IAMFID | 1IAMPID | 307ObjectID | 1525580473IndexID | 0PartitionNumber | 1PartitionID | 72057594043170816iam_chain_type | Dữ liệu trong hàngPageType | 1IndexLevel | 0NextPageFID | 0NextPagePID | 0PrevPageFID | 0PrevPagePID | 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à 376 .

Bây giờ chúng ta có thể lấy PagePID đó và sử dụng nó như sau:

 DBCC TRACEON (3604, -1); TRANG DBCC (Thử nghiệm, 1, 376, 3); 

Ngay bây giờ, chúng tôi chủ yếu quan tâm đến phần sau:

 Vị trí 0 Cột 1 Chênh lệch 0x4 Chiều dài 8 Chiều dài (thực) 8d0 =2025-05-21 10:15:30 +07:00 Vị trí 0 Cột 2 Chênh lệch 0xc Chiều dài 8 Chiều dài (thực) 8d1 =2025-05-21 10:15:30.1 +07:00 Vị trí 0 Cột 3 Chênh lệch 0x14 Chiều dài 8 Chiều dài (thực) 8d2 =2025-05-21 10:15:30.12 +07:00 Vị trí 0 Cột 4 Chênh lệch 0x1c Chiều dài 9 Chiều dài (vật lý) 9d3 =2025-05-21 10:15:30.123 +07:00 Vị trí 0 Cột 5 Chênh lệch 0x25 Độ dài 9 Độ dài (thực) 9d4 =2025-05-21 10:15:30.1235 +07:00 Vị trí 0 Cột 6 Chênh lệch 0x2e Độ dài 10 Chiều dài (thực) 10d5 =2025-05-21 10:15:30.12346 +07:00 Vị trí 0 Cột 7 Chênh lệch 0x38 Chiều dài 10 Chiều dài (thực) 10d6 =2025-05-21 10:15:30.123457 +07:00 Vị trí 0 Cột 8 Chênh lệch 0x42 Chiều dài 10 Chiều dài (vật lý) 10d7 =2025-05-21 10:15:30.1234567 +07:00 

Vì vậy, chúng tôi nhận được kết quả tương tự một lần nữa. Chính xác như tài liệu đã nêu.

Trong khi chúng tôi ở đây, hãy kiểm tra dữ liệu - các giá trị ngày / giờ thực tế khi chúng được lưu trữ trong SQL Server.

Các giá trị thực được lưu trữ trong phần này của tệp trang:

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

Điều đó vẫn bao gồm một vài bit bổ sung. Hãy xóa một vài thứ để chỉ còn lại các giá trị ngày và giờ của chúng ta:

 d22d003f 480ba401 35ca013f 480ba40114e6113f 480ba401 cbfcb200 3f480ba4 01f3dffd063f480b a4017abf ea45003f 480ba401 c17a2bbb023f480b a40187cb b24ba401 
 Các chữ số hex còn lại chứa tất cả dữ liệu ngày và giờ của chúng tôi,  nhưng không phải là độ 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.

 d22d003f480ba401 35ca013f480ba40114e6113f480ba401 cbfcb2003f480ba401f3dffd063f480ba4017abfea45003f480ba401 c17a2bbb023f480ba401 cbfcb2003f480ba401f3dffd063f480ba4017abfea45003f480ba401 c17a2bbb023f480ba40187cbb480ba40 
 Đó 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  datetimeoffset  giá trị thành  varbinary  . Như thế này:

 CHỌN CHUYỂN ĐỔI (VARBINARY (16), d0) AS 'd0', CHUYỂN ĐỔI (VARBINARY (16), d1) AS 'd1', CHUYỂN ĐỔI (VARBINARY (16), d2) AS 'd2', CHUYỂN ĐỔI (VARBINARY ( 16), d3) NHƯ 'd3', CHUYỂN ĐỔI (VARBINARY (16), d4) NHƯ 'd4', CHUYỂN ĐỔI (VARBINARY (16), d5) NHƯ 'd5', CHUYỂN ĐỔI (VARBINARY (16), d6) NHƯ 'd6 ', CHUYỂN ĐỔI (VARBINARY (16), d7) NHƯ' d7'FROM DatetimeoffsetTest; 

Kết quả (sử dụng đầu ra dọc):

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

Vì vậy, chúng tôi nhận được cùng một kết quả - ngoại trừ việc nó đã đượ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
Dữ liệu tệp trang CONVERT ()
d22d003f480ba401 00D22D003F480BA401
35ca013f480ba401 0135CA013F480BA401
14e6113f480ba401 0214E6113F480BA401
cbfcb2003f480ba401 03CBFCB2003F480BA401
f3dffd063f480ba401 04F3DFFD063F480BA401
7abfea45003f480ba401 057ABFEA45003F480BA401
c17a2bbb023f480ba401 06C17A2BBB023F480BA401
87cbb24f1b3f480ba401 0787CBB24F1B3F480BA401

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 bộ datetimeoffs 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 cho mỗi hàng khi tất cả các hàng vẫn sử dụng cùng một độ 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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Làm cách nào để kết nối với SQL Server bằng cách sử dụng bảo mật tích hợp với trình điều khiển JDBC?

  2. Chuyển đổi ‘smalldatetime’ thành ‘datetime’ trong SQL Server (Ví dụ T-SQL)

  3. Cách định dạng số dưới dạng tiền tệ trong SQL Server (T-SQL)

  4. Nhận ngày giữa một loạt các ngày

  5. Cách liệt kê các tệp bên trong một thư mục với SQL Server