web-dev-qa-db-fra.com

Comment combiner la date et l'heure à datetime2 dans SQL Server?

Étant donné les composants suivants

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

Quelle est la meilleure façon de les combiner pour produire un résultat DATETIME2(7) avec la valeur '2013-10-13 23:59:59.9999999'?

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


SELECT @D + @T 

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


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

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


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

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

* Le débordement peut être évité dans Azure SQL Database et SQL Server 2016, en utilisant DATEDIFF_BIG .


SELECT CAST(@D AS DATETIME) + @T 

Les types de données datetime et time sont incompatibles dans l'opérateur d'ajout.


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

Renvoie un résultat mais perd sa précision 2013-10-13 23:59:59.997

51
Martin Smith

Cela semble fonctionner et garder la précision aussi:

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

CAST en DATETIME2(7) convertit la valeur de TIME(7) (@T) En DATETIME2 Où la partie date est '1900-01-01', qui est la valeur par défaut des types date et datetime (voir datetime2 et le commentaire* sur CAST et CONVERT page sur MSDN.)

* ... Lorsque les données de caractères qui représentent 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 non spécifié le composant date 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 la valeur 1900-01-01 Et la valeur DATE (@D).

Testez à: SQL-Fiddle


Comme indiqué 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 parce qu'elle doit être utilisée pour une colonne PERSISTED, le '19000101'** doit être remplacé par 0 ou 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 il effectue une conversion implicite de la chaîne en DATETIME et les conversions de chaînes en datetime sont déterministes uniquement lorsque des styles spécifiques sont utilisé.

52
ypercubeᵀᴹ

Je suis en retard à la fête, mais cette approche, bien que similaire à réponse de @ ypercube , évite d'avoir à utiliser n'importe quelle conversion de chaîne (qui peut être plus coûteuse que les conversions de date), est déterministe et devrait continuer pour fonctionner si MS change jamais la valeur de date par défaut de 1900-01-01 (même s'ils ne changeront probablement pas cela):

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 de l'heure en datetime2 puis en date, il supprime le délai et attribue la date par défaut, vous avez ensuite daté ceci avec votre valeur de date pour obtenir les jours à ajouter, transformez votre heure en datetime2 et ajoutez le jours après.

9
knuckles

Pour SQL Server 2012 et supérieur, 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);

ce qui se traduit par

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

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

5
Michael Green

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 qui est très similaire à la réponse donnée par @Antario, mais un peu plus courte et certains pourraient trouver 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))
2
Brian Jorden

C'est assez stupide de SQL Server de ne pas laisser votre premier exemple fonctionner, et cela va sembler vraiment stupide aussi, mais…

select convert(datetime2, convert(nvarchar(max), @d) + ' ' + convert(nvarchar(max), @t));
0
Atario
SELECT mydate=CAST(CAST(@D AS nvarchar(max)) + ' ' + 
                   CAST(@T AS nvarchar (max)) 
              AS DATETIME2);
0
Mihir