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

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

    この記事では、時間のストレージサイズについて説明します。 SQLServerのデータ型。

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

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

    Microsoftのドキュメント

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

    このデータ型により、ユーザー定義の精度が可能になります。 time(n)を使用できます 精度を指定します。ここで、 n は0から7までのスケールです。

    これが、Microsoftが時間に提示するデータです。 データ型:

    指定されたスケール 結果(精度、スケール) 列の長さ(バイト) フラクショナル秒の精度
    時間 (16,7) 5 7
    time(0) (8,0) 3 0-2
    time(1) (10,1) 3 0-2
    time(2) (11,2) 3 0-2
    time(3) (12,3) 4 3-4
    time(4) (13,4) 4 3-4
    time(5) (14,5) 5 5-7
    time(6) (15,6) 5 5-7
    time(7) (16,7) 5 5-7

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

    ユーザーの観点から、時間 データ型は、 datetime2の時間部分と同じように機能します 。ユーザー定義の秒の小数部の精度があり、0から7のスケールを受け入れます。

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

    変数に格納されたデータ

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

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

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

    DECLARE @t time(7);
    SET @t = '10:15:30.1234567';
    SELECT 
      @t AS 'Value',
      DATALENGTH(@t) AS 'Length in Bytes';
    

    結果

    +------------------+-------------------+
    | Value            | Length in Bytes   |
    |------------------+-------------------|
    | 10:15:30.1234567 | 5                 |
    +------------------+-------------------+
    

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

    これは、Microsoftの表に概説されているストレージサイズと一致するため、予想されることです。

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

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

    一部の開発者は時間を変換するのが好きです またはdatetime2 varbinaryへの変数 これは、SQLServerがデータベースに格納する方法をよりよく表しているためです。これは部分的に当てはまりますが、結果は保存された値と完全に同じではありません(詳細は以下を参照)。

    時間を変換するとどうなりますか 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';
    

    結果

    +----------------+-------------------+
    | Value          | Length in Bytes   |
    |----------------+-------------------|
    | 0x0787A311FC55 | 6                 |
    +----------------+-------------------+
    

    この場合、6バイトを取得します。現在、値はドキュメントに記載されているよりも1バイト多く使用しています。

    これは、精度を格納するために追加のバイトが必要になるためです。

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

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

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

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

    結果

    +--------------+-------------------+
    | Value        | Length in Bytes   |
    |--------------+-------------------|
    | 0x034B823302 | 5                 |
    +--------------+-------------------+
    

    それに応じて長さが短くなっていることもわかります。ただし、繰り返しになりますが、ドキュメントで使用する必要があると記載されているバイト数よりも1バイト多くなります。

    時間に関するMicrosoftのドキュメント これについては明示的に言及されていません。datetime2のドキュメント 次のように述べています:

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

    そしてdatetime2 データ型は、上記の例とまったく同じように機能します。つまり、 varbinaryに変換されたときにのみ余分なバイトを報告します 。

    したがって、Microsoftのドキュメントに記載されている余分なバイトは、時間にも当てはまるようです。 。

    ただし、時間の実際のストレージサイズ 値は、データが保存されている場所に表示されます。

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

    データベース列のタイプが時間の場合 、その精度は列レベルで指定されます–ではありません データレベルで。つまり、列全体に対して1回指定されます。列をtime(7)として定義する場合、これは理にかなっています。 、すべての行が time(7)になることを知っています 。各行にその事実を示す貴重なバイトを使い切る必要はありません。

    時間を調べるとき SQL Serverに保存されている値を見ると、 varbinaryと同じであることがわかります。 結果ですが、なし 精度。

    以下は、時間を示す例です。 値はSQLServerに保存されます。

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

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

    CREATE DATABASE Test;
    

    テーブルを作成します:

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

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

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

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

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

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

    結果:

    +------+------+------+------+------+------+------+------+
    | t0   | t1   | t2   | t3   | t4   | t5   | t6   | t7   |
    |------+------+------+------+------+------+------+------|
    | 3    | 3    | 3    | 4    | 4    | 5    | 5    | 5    |
    +------+------+------+------+------+------+------+------+
    

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

    これはであることを忘れないでください データを挿入します。列自体が、挿入されるデータの精度(したがってストレージサイズ)を決定します。その逆ではありません。

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

    次に、データを挿入してから、DBCC PAGEを使用します。 各列に保存するデータの実際のストレージサイズを確認します。

    データの挿入:

    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;
    

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

    SELECT * FROM TimeTest;
    

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

    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
    

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

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

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

    PagePIDを見つける:

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

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

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

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

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

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

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

    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                                                                      
    

    したがって、ドキュメントに記載されているのと同じ結果が得られます。これは、精度が値とともに保存されていないことを示しています。

    実際のデータを調べることで確認できます。

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

    Memory Dump @0x0000000423ADA060
    
    0000000000000000:   10002400 42900095 a205d459 384b8233 02f31603  ..$.B..•¢.ÔY8K‚3.ó..
    0000000000000014:   167ae51e dc00c1f6 34990887 a311fc55 080000    .zå.Ü.Áö4..‡£.üU...
    

    いくつかを削除することで、実際の時間値を抽出できます。削除すると、次のものが残ります。

    42900095 a205d459 384b8233 02f31603
    167ae51e dc00c1f6 34990887 a311fc55
    

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

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

    429000
    95a205
    d45938
    4b823302
    f3160316
    7ae51edc00
    c1f6349908
    87a311fc55
    

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

    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;
    

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

    t0 | 0x00429000
    t1 | 0x0195A205
    t2 | 0x02D45938
    t3 | 0x034B823302
    t4 | 0x04F3160316
    t5 | 0x057AE51EDC00
    t6 | 0x06C1F6349908
    t7 | 0x0787A311FC55
    

    そのクエリは同じ結果を生成しますが、各値の前に精度が付加されている点が異なります。

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

    ページファイルデータ CONVERT()データ
    429000 00429000
    95a205 0195A205
    d45938 02D45938
    4b823302 034B823302
    f3160316 04F3160316
    7ae51edc00 057AE51EDC00
    c1f6349908 06C1F6349908
    87a311fc55 0787A311FC55

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

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

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

    結論

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

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


    1. CASTとIsNumeric

    2. SQL ServerのOBJECTPROPERTY()を使用して、オブジェクトが主キーであるかどうかを確認します

    3. ストアドプロシージャで出力パラメータサイズを定義するにはどうすればよいですか?

    4. 「SQLServerを使用したMicrosoftAccessの最適化」プレゼンテーションをご覧ください