Bagaimana cara menggabungkan tanggal dan waktu ke datetime2 di SQL Server?

48

Diberikan komponen-komponen berikut

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

Apa cara terbaik menggabungkan mereka untuk menghasilkan DATETIME2(7)hasil dengan nilai '2013-10-13 23:59:59.9999999'?

Beberapa hal yang tidak berfungsi tercantum di bawah ini.


SELECT @D + @T 

Operan tanggal tipe data tidak valid untuk operator tambahan.


SELECT CAST(@D AS DATETIME2(7)) + @T 

Operand tipe data datetime2 tidak valid untuk operator tambahan.


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

Fungsi Dateiff mengakibatkan overflow. Jumlah suku cadang yang memisahkan dua instance tanggal / waktu terlalu besar. Cobalah menggunakan Dateiff dengan bagian data yang kurang tepat.

* Overflow dapat dihindari di Azure SQL Database dan SQL Server 2016, menggunakan DATEDIFF_BIG.


SELECT CAST(@D AS DATETIME) + @T 

Tipe data datetime dan waktu tidak kompatibel di add operator.


SELECT CAST(@D AS DATETIME) + CAST(@T AS DATETIME)

Mengembalikan hasil tetapi kehilangan presisi 2013-10-13 23:59:59.997

Martin Smith
sumber

Jawaban:

49

Ini tampaknya bekerja dan menjaga presisi juga:

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

The CASTuntuk DATETIME2(7)mengonversi TIME(7)nilai ( @T) ke DATETIME2tempat bagian tanggal berada '1900-01-01', yang merupakan nilai default tipe tanggal dan datetime (lihat datetime2dan komentar * di CASTdanCONVERT halaman di MSDN.)

* ... Ketika data karakter yang hanya mewakili tanggal atau hanya komponen waktu dilemparkan ke datetime atau tipe data waktu-kecil, komponen waktu yang tidak ditentukan diatur ke 00: 00: 00.000, dan komponen tanggal yang tidak ditentukan diatur ke 1900-01- 01 .

Fungsi DATEADD()dan DATEDIFF()mengurus sisanya, yaitu menambahkan perbedaan hari antara 1900-01-01dan DATEnilai ( @D).

Tes di: SQL-Fiddle


Sebagaimana diperhatikan oleh @Quandary , ekspresi di atas dianggap tidak-deterministik oleh SQL Server. Jika kita menginginkan ekspresi deterministik, katakan karena itu digunakan untuk PERSISTEDkolom, '19000101'** perlu diganti oleh 0atau 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)tidak deterministik karena ia melakukan konversi implisit string ke DATETIMEdan konversi dari string ke datetime hanya deterministik ketika gaya tertentu digunakan.

ypercubeᵀᴹ
sumber
8

Saya terlambat ke pesta tetapi pendekatan ini, walaupun mirip dengan jawaban @ ypercube , menghindari perlunya menggunakan konversi string apa pun (yang bisa lebih mahal daripada konversi tanggal), bersifat deterministik, dan harus terus bekerja jika MS pernah mengubah nilai tanggal default dari 1900-01-01 (meskipun mereka mungkin tidak akan mengubahnya):

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

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

Prinsipnya adalah bahwa dengan mengubah nilai waktu menjadi datetime2 dan kemudian tanggal, itu menghapus waktu dan menetapkan tanggal default, Anda kemudian memberi tanggal dengan nilai tanggal Anda untuk mendapatkan hari untuk ditambahkan, membuang waktu Anda ke datetime2 dan menambahkan hari-hari selanjutnya.

buku-buku jari
sumber
Alih-alih "DATEDIFF (DAY, @T, @D)" itu harus "DATEDIFF (DAY, 0, @D)". Hasilnya sama, tetapi membantu menghindari kebingungan. DateDiff (hari, ...) memberikan argumen ke jumlah hari int terendah, jadi @T dikonversi menjadi 0.
Dennis Gorelik
5

Untuk SQL Server 2012 dan di atasnya ada fungsi DATETIME2FROMPARTS . Ini memiliki bentuk ini:

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

Untuk data sampel yang diberikan ini menjadi

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

yang mengakibatkan

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

Bagian dapat diperoleh dengan menggunakan DATEPART () jika dimulai dari tipe data temporal, atau dari teks yang digunakan untuk membangun nilai sampel dalam pertanyaan.

Michael Green
sumber
0

Ini cukup bodoh dari SQL Server untuk tidak membiarkan contoh pertama Anda bekerja, dan ini akan tampak sangat bodoh juga, tapi ...

select convert(datetime2, convert(nvarchar(max), @d) + ' ' + convert(nvarchar(max), @t));
Atario
sumber
0
SELECT mydate=CAST(CAST(@D AS nvarchar(max)) + ' ' + 
                   CAST(@T AS nvarchar (max)) 
              AS DATETIME2);
Mihir
sumber
5
Sudahkah Anda menguji ini? Apakah itu bekerja? Apakah ini dipengaruhi oleh pengaturan bahasa?
ypercubeᵀᴹ
0

Saya sedang mencari sesuatu yang lain ketika saya mendarat di sini. Pertanyaannya cukup lama, tetapi ada beberapa komentar dan aktivitas terbaru. Saya pikir saya akan membagikan metode sederhana yang sangat mirip dengan jawaban yang diberikan @Atario, tetapi sedikit lebih pendek dan beberapa mungkin akan lebih mudah dibaca:

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

select cast(concat(@d, ' ', @t) as datetime2(7))
Brian Jorden
sumber
-3

Anda dapat memotong gips dengan DATE untuk memotong, lalu kembali ke DATETIME untuk menambahkan TIME

select CAST( cast(getdate() as date) as DATETIME)  + CAST(getdate() as TIME)
pengguna3448451
sumber
Triknya bagus, tetapi tidak menjawab pertanyaan di atas.
user259412