Jak połączyć datę i godzinę z datetime2 w SQL Server?


48

Biorąc pod uwagę następujące elementy

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

Jaki jest najlepszy sposób na połączenie ich w celu uzyskania DATETIME2(7)wyniku o wartości '2013-10-13 23:59:59.9999999'?

Niektóre rzeczy, które nie działają, są wymienione poniżej.


SELECT @D + @T 

Data typu argumentu operand jest niepoprawna dla operatora dodawania.


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

Operand typ danych datetime2 jest niepoprawny dla operatora dodawania.


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

Funkcja datowania spowodowała przepełnienie. Liczba elementów daty oddzielających dwa wystąpienia daty / godziny jest zbyt duża. Spróbuj użyć daty z mniej dokładną datą.

* Przepełnienia można uniknąć w usłudze Azure SQL Database i SQL Server 2016 za pomocą DATEDIFF_BIG.


SELECT CAST(@D AS DATETIME) + @T 

Typy danych data i godzina są niezgodne w operatorze dodawania.


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

Zwraca wynik, ale traci precyzję 2013-10-13 23:59:59.997

Odpowiedzi:


49

Wydaje się, że działa to również z zachowaniem precyzji:

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

CASTDo DATETIME2(7)konwertuje TIME(7)wartość ( @T) Do DATETIME2gdzie część jest data '1900-01-01', która jest wartością domyślną datą i datetime typów (patrz datetime2i skomentować * na CASTiCONVERT strona na MSDN).

* ... Gdy dane znakowe reprezentujące tylko komponenty daty lub tylko godziny są rzutowane na typy danych datetime lub smalldatetime, nieokreślony komponent czasu jest ustawiony na 00: 00: 00.000, a nieokreślony komponent daty jest ustawiony na 1900-01- 01 .

DATEADD()I DATEDIFF()funkcja dbać o resztę, czyli dodając różnicę w dniach pomiędzy 1900-01-01a DATEwartości ( @D).

Test na: SQL-Fiddle


Jak zauważył @Quandary , powyższe wyrażenie jest uważane przez SQL Server za niedeterministyczne. Jeśli chcemy wyrażenia deterministycznego, powiedzmy, ponieważ należy go użyć dla PERSISTEDkolumny, '19000101'** należy zastąpić przez : 0lub 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)nie jest deterministyczny, ponieważ dokonuje niejawnej konwersji łańcucha na, DATETIMEa konwersje ciągów na datę i godzinę są deterministyczne tylko wtedy, gdy stosowane są określone style.


8

Jestem spóźniony na imprezę, ale to podejście, chociaż podobne do odpowiedzi @ ypercube , pozwala uniknąć konieczności użycia jakiejkolwiek konwersji ciągów (które mogą być droższe niż konwersje daty), jest deterministyczne i powinno nadal działać, jeśli stwardnienie rozsiane kiedykolwiek zmieni domyślna wartość daty od 1900-01-01 (chociaż prawdopodobnie tego nie zmienią):

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

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

Zasada jest taka, że ​​konwertując wartość czasu na datetime2, a następnie na datę, usuwa limit czasu i przypisuje domyślną datę, a następnie datujesz z tą wartością daty, aby uzyskać dni do dodania, rzuć swój czas na datetime2 i dodaj dni później


Zamiast „DATEDIFF (DZIEŃ, @T, @D)” powinna być „DATEDIFF (DZIEŃ, 0, @D)”. Wynik jest taki sam, ale pomaga uniknąć zamieszania. DateDiff (dzień, ...) przekazuje argumenty do najniższej liczby int dni, więc @T i tak jest konwertowane na 0.
Dennis Gorelik

5

W przypadku SQL Server 2012 i nowszych dostępna jest funkcja DATETIME2FROMPARTS . Ma ten formularz:

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

Dla danych przykładowych staje się

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

Co skutkuje w

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

Części można uzyskać za pomocą DATEPART (), jeśli zaczynamy od czasowych typów danych lub z tekstu użytego do skonstruowania przykładowych wartości w pytaniu.


0

To głupie, że SQL Server nie pozwala, aby twój pierwszy przykład działał, i to też wydaje się naprawdę głupie, ale…

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
Testowałeś to? Czy to działa? Czy wpływają na to ustawienia języka?
ypercubeᵀᴹ

0

Kiedy tu wylądowałem, szukałem czegoś innego. Pytanie jest dość stare, ale ostatnio pojawiły się komentarze i działania. Pomyślałem, że podzielę się prostą metodą, która jest bardzo podobna do odpowiedzi udzielonej przez @Atario, ale nieco krótsza, a niektóre mogą być łatwiejsze do odczytania:

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

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

-3

Możesz obciąć rzutem do DATE, aby obciąć, a następnie z powrotem do DATETIME, aby dodać CZAS

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

Sztuczka jest dobra, ale nie odpowiada na pytanie na górze.
user259412
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.