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

Hiểu kích thước bộ nhớ ‘datetime2’ trong SQL Server

Trong bài viết này, tôi chia sẻ một số quan sát mà tôi đã có về datetime2 kích thước lưu trữ của loại dữ liệu trong SQL Server. Có lẽ tôi sẽ làm rõ một số điểm về kích thước lưu trữ thực tế được sử dụng bởi kiểu dữ liệu này khi được lưu trữ trong cơ sở 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()

Một số trong số đó dường như mâu thuẫn với nhau và bạn sẽ thấy hai lượng kích thước bộ nhớ khác nhau cho cùng một giá trị, tùy thuộc vào nơi bạn nhìn.

A datetime2 giá trị có thể hiển thị kích thước bộ nhớ khác nhau, tùy thuộc vào việc nó có được lưu trữ trong cơ sở dữ liệu hay không, dưới dạng datetime2 biến hoặc được chuyển đổi thành varbinary .

Nhưng có một lời giải thích hợp lý cho điều này - nó phụ thuộc vào nơi độ chính xác đang được lưu trữ.

Trong quá trình nghiên cứu về vấn đề này, tôi đã tìm thấy bài viết chuyên sâu của Ronen Ariely về cách datetime2 được lưu trữ trong tệp dữ liệu rất giàu thông tin và nó đã thúc đẩy tôi chạy một số thử nghiệm tương tự trong môi trường phát triển của riêng tôi và trình bày chúng tại đây.

Tài liệu của Microsoft

Trước tiên, hãy xem tài liệu chính thức nói gì.

Tài liệu của Microsoft về datetime2 kiểu dữ liệu nói rằng kích thước lưu trữ của nó như sau:

6 byte cho độ chính xác nhỏ hơn 3.
7 byte cho độ chính xác 3 hoặc 4.
Tất cả các độ chính xác khác yêu cầu 8 byte.

Nhưng nó đủ điều kiện cho bảng trên với câu lệ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ì vậy, với thông tin trên, kết luận rõ ràng cần rút ra là bảng có thể / (nên?) Được viết như sau:

7 byte đối với độ chính xác nhỏ hơn 3.
8 byte đối với độ chính xác 3 hoặc 4.
Tất cả các độ chính xác khác yêu cầu 9 byte.

Bằng cách đó, họ sẽ không cần phải chứng nhận nó với thông tin bổ sung về độ chính xác.

Nhưng nó không hoàn toàn đơn giản như vậy.

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

Đầu tiên, hãy lưu trữ datetime2 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 datetime2 (7) giá trị:

 DECLARE @d datetime2 (7); SET @d ='2025-05-21 10:15:30.1234567'; 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 | 8 | + ----------------------------- + ----------------- - + 

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

Điều này dường như mâu thuẫn với những gì Microsoft tuyên bố về việc cần thêm một byte để lưu trữ độ chính xác. Để trích dẫn Microsoft, Điều này tạo 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. .

Mặc dù đúng là chúng tôi có vẻ nhận được 8 byte để lưu trữ dữ liệu , chúng tôi dường như thiếu 1 byte được sử dụng để lưu trữ độ chính xác.

Tuy nhiên, nếu chúng tôi chuyển đổi giá trị thành varbinary chúng ta có một câu chuyện khác.

Độ dài tính bằng byte sau khi chuyển đổi thành 'varbinary'

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

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

Kết quả

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

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

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

Kết quả

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

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

Vì vậy, trong trường hợp này, kết quả của chúng tôi hoàn toàn khớp với tài liệu của Microsoft - một byte bổ sung đã được thêm vào để tạo độ chính xác.

