web-dev-qa-db-ja.com

日時の小数秒

Sybase ASE 15.7ドキュメント からdatetimeデータ型の説明を読んでいます:

datetime列は、1753年1月1日から9999年12月31日までの日付を保持します。datetime値は、このレベルの粒度をサポートするプラットフォームでは1/300秒まで正確です。

上記は非常に混乱します。まず、これは、「このレベルの粒度をサポートするプラットフォーム上で」警告が追加される唯一のデータ型です。これは正確に何を意味し、私のプラットフォームが「このレベルの細分性をサポートする」ものであるかどうかをどのようにして見つけますか?

さらに、1/300分の1秒を正確に格納できるということは、何を効果的に意味するのか私には明確ではありません。もっともらしいタイプが Java.sql.Timestamp であるJDBCを使用してデータベースにアクセスしています。このタイプでは、ナノ秒の精度まで取得できます。ただし、一般的な場合、300で除算するには10進法で無限の桁が必要なので、1/300秒として表される値を保持するには、事実上、小数のナノ秒(無限の10進数)が必要です。したがって、これは、無視できる程度の精度ではなく、サーバーに格納されている値を取得できないことを意味します。

最後に、次のクエリを実行すると、

SELECT convert(char(32), submission_date, 139) FROM some..table

次のような値が表示されます。

Jan  6 2014 12:36:12.420000     
Sep 12 2013 13:44:57.100000     
Sep 10 2014 13:47:02.240000     
Sep 10 2014 13:47:07.850000     
Sep 10 2014 13:47:13.346000     
Sep 10 2014 13:47:19.033000     
Sep 10 2014 13:47:24.533000     
Sep 10 2014 13:47:30.030000     
Sep 10 2014 13:47:35.636000     
Sep 10 2014 13:47:41.136000     
Sep 10 2014 13:47:46.750000     
Sep 10 2014 13:47:52.240000     
Sep 25 2014 09:01:18.426000     

これらの値は、1000分の1秒秒のみが維持されることを示しているようです(1/300秒ではありません-これには、1000分の1の小数が必要です)。 。サーバーが「1/300秒に正確に」値を内部的に保存する場合は、10進表記への変換でallを使用すると予想されます。利用可能な小数点以下の桁数(3/300秒、30/300秒、150/300秒、および10進法で桁数を無制限にする必要のないその他の少数のエッジの場合を除く)。

Adaptive Server Enterpriseのdatetime(およびSQL Server。日時型を含む共通コードベースを共有しているため)は、8バイトを使用して格納されます。日付用に4バイト、時刻用に4バイト。これは、日時のバイナリバージョンを見るとわかります。

SELECT CONVERT(varbinary(8), CONVERT(datetime, '1753-01-01T00:00:00.000'), 0)
SELECT CONVERT(varbinary(8), CONVERT(datetime, '1900-01-01T00:00:00.000'), 0)
SELECT CONVERT(varbinary(8), CONVERT(datetime, '1900-01-02T00:00:00.000'), 0)
SELECT CONVERT(varbinary(8), CONVERT(datetime, '3000-12-31T23:59:59.997'), 0)

4つのバイナリ値は次のとおりです。

0xFFFF2E4600000000 
 0x0000000000000000 
 0x0000000100000000 
 0x000622D3018B81FF

次の例では、1/300秒が機能する場所を示しています。

SELECT CONVERT(varbinary(8), CONVERT(datetime, '1900-01-01T00:00:00.000'), 0) 
SELECT CONVERT(varbinary(8), CONVERT(datetime, '1900-01-02T00:00:00.000'), 0) 
SELECT CONVERT(varbinary(8), CONVERT(datetime, '1900-01-02T00:00:00.003'), 0) 

2番目と3番目の値の違いは1つです。

0x0000000000000000 
 0x0000000100000000 
 0x0000000100000001

