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à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
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ụngDATALENGTH()
để 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ụngDBCC 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.1234567Như 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ụngDBCC 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.1234567Vì 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 553f480bCá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 | 0x0787A311FC553F480BVì 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 tệp trang | Dữ liệuCONVERT () |
---|---|
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).