web-dev-qa-db-ja.com

SQL Serverで日付と時刻をdatetime2に組み合わせる方法は?

次のコンポーネントを考える

_DECLARE @D DATE = '2013-10-13'
DECLARE @T TIME(7) = '23:59:59.9999999'
_

それらを組み合わせてDATETIME2(7)結果を値_'2013-10-13 23:59:59.9999999'_で生成する最良の方法は何ですか?

機能しないいくつかのことを以下に示します。


_SELECT @D + @T 
_

オペランドのデータ型日付は、追加演算子には無効です。


_SELECT CAST(@D AS DATETIME2(7)) + @T 
_

オペランドのデータ型datetime2は、add演算子には無効です。


_SELECT DATEADD(NANOSECOND,DATEDIFF(NANOSECOND,CAST('00:00:00.0000000' AS TIME),@T),@D)
_

Datediff関数がオーバーフローを引き起こしました。 2つの日付/時刻インスタンスを区切る日付部分の数が多すぎます。精度の低いdatepartでdatediffを使用してみてください。

* Azure SQL DatabaseおよびSQL Server 2016では、 _DATEDIFF_BIG_ を使用してオーバーフローを回避できます。


_SELECT CAST(@D AS DATETIME) + @T 
_

データ型datetimeとtimeは、add演算子では互換性がありません。


_SELECT CAST(@D AS DATETIME) + CAST(@T AS DATETIME)
_

結果を返しますが精度を失います_2013-10-13 23:59:59.997_

51
Martin Smith

これは機能し、精度も維持するようです:

_SELECT DATEADD(day, DATEDIFF(day,'19000101',@D), CAST(@T AS DATETIME2(7)))
_

CAST to DATETIME2(7)は、TIME(7)値(_@T_)を_DATETIME2_に変換します。ここで、日付部分は_'1900-01-01'_、これは、日付および日時タイプのデフォルト値です( _datetime2_ およびコメントを参照)。* CASTおよびCONVERT MSDNのページ。)

* ...日付コンポーネントまたは時刻コンポーネントのみを表す文字データがdatetimeまたはsmalldatetimeデータ型にキャストされる場合、未指定の時間コンポーネントは00:00:00.000に設定され、未指定日付コンポーネントは1900-01-01に設定されています。

DATEADD()DATEDIFF() functionが残りの処理を行います。つまり、_1900-01-01_とDATE値(_@D_ )。

テスト: SQL-Fiddle


@ Quandary が示すように、上記の式はSQL Serverによって決定論的ではないと見なされます。決定的な式が必要な場合、たとえば、それがPERSISTED列に使用されるため、_'19000101'_** _0_またはCONVERT(DATE, '19000101', 112)で置き換える必要があります:

_CREATE TABLE date_time
( d DATE NOT NULL,
  t TIME(7) NOT NULL,
  dt AS DATEADD(day, 
                DATEDIFF(day, CONVERT(DATE, '19000101', 112), d), 
                CAST(t AS DATETIME2(7))
               ) PERSISTED
) ;
_

**:DATEDIFF(day, '19000101', d)は、文字列からDATETIMEへの暗黙の変換を行い、文字列から日時への変換は deterministic 特定のスタイルの場合のみであるため、確定的ではありません中古。

52
ypercubeᵀᴹ

私はパーティーに遅れますが、このアプローチは @ ypercubeの回答 に似ていますが、文字列変換(日付変換よりもコストがかかる可能性があります)を使用する必要がなく、確定的であり、続行する必要がありますMSがデフォルトの日付値を1900-01-01から変更した場合に機能します(おそらくこれを変更しない場合でも)。

DECLARE @D DATE = SYSUTCDATETIME()
, @T TIME = SYSUTCDATETIME();

SELECT DATEADD(DAY, DATEDIFF(DAY, @T, @D), CONVERT(DATETIME2, @T));

原則は、時刻の値をdatetime2に変換してから日付に変換することで、タイムアウトを取り除き、デフォルトの日付を割り当てます。次に、これを日付の値とdatediffして、追加する日数を取得し、時刻をdatetime2にキャストして、日。

9
knuckles

SQL Server 2012以降の場合、 DATETIME2FROMPARTS 関数があります。それはこの形をしています:

DATETIME2FROMPARTS(year, month, day, hour, minute, seconds, fractions, precision)

与えられたサンプルデータの場合、これは次のようになります。

select Answer = DATETIME2FROMPARTS(2013, 10, 13, 23, 59, 59, 9999999, 7);

その結果

Answer
---------------------------
2013-10-13 23:59:59.9999999

時間データ型から開始する場合、または質問のサンプル値を構成するために使用するテキストから開始する場合、パーツは DATEPART() を使用して取得できます。

5
Michael Green

私がここに着いたとき、私は何か他のものを探していました。質問はかなり古いですが、いくつかの最近のコメントと活動があります。 @Atarioの回答と非常によく似た簡単な方法を共有したいと思いましたが、少し短く、一部は読みやすくなるかもしれません。

declare @d date = '2013-10-13'
declare @t time(7) = '23:59:59.9999999'

select cast(concat(@d, ' ', @t) as datetime2(7))
2
Brian Jorden

最初の例を機能させないのはSQL Serverのかなり愚かなことであり、これも非常に馬鹿げているように思われますが…

select convert(datetime2, convert(nvarchar(max), @d) + ' ' + convert(nvarchar(max), @t));
0
Atario
SELECT mydate=CAST(CAST(@D AS nvarchar(max)) + ' ' + 
                   CAST(@T AS nvarchar (max)) 
              AS DATETIME2);
0
Mihir