したがって、日付は最上位の4バイトに格納されます。時間は最下位の4バイトに格納されます。 4バイトのストレージでは、3ミリ秒(1/300秒)を超える精度に移行できますが、これが実際に使用されるすべての精度です。

SQL Serverでは、datetime2(7)データ型を使用して、100nsの精度で7桁まで精度を下げることができます。

SELECT CONVERT(varbinary(16), CONVERT(datetime2(7), '1900-01-01T00:00:00.0000000'), 0)
SELECT CONVERT(varbinary(16), CONVERT(datetime2(7), '1900-01-01T00:00:00.0000001'), 0)
SELECT CONVERT(varbinary(16), CONVERT(datetime2(7), '1900-01-01T00:00:00.0000002'), 0)

これらの値の格納は少し異なりますが、バイナリ値が増加していることがわかります。

0x0700000000005B950A 
 0x0701000000005B950A 
 0x0702000000005B950A

私はSybase ISQLクライアントを使用しています。 Sybase CTISQLユーティリティ/15.7/P-EBF20996 SP100/DRV.15.7.0.10

余談ですが、Adaptive Server Enterprise(ASE)とSQL Serverがdatetime値を丸める方法には小さな違いがあります。 SQL Serverでは、ミリ秒は0.0000.003、および0.007に丸められますが、ASEでは、0.0000.003、および0.006に丸められます。これをASEで確認するには、次のクエリを実行します。

SELECT CONVERT(varchar(50), CONVERT(datetime, N'2017-01-01T23:59:59.997'), 139);

どちらが戻ります:

Jan  1 2017 23:59:59.996000

SQL Serverでは、同等のコードSELECT CONVERT(varchar(50), CONVERT(datetime, N'2017-01-01T23:59:59.997'), 109);は次を返します。

Jan  1 2017 11:59:59:997PM
6
Max Vernon

この答え に基づいて、正確なエンコーディングが不透明で直感に反することは明らかです。 datetimeが値を保持できるというaccurate to 1/300 secondの効果に関するSybaseのドキュメントのステートメントが誤解を招きやすく不正確であることも明らかです。

ドキュメントが言っているべきことは、datetimeの解像度/精度/粒度(お好きな用語を選択)がおよそ1/300秒または約3 ms

これを裏付けるテストを実行しました。約1000個のdatetime値(ユーザー入力に応じてランダムに生成される)が含まれるテーブルで、発生するミリ秒の値がいくつあるかを確認したいと思いました。

 SELECT COUNT(*) FROM
 (
   SELECT DISTINCT y.millis FROM
   (
      SELECT RIGHT(RTRIM(LTRIM(x.submissionDate)), 6) AS millis FROM (
         SELECT convert(char(32), submission_date, 139) AS submissionDate FROM someTable
      ) x
   ) y
 ) z

上記のクエリの最も内側のSQLステートメントも私の質問で使用され、私の質問に投稿した値が得られます。

複合クエリの結果は237で、上記と一致しています。

さらに、 SQL Serverのドキュメントdatetimeの実装は、Sybase ASEとSQL Serverの間の共有コードベースの一部であるため適用されます)には、次のように書かれています。


画像は次のとおりです。

enter image description here


同じページで、次の点にも注意してください。

datetimeはANSIまたはISO 8601に準拠していません。

全体的に私はそのような毛深いタイプを使用することはアンチパターンだと思います。エポック以降の秒数を保持するためにINTを単純に使用しない理由、またはエポック以降のミリ秒またはマイクロ秒またはナノ秒を保持するためのBIGINTを使用しない理由を理解することはできません。


徹底的なテスト後に更新

私はこの問題について徹底的なテストを行う時間を見つけることができ、なんとかそれの底に着くことができました。これが私の調査結果です(Sybase ASE 15.7に対してテストされましたが、SQL Serverにも同じ結果が当てはまると思います)。