Nhiều nhà phát triển giả định rằng đây là cách SQL Server lưu trữ datetime2 của nó giá trị trong cơ sở dữ liệu. Tuy nhiên, giả định đó dường như không chính xác.

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 chứa một bảng với nhiều datetime2 (n) khác nhau cột. Sau đó, tôi 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 nó, trước khi sử dụng DBCC PAGE để kiểm tra kích thước bộ nhớ mà mỗi datetime2 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 Datetime2Test (d0 datetime2 (0), d1 datetime2 (1), d2 datetime2 (2), d3 datetime2 (3), d4 datetime2 (4), d5 datetime2 (5), d6 datetime2 (6 ), d7 datetime2 (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 datetime2 (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 ('Datetime2Test', 'd0') AS 'd0', COL_LENGTH ('Datetime2Test', 'd1') AS 'd1', COL_LENGTH ('Datetime2Test', 'd2') AS 'd2', COL_LENGTH ('Datetime2Test', 'd3') AS 'd3', COL_LENGTH ('Datetime2Test', 'd4') AS 'd4', COL_LENGTH ('Datetime2Test', 'd5') AS 'd5', COL_LENGTH ('Datetime2Test', 'd6') AS 'd6', COL_LENGTH ('Datetime2Test', 'd7') AS 'd7'; 

Kết quả:

 + ------ + ------ + ------ + ------ + ------ + ------ + ---- - + ------ + | d0 | d1 | d2 | d3 | d4 | d5 | d6 | d7 || ------ + ------ + ------ + ------ + ------ + ------ + ----- - + ------ || 6 | 6 | 6 | 7 | 7 | 8 | 8 | 8 | + ------ + ------ + ------ + ------ + ------ + ------ + ----- - + ------ + 

Vì vậy, một lần nữa, chúng tôi dường như không sử dụng thêm byte để lưu trữ độ chính xác.

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 datetime2 (7) ='2025-05-21 10:15:30.1234567'; CHÈN VÀO Datetime2Test (d0, d1, d2, d3, d4, d5, d6, d7) CHỌN @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Ừ Datetime2Test; 

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

 d0 | 2025-05-21 10:15:30d1 | 2025-05-21 10:15:30.1d2 | 2025-05-21 10:15:30.12d3 | 2025-05-21 10:15:30.123d4 | 2025-05-21 10:15:30.1235d5 | 2025-05-21 10:15:30.12346d6 | 2025-05-21 10:15:30.123457d7 | 2025-05-21 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.

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.Datetime2Test', 0); 

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

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

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, 360, 3); 

Điều này tạo ra rất nhiều dữ liệu, nhưng chúng tôi chủ yếu quan tâm đến phần sau:

 Khe 0 Cột 1 Chênh lệch 0x4 Chiều dài 6 Chiều dài (thực) 6d0 =2025-05-21 10:15:30 Vị trí 0 Cột 2 Chênh lệch 0xa Chiều dài 6 Chiều dài (thực) 6d1 =2025-05-21 10:15:30.1 Khe 0 Cột 3 Chênh lệch 0x10 Chiều dài 6 Chiều dài (thực) 6d2 =2025-05-21 10:15:30.12 Khe 0 Cột 4 Chênh lệch 0x16 Chiều dài 7 Chiều dài (thực) 7d3 =2025-05-21 10:15:30.123 Khe 0 Cột 5 Chênh lệch 0x1d Độ dài 7 Độ dài (thực) 7d4 =2025-05-21 10:15:30.1235 Vị trí 0 Cột 6 Chênh lệch 0x24 Độ dài 8 Độ dài (thực) 8d5 =2025-05-21 10:15:30.12346 Vị trí 0 Cột 7 Chênh lệch 0x2c Chiều dài 8 Chiều dài (thực) 8d6 =2025-05-21 10:15:30.123457 Vị trí 0 Cột 8 Chênh lệch 0x34 Chiều dài 8 Chiều dài (thực) 8d7 =2025-05-21 10:15:30.1234567 

Vì vậy, có vẻ như nó không sử dụng thêm byte để tạo độ chính xác.

Nhưng chúng ta hãy kiểm tra dữ liệu thực tế trước khi chúng tôi đưa ra bất kỳ kết luận nào.

Dữ liệu thực tế được lưu trữ trong phần này của tệp trang:

 Memory Dump @ 0x000000041883A0600000000000000000:10003c00 4290003f 480b95a2 053f480b d459383f .. <. B ..? H. • ¢.? H.ÔY8? 000000000000000014:480b4b82 33023f48 0bf3160‚ 163. H.zå.Ü00000000000028:003f480b c1f63499 083f480b 87a311fc 553f480b.? H.Áö4 ..? H. ‡ £ .üU? H.000000000000003C:080000 ... ... 

Như bạn có thể thấy, không có kết quả nào trong số đó giống như kết quả mà chúng tôi sẽ nhận được bằng cách chuyển đổi datetime2 giá trị thành varbinary . Nhưng nó khá gần.

Đây là giao diện nếu tôi xóa một vài thứ:

 4290003f 480b95a2 053f480b d459383f480b4b82 33023f48 0bf31603 163f480b 7ae51edc003f480b c1f63499 083f480b 87a311fc 553f480b 

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 ta phải sắp xếp lại các khoảng trống để có được giá trị thực cho mỗi hàng.

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

 4290003f480b 95a2053f480b d459383f480b 4b8233023f480bf31603163f480b 7ae51edc003f480b c1f63499083f480b 87a311fc553f480b 

Đó 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 datetime2 giá trị thành varbinary . Để chắc chắn, hãy tiếp tục và thực hiện điều đó:

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

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

 d0 | 0x004290003F480Bd1 | 0x0195A2053F480Bd2 | 0x02D459383F480Bd3 | 0x034B8233023F480Bd4 | 0x04F31603163F480Bd5 | 0x057AE51EDC003F480Bd6 | 0x06C1F63499083F480Bd7 | 0x0787A311FC553F480B 

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.

Nhưng để làm cho mọi thứ trở nên rõ ràng, đâ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 ()
4290003f480b 004290003F480B
95a2053f480b 0195A2053F480B
d459383f480b 02D459383F480B
4b8233023f480b 034B8233023F480B
f31603163f480b 04F31603163F480B
7ae51edc003f480b 057AE51EDC003F480B
c1f63499083f480b 06C1F63499083F480B
87a311fc553f480b 0787A311FC553F480B

Vì vậy, chúng ta có thể thấy rõ 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 datetime2 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 phải lưu trữ thêm một byte với mỗi hàng nếu nó có thể được chỉ định ở cấp cột. Vì vậy, nếu bạn chỉ định nói, datetime2 (7) ở cấp cột, thì mỗi hàng sẽ là datetime2 (7) . Không cần phải nhắc lại điều này trên mọi hàng.

Ronen Ariely đã đưa ra kết luận tương tự trong bài báo của anh ấy đã đề cập ở trên.

Nếu bạn có một triệu hàng với datetime2 (7) , lưu trữ độ chính xác với mỗi hàng sẽ yêu cầu 9.000.000 byte, so với chỉ 8.000.001 nếu độ chính xác được lưu trữ một lần cho toàn bộ cột.

Điều này cũng củng cố datetime2 của Trường hợp của ‘s khi so sánh với datetime . Ngay cả khi sử dụng cùng một số vị trí thập phân như ngày giờ (tức là 3), datetime2 kiểu dữ liệu sử dụng ít dung lượng hơn (ít nhất là khi được lưu trữ trong bảng có nhiều hơn một hàng). Và nó thực hiện điều này với độ chính xác cao hơn. A ngày giờ giá trị sử dụng 8 byte, trong khi datetime2 (3) sử dụng 7 byte (cộng với 1 byte "độ chính xác" được chia sẻ trên tất cả các hàng).


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nhật ký giao dịch SQL Server - Phần 1

  2. Cách tìm nạp số lượng hàng cho tất cả các bảng trong cơ sở dữ liệu SQL SERVER

  3. Cách tốt nhất để nhận Hướng dẫn PK của hàng đã chèn

  4. Mẹo nhanh để sửa chữa và khôi phục cơ sở dữ liệu SQL mà không cần sao lưu

  5. Gói điều chỉnh Spotlight Cơ bản:Công cụ tối ưu hóa SQL miễn phí tốt nhất