sql >> データベース >  >> RDS >> Sqlserver

SQLServerの「datetimeoffset」ストレージサイズを理解する

    この記事では、 datetimeoffset データ型はSQLServerに保存され、使用している内容に応じて、さまざまなレポートストレージサイズの結果を取得する方法が示されます。

    これは、 datetime2で行ったことと似ています。 データ型。

    特に、私は以下を見ています:

    • Microsoftのドキュメント
    • 変数に格納されているデータ
      • DATALENGTH()を使用したバイト単位の長さ
      • DATALENGTH()を使用したバイト単位の長さ varbinaryに変換した後
    • データベースに保存されているデータ
      • COL_LENGTH()を使用したバイト単位の長さ
      • DBCC PAGE()を使用したバイト単位の長さ

    Microsoftのドキュメント

    datetimeoffsetに関するMicrosoftの公式ドキュメント データ型は、使用されている精度に応じて、ストレージサイズが8〜10バイトであることを示します。

    datetime2(n)に似ています 、 datetimeoffset(n)を使用できます 精度を指定します。ここで、 n は0から7までのスケールです。

    このデータ型についてMicrosoftが提示するデータは次のとおりです。

    指定されたスケール 結果(精度、スケール) 列の長さ(バイト) フラクショナル秒の精度
    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

    この記事では、主に列の長さ(バイト)に関心があります。 桁。これにより、このデータ型をデータベースに格納するために使用されるバイト数がわかります。

    私がこの記事を書きたい(そして以下の実験を実行したい)主な理由は、Microsoftのドキュメントでは、精度のために余分なバイトが使用されることを説明していないためです( datetime2 データ・タイプ)。 datetime2のドキュメント 、それは述べています:

    datetime2の最初のバイト valueは、値の精度を格納します。これは、 datetime2に必要な実際のストレージを意味します valueは、上記の表に示されているストレージサイズに、精度を格納するための1バイトを加えたものです。これにより、 datetime2の最大サイズになります 値9バイト– 1バイトは精度を格納し、さらに最大精度でデータを保存するために8バイトを格納します。

    ただし、 datetimeoffsetのドキュメント このテキストは含まれていません。また、時間も含まれていません。 ドキュメント。

    これにより、これらのデータ型が値を格納する方法に違いがあるのではないかと思いました。 Logicは、すべてユーザー定義の精度を持っているので、同じように機能する必要があると私に言いましたが、私は知りたいと思いました。

    簡単な答えは「はい」です。datetimeoffset datetime2と同じように機能するようです (余分なバイトに関して)そのように文書化されていなくても。

    この記事の残りの部分では、 datetimeoffsetのストレージサイズを返すさまざまな例を紹介します。 さまざまなコンテキストでの値。

    変数に格納されたデータ

    datetimeoffsetを保存しましょう 変数の値を入力し、そのストレージサイズを確認します。次に、その値を varbinaryに変換します もう一度確認してください。

    DATALENGTHを使用したバイト単位の長さ

    DATALENGTH()を使用するとどうなりますか datetimeoffset(7)に使用されたバイト数を返す関数 値:

    DECLARE @d datetimeoffset(7);
    SET @d = '2025-05-21 10:15:30.1234567 +07:00';
    SELECT 
      @d AS 'Value',
      DATALENGTH(@d) AS 'Length in Bytes';
    

    結果

    +------------------------------------+-------------------+
    | Value                              | Length in Bytes   |
    |------------------------------------+-------------------|
    | 2025-05-21 10:15:30.1234567 +07:00 | 10                |
    +------------------------------------+-------------------+
    

    この例の値の最大スケールは7です(変数を datetimeoffset(7)として宣言しているためです。 )、10バイトの長さを返します。

    ここで驚くことはありません。これは、Microsoftのドキュメントに示されている正確なストレージサイズです。

    ただし、値を varbinaryに変換すると 別の結果が得られます。

    「varbinary」に変換した後のバイト単位の長さ

    一部の開発者は、 datetimeoffsetを変換することを好みます およびdatetime2 varbinaryへの変数 、SQLServerがデータベースに保存する方法をよりよく表しているためです。これは部分的に当てはまりますが、結果は保存されている値とまったく同じではありません(後で説明します)。

    datetimeoffsetを変換するとどうなりますか varbinaryの値 :

    DECLARE @d datetimeoffset(7);
    SET @d = '2025-05-21 10:15:30.1234567 +07:00';
    SELECT 
      CONVERT(VARBINARY(16), @d) AS 'Value',
      DATALENGTH(CONVERT(VARBINARY(16), @d)) AS 'Length in Bytes';
    

    結果

    +--------------------------+-------------------+
    | Value                    | Length in Bytes   |
    |--------------------------+-------------------|
    | 0x0787CBB24F1B3F480BA401 | 11                |
    +--------------------------+-------------------+
    

    この場合、11バイトを取得します。

    これは、 datetimeoffsetの16進表現です。 価値。実際の日時オフセット値(およびその精度)は、0x以降のすべてです。 。 16進文字の各ペアは1バイトです。 11ペア、つまり11バイトがあります。これは、DATALENGTH()を使用するときに確認されます 長さをバイト単位で返します。

    この例では、最初のバイトが07であることがわかります。 。これは精度を表します(私は7のスケールを使用したので、ここに表示されます)。

    スケールを変更すると、最初のバイトがスケールに一致するように変更されていることがわかります。

    DECLARE @d datetimeoffset(3);
    SET @d = '2025-05-21 10:15:30.1234567 +07:00';
    SELECT 
      CONVERT(VARBINARY(16), @d) AS 'Value',
      DATALENGTH(CONVERT(VARBINARY(16), @d)) AS 'Length in Bytes';
    

    結果

    +------------------------+-------------------+
    | Value                  | Length in Bytes   |
    |------------------------+-------------------|
    | 0x03CBFCB2003F480BA401 | 10                |
    +------------------------+-------------------+
    

    それに応じて長さが短くなっていることもわかります。

    データベースに保存されているデータ

    この例では、さまざまな datetimeoffset(n)を使用してデータベースを作成します。 列をクリックしてから、COL_LENGTH()を使用します 各列の長さをバイト単位で返します。次に、DBCC PAGEを使用する前に、列に値を挿入します 各datetimeoffsetのストレージサイズを確認します 値はページファイルで使用されます。

    データベースを作成します:

    CREATE DATABASE Test;
    

    テーブルを作成します:

    USE Test;
    
    CREATE TABLE 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)
        );
    

    この場合、8つの列を作成します。 datetimeoffset(n)で使用できるユーザー定義のスケールごとに1つです。 。

    これで、各列のストレージサイズを確認できます。

    COL_LENGTH()を使用したバイト単位の長さ

    COL_LENGTH()を使用します 各列の長さ(バイト単位)を確認するには:

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

    結果:

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

    したがって、もう一度、ドキュメントに記載されているのと同じ結果が得られます。ドキュメントには「列の長さ(バイト)」と明示的に記載されているため、これは予想されることです。これはまさにここで測定しているものです。

    DBCCPAGEを使用して保存データを確認する

    それでは、DBCC PAGEを使用しましょう このテーブルに保存するデータの実際のストレージサイズを確認します。

    まず、いくつかのデータを挿入しましょう:

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

    次に、データを選択します(確認するためだけに):

    SELECT * FROM DatetimeoffsetTest;
    

    結果(垂直出力を使用):

    d0 | 2025-05-21 10:15:30.0000000 +07:00
    d1 | 2025-05-21 10:15:30.1000000 +07:00
    d2 | 2025-05-21 10:15:30.1200000 +07:00
    d3 | 2025-05-21 10:15:30.1230000 +07:00
    d4 | 2025-05-21 10:15:30.1235000 +07:00
    d5 | 2025-05-21 10:15:30.1234600 +07:00
    d6 | 2025-05-21 10:15:30.1234570 +07:00
    d7 | 2025-05-21 10:15:30.1234567 +07:00
    

    予想どおり、値は以前に列レベルで指定された精度を使用します。

    私のシステムは末尾のゼロを表示することに注意してください。あなたはそうするかもしれないし、しないかもしれない。とにかく、これは実際の精度や精度には影響しません。

    ここで、DBCC PAGE()を使用する前に 、どのPagePIDを渡すかを知る必要があります。 DBCC IND()を使用できます それを見つけるために。

    PagePIDを見つける:

    DBCC IND('Test', 'dbo.DatetimeoffsetTest', 0);
    

    結果(垂直出力を使用):

    -[ RECORD 1 ]-------------------------
    PageFID         | 1
    PagePID         | 307
    IAMFID          | NULL
    IAMPID          | NULL
    ObjectID        | 1525580473
    IndexID         | 0
    PartitionNumber | 1
    PartitionID     | 72057594043170816
    iam_chain_type  | In-row data
    PageType        | 10
    IndexLevel      | NULL
    NextPageFID     | 0
    NextPagePID     | 0
    PrevPageFID     | 0
    PrevPagePID     | 0
    -[ RECORD 2 ]-------------------------
    PageFID         | 1
    PagePID         | 376
    IAMFID          | 1
    IAMPID          | 307
    ObjectID        | 1525580473
    IndexID         | 0
    PartitionNumber | 1
    PartitionID     | 72057594043170816
    iam_chain_type  | In-row data
    PageType        | 1
    IndexLevel      | 0
    NextPageFID     | 0
    NextPagePID     | 0
    PrevPageFID     | 0
    PrevPagePID     | 0
    

    これにより、2つのレコードが返されます。 PageType of 1(2番目のレコード)に関心があります。そのレコードからPagePIDが必要です。この場合、PagePIDは 376 です。 。

    これで、そのPagePIDを取得して、次の場所で使用できます。

    DBCC TRACEON(3604, -1);
    DBCC PAGE(Test, 1, 376, 3);
    

    現在、主に次の部分に関心があります。

    Slot 0 Column 1 Offset 0x4 Length 8 Length (physical) 8
    
    d0 = 2025-05-21 10:15:30 +07:00     
    
    Slot 0 Column 2 Offset 0xc Length 8 Length (physical) 8
    
    d1 = 2025-05-21 10:15:30.1 +07:00   
    
    Slot 0 Column 3 Offset 0x14 Length 8 Length (physical) 8
    
    d2 = 2025-05-21 10:15:30.12 +07:00  
    
    Slot 0 Column 4 Offset 0x1c Length 9 Length (physical) 9
    
    d3 = 2025-05-21 10:15:30.123 +07:00 
    
    Slot 0 Column 5 Offset 0x25 Length 9 Length (physical) 9
    
    d4 = 2025-05-21 10:15:30.1235 +07:00
    
    Slot 0 Column 6 Offset 0x2e Length 10 Length (physical) 10
    
    d5 = 2025-05-21 10:15:30.12346 +07:00                                    
    
    Slot 0 Column 7 Offset 0x38 Length 10 Length (physical) 10
    
    d6 = 2025-05-21 10:15:30.123457 +07:00                                   
    
    Slot 0 Column 8 Offset 0x42 Length 10 Length (physical) 10
    
    d7 = 2025-05-21 10:15:30.1234567 +07:00                                                                
    

    したがって、同じ結果が再び得られます。ドキュメントに記載されているとおりです。

    ここにいる間に、データ、つまりSQLServerに保存されている実際の日付/時刻の値を調べてみましょう。

    実際の値は、ページファイルのこの部分に保存されます:

    Memory Dump @0x000000041951A060
    
    0000000000000000:   10004c00 d22d003f 480ba401 35ca013f 480ba401  ..L.Ò-.?H.¤.5Ê.?H.¤.
    0000000000000014:   14e6113f 480ba401 cbfcb200 3f480ba4 01f3dffd  .æ.?H.¤.Ëü².?H.¤.óßý
    0000000000000028:   063f480b a4017abf ea45003f 480ba401 c17a2bbb  .?H.¤.z¿êE.?H.¤.Áz+»
    000000000000003C:   023f480b a40187cb b24f1b3f 480ba401 080000    .?H.¤.‡Ë²O.?H.¤....
    

    それはまだいくつかの余分なビットが含まれています。日付と時刻の値だけが残るように、いくつか削除してみましょう。

    d22d003f 480ba401 35ca013f 480ba401
    14e6113f 480ba401 cbfcb200 3f480ba4 01f3dffd
    063f480b a4017abf ea45003f 480ba401 c17a2bbb
    023f480b a40187cb b24f1b3f 480ba401
    

    残りの16進数には、すべての日付と時刻のデータが含まれますが、精度は含まれません 。ただし、これらは4バイトのチャンクに配置されるため、個々の値を取得するにはスペースを再配置する必要があります。

    これが最終結果です。読みやすくするために、各日付/時刻の値を新しい行に配置しました。

    d22d003f480ba401 
    35ca013f480ba401
    14e6113f480ba401 
    cbfcb2003f480ba401
    f3dffd063f480ba401
    7abfea45003f480ba401 
    c17a2bbb023f480ba401
    87cbb24f1b3f480ba401
    

    これらは実際の16進値です(精度を差し引いたもの datetimeoffsetを変換した場合に得られるもの varbinaryの値 。このように:

    SELECT 
      CONVERT(VARBINARY(16), d0) AS 'd0',
      CONVERT(VARBINARY(16), d1) AS 'd1',
      CONVERT(VARBINARY(16), d2) AS 'd2',
      CONVERT(VARBINARY(16), d3) AS 'd3',
      CONVERT(VARBINARY(16), d4) AS 'd4',
      CONVERT(VARBINARY(16), d5) AS 'd5',
      CONVERT(VARBINARY(16), d6) AS 'd6',
      CONVERT(VARBINARY(16), d7) AS 'd7'
    FROM DatetimeoffsetTest;
    

    結果(垂直出力を使用):

    d0 | 0x00D22D003F480BA401
    d1 | 0x0135CA013F480BA401
    d2 | 0x0214E6113F480BA401
    d3 | 0x03CBFCB2003F480BA401
    d4 | 0x04F3DFFD063F480BA401
    d5 | 0x057ABFEA45003F480BA401
    d6 | 0x06C17A2BBB023F480BA401
    d7 | 0x0787CBB24F1B3F480BA401
    

    したがって、同じ結果が得られますが、精度が付加されている点が異なります。

    これは、実際のページファイルデータをCONVERT()の結果と比較する表です。 操作。

    ページファイルデータ CONVERT()データ
    d22d003f480ba401 00D22D003F480BA401
    35ca013f480ba401 0135CA013F480BA401
    14e6113f480ba401 0214E6113F480BA401
    cbfcb2003f480ba401 03CBFCB2003F480BA401
    f3dffd063f480ba401 04F3DFFD063F480BA401
    7abfea45003f480ba401 057ABFEA45003F480BA401
    c17a2bbb023f480ba401 06C17A2BBB023F480BA401
    87cbb24f1b3f480ba401 0787CBB24F1B3F480BA401

    したがって、ページファイルには精度が保存されていないことがわかりますが、変換された結果には保存されています。

    実際の日付と時刻の部分を赤で強調表示しました。 0xも削除しました 変換された結果のプレフィックス。実際の日付/時刻データのみが(精度とともに)表示されます。

    また、16進数は大文字と小文字を区別しないため、一方が小文字を使用し、もう一方が大文字を使用するという事実は問題ではないことに注意してください。

    結論

    datetimeoffsetを変換する場合 varbinaryの値 、精度を格納するために追加のバイトが必要です。時間部分を解釈するには精度が必要です(これは時間間隔として保存されるため、正確な値は精度によって異なります)。

    データベースに格納する場合、精度は列レベルで1回指定されます。とにかくすべての行が同じ精度を使用する場合、各行に精度を追加する必要がないため、これは論理的なようです。これには、行ごとに追加のバイトが必要になり、ストレージ要件が不必要に増加します。


    1. PHPを使用してMySQLに接続する方法

    2. OracleFormsのクエリで表示項目の値を入力する

    3. SQL:カンマ区切りの文字列を解析し、結合として使用します

    4. COUNTSQL関数のパフォーマンス