Làm cách nào để kết hợp ngày và giờ với datetime2 trong SQL Server?


48

Cho các thành phần sau

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

Cách tốt nhất để kết hợp chúng để tạo ra một DATETIME2(7)kết quả với giá trị là '2013-10-13 23:59:59.9999999'gì?

Một số thứ không hoạt động được liệt kê dưới đây.


SELECT @D + @T 

Ngày loại dữ liệu toán tử không hợp lệ cho toán tử thêm.


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

Kiểu dữ liệu toán tử datetime2 không hợp lệ cho toán tử thêm.


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

Hàm dateiff dẫn đến tràn. Số lượng ngày tháng phân tách hai trường hợp ngày / thời gian là quá lớn. Cố gắng sử dụng dateiff với một datepart ít chính xác hơn.

* Có thể tránh tràn tràn trong Azure SQL Database và SQL Server 2016, bằng cách sử dụng DATEDIFF_BIG.


SELECT CAST(@D AS DATETIME) + @T 

Các kiểu dữ liệu datetime và time không tương thích trong toán tử add.


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

Trả về kết quả nhưng mất độ chính xác 2013-10-13 23:59:59.997

Câu trả lời:


49

Điều này dường như hoạt động và giữ độ chính xác là tốt:

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

Các CASTđể DATETIME2(7)cải các TIME(7)giá trị ( @T) vào một DATETIME2nơi phần ngày là '1900-01-01', đó là giá trị mặc định của ngày và datetime loại (xem datetime2và nhận xét *CASTCONVERT trang tại MSDN.)

* ... Khi dữ liệu ký tự chỉ đại diện cho các thành phần ngày hoặc chỉ thời gian được truyền thành kiểu dữ liệu thời gian hoặc thời gian nhỏ, thành phần thời gian không xác định được đặt thành 00: 00: 00.000 và thành phần ngày không xác định được đặt thành 1900-01- 01 .

Hàm DATEADD()DATEDIFF()hàm đảm nhiệm phần còn lại, tức là thêm chênh lệch số ngày giữa giá trị 1900-01-01DATEgiá trị ( @D).

Kiểm tra tại: SQL-Fiddle


Như @Quandary nhận thấy , biểu thức trên được SQL Server coi là không xác định. Nếu chúng ta muốn một biểu thức xác định, giả sử vì nó được sử dụng cho một PERSISTEDcột, '19000101'** cần được thay thế bằng 0hoặc 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)không mang tính quyết định vì nó thực hiện chuyển đổi ngầm định chuỗi DATETIMEvà chuyển đổi từ chuỗi sang datetime chỉ mang tính xác định khi các kiểu cụ thể được sử dụng.


8

Tôi đến bữa tiệc muộn nhưng cách tiếp cận này, tương tự như câu trả lời của @ ypercube , tránh sự cần thiết phải sử dụng bất kỳ chuyển đổi chuỗi nào (có thể tốn kém hơn chuyển đổi ngày), mang tính quyết định và sẽ tiếp tục hoạt động nếu MS thay đổi giá trị ngày mặc định từ 1900-01-01 (mặc dù có thể họ sẽ không thay đổi điều này):

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

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

Nguyên tắc là bằng cách chuyển đổi giá trị thời gian thành datetime2 và sau đó đến ngày, nó sẽ loại bỏ thời gian và gán ngày mặc định, sau đó bạn định ngày này với giá trị ngày của bạn để lấy ngày, thêm thời gian của bạn vào datetime2 và thêm ngày về


Thay vì "DATEDIFF (NGÀY, @T, @D)", nó phải là "DATEDIFF (NGÀY, 0, @D)". Kết quả là như nhau, nhưng giúp tránh nhầm lẫn. DateDiff (ngày, ...) chuyển các đối số thành số ngày int thấp nhất, vì vậy @T được chuyển đổi thành 0.
Dennis Gorelik

5

Đối với SQL Server 2012 trở lên, có chức năng DATETIME2FROMPARTS . Nó có dạng này:

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

Đối với dữ liệu mẫu đã cho, điều này trở thành

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

kết quả trong

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

Các phần có thể được lấy bằng cách sử dụng DATEPART () nếu bắt đầu từ các kiểu dữ liệu tạm thời hoặc từ văn bản được sử dụng để xây dựng các giá trị mẫu trong câu hỏi.


0

SQL Server khá ngu ngốc khi không để ví dụ đầu tiên của bạn hoạt động và điều này dường như cũng thực sự ngu ngốc, nhưng

select convert(datetime2, convert(nvarchar(max), @d) + ' ' + convert(nvarchar(max), @t));

0
SELECT mydate=CAST(CAST(@D AS nvarchar(max)) + ' ' + 
                   CAST(@T AS nvarchar (max)) 
              AS DATETIME2);

5
Bạn đã thử nghiệm điều này? Nó có hoạt động không? Có bị ảnh hưởng bởi cài đặt ngôn ngữ?
ypercubeᵀᴹ

0

Tôi đang tìm kiếm một cái gì đó khác khi tôi hạ cánh ở đây. Câu hỏi khá cũ, nhưng có một số ý kiến ​​và hoạt động gần đây. Tôi nghĩ rằng tôi sẽ chia sẻ một phương pháp đơn giản rất giống với câu trả lời mà @Atario đã đưa ra, nhưng ngắn hơn một chút và một số có thể dễ đọc hơn:

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

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

-3

Bạn có thể cắt ngắn với cast thành DATE để cắt ngắn, sau đó quay lại DATETIME để thêm TIME

select CAST( cast(getdate() as date) as DATETIME)  + CAST(getdate() as TIME)

Bí quyết là tốt, nhưng nó không trả lời câu hỏi trên đầu.
dùng259412
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.