サーバーがクライアントから提供されたDatetime値を1/300秒の解像度で保持していることを実験的に証明しました。次のDDLでテーブルを作成しました。

DROP TABLE DatetimeTest;
CREATE TABLE DatetimeTest (
    d DATETIME NOT NULL
);

…そして、(Javaのドライバプログラムを使用して)ミリ秒単位で1秒の1000分の1の増分で1000の値を入力しました。次のクエリを実行します。

 SELECT COUNT(*) FROM
 (
 SELECT DISTINCT y.millis FROM
 (
 SELECT RIGHT(RTRIM(LTRIM(x.d)), 6) AS millis FROM (
 SELECT convert(char(32), d, 139) AS d FROM DatetimeTest
 ) x
 ) y
 ) z                                                  

…は正確に300をもたらしました。

上記は、サーバーが小数部の300の異なる値のみを保持できることを示しています。これは、保持される実際の値が1/300秒の倍数であることを証明するものではありませんが、妥当な指標です。

上記の意味するところは、クライアントからサーバーに提供される秒の小数部のストレージは、損失が生じる可能性があるということです。そのため、私はさらにテストを行って、損失の程度を正確に確認しました。私が見つけたものは次のとおりです:

  • クライアントで2番目の精度の時間値を無損失でサーバーに格納できます(問題ありませんでした)
  • 10分の1秒の精度を持つクライアントの時間値は、サーバーにロスレスで保存できます。
  • 100分の1秒の精度を持つクライアントの時間値は、サーバーにロスレスで保存できます。
  • クライアントでミリ秒精度の時間値の10%のみがサーバーにロスレスで保存できます(100分の1秒に相当するもの)。残りの90%の値については、クライアントが提供したものと、サーバーが最大3ミリ秒(およびそれを含む)を保存したものとの違いを確認できます。

上記は、時間コンポーネントを1/300秒の整数として格納するサーバーの仮説と一致しています。

  • 10分の1秒の値はexactlyの形式で分数として表現できますn/300
  • 100分の1秒の値は次のようになります正確にn/300の形式で分数として表されます
  • exactlyは、可能なミリ秒値の10%のみがn/300の形式で分数として表されます。

最後に、クエリとしてのconvert関数に問題があることは明らかです。

SELECT convert(char(32), d, 139) AS d FROM DatetimeTest

…小数点第3位を超えるとゼロが報告されますが、これは明らかに正しくないため、混乱を招きます。しかし、それが上記を反証しているとは思わない。

アップデートII

奇妙なことに、ミリ秒値の非正確なサーバーマッピングについて私のテストコードによって報告された割合は、テストで Sybase jConnect JDBCドライバー を使用した場合にのみ90%になります。 オープンソースのJTDS JDBCドライバー の場合、70%の改善されたすばらしいラウンドが適用されます。基本的には、テストロジックは、クライアント側でミリ秒の精度で値を作成し、サーバーに保存してから、クライアントから再度読み取り、何が返されるかを確認します。このテストの配置により、クライアント側のドライバーは、途中でサーバーの値を穏やかに修正できます(クライアント側のコードがデータベースから読み取るとき)。

明らかに、JTDSドライバーは、Sybaseサーバーが小数秒のコンポーネントを格納する特異な方法をより巧妙に説明でき、個別のサーバー値のall 300(逆の丸めエラーを補正)方向-サーバーからクライアントへ-必要に応じて)、0から1000の範囲のミリ秒の値に正確に対応する100だけではありません。

いずれにせよこれはサーバー側の事実を変更しないであり、クライアント側の言語とDatetimeをプルするために使用されるドライバーを制御できる場合にのみ関連します。サーバー外の値。

単純にするためにその部分はスキップするつもりでしたが、完全を期すために追加したいと思ったのです。また、これらの数値(300、90%、70%)がすべてナラティブに当てはまるため、Datetime値のサーバーストレージモデルを裏付けています。しかし、人生は短すぎるので、次に進む時間です。