Як поєднувати дату та час до datetime2 у SQL Server?


48

З огляду на наступні компоненти

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 недійсний для оператора додавання.


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

Функція dateiff призвела до переповнення. Кількість розділів дат, що розділяють два екземпляри дати / часу, занадто велика. Спробуйте використовувати dateiff з менш точною датою.

* Переповнення можна уникнути в базах даних Azure SQL та SQL Server 2016, використовуючи DATEDIFF_BIG.


SELECT CAST(@D AS DATETIME) + @T 

Типи даних дата та час несумісні в операторі додавання.


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

Повертає результат, але втрачає точність 2013-10-13 23:59:59.997

Відповіді:


49

Це, здається, працює і підтримує точність також:

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

CASTДо DATETIME2(7)Перетворює TIME(7)значення ( @T) до DATETIME2де дата частина '1900-01-01', яка є значенням за замовчуванням дати і DateTime типу (див , datetime2і коментар * на CASTіCONVERT сторінки в MSDN) .

* ... Коли дані символів, що представляють лише компоненти дати або лише час, передані типам даних дати або малим часу, невказаний компонент часу встановлюється на 00: 00: 00.000, а для не визначеного компонента дати - 1900-01- 01 .

Функція DATEADD()та DATEDIFF()функція піклуються про інше, тобто додають різницю в днях між значенням 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а перетворення з рядків у дату часу визначаються лише тоді, коли використовуються конкретні стилі.


8

Я запізнююся на партію, але такий підхід, хоча і схожий на відповідь @ ypercube , уникає необхідності використовувати будь-яке перетворення рядків (яке може бути дорожчим за перетворення дати), є детермінованим і повинен продовжувати працювати, якщо MS коли-небудь змінить значення дати за замовчуванням з 1900-01-01 (навіть якщо вони, ймовірно, не зможуть змінити це):

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

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

Принцип полягає в тому, що, перетворюючи значення часу в datetime2, а потім на дату, воно знімає час та призначає дату за замовчуванням, потім ви датуєте це значенням дати, щоб отримати дні для додавання, віддати час до datetime2 і додати днів.


Замість "DATEDIFF (DAY, @T, @D)" має бути "DATEDIFF (DAY, 0, @D)". Результат такий же, але допомагає уникнути плутанини. DateDiff (день, ...) наводить аргументи на найменшу кількість int кількості днів, тому @T все одно перетворюється на 0.
Денніс Горелік

5

Для 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 (), якщо починати з тимчасових типів даних або з тексту, який використовується для побудови вибіркових значень у запитанні.


0

Дуже нерозумно SQL Server не дозволяти вашому першому прикладу працювати, і це теж здасться дуже глумим, але ...

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
Ви перевірили це? Це працює? На це впливають налаштування мови?
ypercubeᵀᴹ

0

Я шукав щось інше, коли я приземлився тут. Питання досить старе, але є деякі останні коментарі та активність. Думав, я поділюсь простим методом, який дуже схожий на відповідь, який дав @Atario, але трохи коротший, і деякі з них можуть бути легшими для читання:

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

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

-3

Ви можете урізати клавішу "cast" в DATE, щоб усікати, а потім повернутися до DATETIME, щоб додати час

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

Трюк хороший, але він не відповідає на питання вгорі.
користувач259412
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.