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

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

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ữ 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ề 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.

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..•¢.ÔY8K‚3.ó..
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
Dữ liệu tệp trang CONVERT ()
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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Điểm kiểm tra cơ sở dữ liệu trong SQL Server

  2. 4 Tài nguyên giám sát máy chủ SQL tuyệt vời dành cho quản trị viên cơ sở dữ liệu

  3. Cách tìm số nhận dạng duy nhất tối ưu trong bảng trong SQL Server:sp_special_columns

  4. Sự khác biệt giữa kết nối trái và tham gia phải trong SQL Server

  5. Làm cách nào để tạo một Bảng được liên kết ODBC theo cách lập trình đến Dạng xem Máy chủ SQL và nó có thể chỉnh sửa được không?