Comment combiner date et heure à datetime2 dans SQL Server?

48

Étant donné les composants suivants

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

Quel est le meilleur moyen de les combiner pour produire un DATETIME2(7)résultat avec de la valeur '2013-10-13 23:59:59.9999999'?

Certaines choses qui ne fonctionnent pas sont énumérées ci-dessous.


SELECT @D + @T 

La date du type de données d'opérande n'est pas valide pour l'opérateur add.


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

Le type de données d'opérande datetime2 n'est pas valide pour l'opérateur add.


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

La fonction de datiff a entraîné un débordement. Le nombre de dates séparant deux instances de date / heure est trop grand. Essayez d’utiliser datiff avec une date moins précise.

* Le dépassement de capacité peut être évité dans Azure SQL Database et SQL Server 2016, à l'aide de DATEDIFF_BIG.


SELECT CAST(@D AS DATETIME) + @T 

Les types de données date / heure et heure sont incompatibles dans l'opérateur d'ajout.


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

Retourne un résultat mais perd de la précision 2013-10-13 23:59:59.997

Martin Smith
la source

Réponses:

49

Cela semble fonctionner et garder la précision aussi bien:

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

Le CASTto DATETIME2(7)convertit la TIME(7)valeur ( @T) en un DATETIME2emplacement où se trouve la partie date '1900-01-01', qui correspond à la valeur par défaut des types date et date / heure (voir datetime2et le commentaire * à CASTet laCONVERT page sur MSDN).

* ... Lorsque des données de caractère représentant uniquement des composants de date ou d'heure sont converties en types de données datetime ou smalldatetime, le composant d'heure non spécifié est défini sur 00: 00: 00.000 et le composant de date non spécifié est défini sur 1900-01- 01 .

La fonction DATEADD()et DATEDIFF()s'occupe du reste, c'est-à-dire en ajoutant la différence de jours entre le 1900-01-01et la DATEvaleur ( @D).

Testez à: SQL-Fiddle


Comme noté par @Quandary , l'expression ci-dessus est considérée comme non déterministe par SQL Server. Si nous voulons une expression déterministe, disons, car elle doit être utilisée pour une PERSISTEDcolonne, le '19000101'** doit être remplacé par 0ou 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)n'est pas déterministe, car une conversion implicite de la chaîne en DATETIMEet les conversions de chaînes en date-heure ne sont déterministes que lorsque des styles spécifiques sont utilisés.

ypercubeᵀᴹ
la source
8

Je suis en retard pour le parti mais cette approche, bien que similaire à celle de @ ypercube , évite de recourir à toute conversion de chaîne (qui peut être plus onéreuse que les conversions de date), est déterministe et devrait continuer à fonctionner si MS modifiait jamais le valeur de date par défaut du 1900-01-01 (même si cela ne changera probablement pas):

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

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

Le principe est qu'en convertissant la valeur time en datetime2, puis en date, il supprime le délai d'expiration et attribue la date par défaut, vous la datez ensuite avec votre valeur de date pour obtenir les jours à ajouter, transformez votre date en datetime2 et ajoutez le jours sur.

jointures
la source
Au lieu de "DATEDIFF (DAY, @T, @D)", il devrait être "DATEDIFF (DAY, 0, @D)". Le résultat est le même, mais aide à éviter la confusion. DateDiff (day, ...) convertit les arguments en nombre de jours le plus bas, de sorte que @T est toujours converti en 0.
Dennis Gorelik
5

Pour SQL Server 2012 et les versions ultérieures, il existe la fonction DATETIME2FROMPARTS . Il a cette forme:

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

Pour les données d'échantillon données, cela devient

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

qui se traduit par

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

Les parties peuvent être obtenues à l'aide de DATEPART () si vous partez de types de données temporels ou du texte utilisé pour construire les exemples de valeurs dans la question.

Michael Green
la source
0

C'est assez stupide de la part de SQL Server de ne pas laisser votre premier exemple fonctionner, et cela va paraître vraiment stupide aussi, mais…

select convert(datetime2, convert(nvarchar(max), @d) + ' ' + convert(nvarchar(max), @t));
Atario
la source
0
SELECT mydate=CAST(CAST(@D AS nvarchar(max)) + ' ' + 
                   CAST(@T AS nvarchar (max)) 
              AS DATETIME2);
Mihir
la source
5
Avez-vous testé cela? Est-ce que ça marche? Est-il affecté par les paramètres de langue?
Ypercubeᵀᴹ
0

Je cherchais autre chose quand j'ai atterri ici. La question est assez ancienne, mais il y a quelques commentaires et activités récents. Je pensais partager une méthode simple, très similaire à la réponse donnée par @Antario, mais un peu plus courte et que certains trouveraient plus facile à lire:

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

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

Vous pouvez tronquer le cast avec DATE pour le tronquer, puis revenir à DATETIME pour ajouter le TIME.

select CAST( cast(getdate() as date) as DATETIME)  + CAST(getdate() as TIME)
utilisateur3448451
la source
Le truc est bon, mais il ne répond pas à la question du haut.
Peter dit réintégrer Monica le