Créer une date à partir du jour mois et année avec T-SQL

265

J'essaie de convertir une date avec des parties individuelles telles que 12, 1, 2007 en une date / heure dans SQL Server 2005. J'ai essayé ce qui suit:

CAST(DATEPART(year, DATE)+'-'+ DATEPART(month, DATE) +'-'+ DATEPART(day, DATE) AS DATETIME)

mais cela se traduit par une mauvaise date. Quelle est la bonne façon de transformer les trois valeurs de date en un format datetime approprié.

Brandon
la source
8
Veuillez envisager de modifier votre réponse acceptée weblogs.sqlteam.com/jeffs/archive/2007/09/10/…
Brian Webster
DATEFROMPARTS (année, mois, jour)
Kate

Réponses:

175

En supposant que y, m, dtout est int, que diriez-vous:

CAST(CAST(y AS varchar) + '-' + CAST(m AS varchar) + '-' + CAST(d AS varchar) AS DATETIME)

Veuillez consulter mon autre réponse pour SQL Server 2012 et supérieur

Cade Roux
la source
Un mauvais. Composez-moi à partir de la date du 1er janvier 0001
Oleg Dok
24
Oleg SQL Server DateTime ne remonte pas plus loin que le 1753-01-01.
CodeMonkey
2
Cette réponse dépend des paramètres de format de date, qui dépendent des paramètres régionaux de votre serveur si vous ne spécifiez pas. Le yyyymmddformat fonctionne indépendamment de ces paramètres. "Une chaîne de six ou huit chiffres est toujours interprétée comme ymd ." docs.microsoft.com/en-us/sql/t-sql/data-types/… Voir cette réponse: stackoverflow.com/a/46064419/2266979
Riley Major
339

Essaye ça:

Declare @DayOfMonth TinyInt Set @DayOfMonth = 13
Declare @Month TinyInt Set @Month = 6
Declare @Year Integer Set @Year = 2006
-- ------------------------------------
Select DateAdd(day, @DayOfMonth - 1, 
          DateAdd(month, @Month - 1, 
              DateAdd(Year, @Year-1900, 0)))

Il fonctionne également, a l'avantage supplémentaire de ne pas effectuer de conversions de chaînes, il s'agit donc d'un traitement arithmétique pur (très rapide) et ne dépend d'aucun format de date. Cela capitalise sur le fait que la représentation interne de SQL Server pour les valeurs datetime et smalldatetime est de deux valeur de la partie dont la première partie est un entier représentant le nombre de jours depuis le 1er janvier 1900, et la deuxième partie est une fraction décimale représentant la partie fractionnaire d'un jour (pour l'heure) --- Donc, la valeur entière 0 (zéro ) se traduit toujours directement en minuit matin du 1er janvier 1900 ...

ou, grâce à la suggestion de @brinary,

Select DateAdd(yy, @Year-1900,  
       DateAdd(m,  @Month - 1, @DayOfMonth - 1)) 

Édité en octobre 2014. Comme noté par @cade Roux, SQL 2012 a maintenant une fonction intégrée:
DATEFROMPARTS(year, month, day)
cela fait la même chose.

Édité le 3 octobre 2016, (Merci à @bambams pour l'avoir remarqué et à @brinary pour l'avoir corrigé), La dernière solution, proposée par @brinary. ne semble pas fonctionner pendant les années bissextiles, sauf si l'addition des années est effectuée en premier

select dateadd(month, @Month - 1, 
     dateadd(year, @Year-1900, @DayOfMonth - 1)); 
Charles Bretana
la source
36
@Brandon, vous devez plutôt la marquer comme cette réponse. C'est le meilleur. Faites-le en tant que service pour d'autres lecteurs StackOverflow.
Bill Paetzke
3
Fonctionne pour les années bissextiles: sélectionnez dateadd (mm, (@ y-1900) * 12 + @m - 1,0) + (@ d-1)
caché le
8
Entraîne une valeur de date valide mais fausse quand une combinaison de valeurs invalide est passée @Year = 2001, par exemple , @Month = 13et @DayOfMonth = 32entraîne 2002-02-01T00:00:00.000. La réponse acceptée (par Cade Roux) génère une erreur, ce qui est plus utile.
quand
6
Vous n'avez pas besoin de commencer par zéro et d'ajouter des jours. Vous pouvez commencer directement avec @ DayOfMonth-1, puis ajouter les mois et les années. C'est un DateAdd () de moins!
brianary
2
ma tête tourne toujours - il n'y a vraiment pas de meilleure façon de le faire? (Je suis chargé de corriger une requête dans SQL Server 2005)
Peter Perháč
241

SQL Server 2012 a une nouvelle fonction DATEFROMPARTS merveilleuse et attendue depuis longtemps (qui générera une erreur si la date n'est pas valide - ma principale objection à une solution basée sur DATEADD à ce problème):

http://msdn.microsoft.com/en-us/library/hh213228.aspx

DATEFROMPARTS(ycolumn, mcolumn, dcolumn)

ou

DATEFROMPARTS(@y, @m, @d)
Cade Roux
la source
11
En outre, en référence à la question d'origine, où l'objet Datetime a été mentionné, il existe également une fonction appelée DATETIMEFROMPARTS: msdn.microsoft.com/pl-pl/library/hh213233%28v=sql.110%29.aspx
Maciej Jaśniaczyk
116

Ou en utilisant une seule fonction dateadd:

DECLARE @day int, @month int, @year int
SELECT @day = 4, @month = 3, @year = 2011

SELECT dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1)
Shrike
la source
4
meilleure réponse OMI. A tous les avantages de la réponse de Charles et est beaucoup plus court.
Michael
1
C'est de loin le plus propre et le plus simple. Et cela ne génère pas non plus d'erreur lorsque les valeurs de jour sont hors limites. Cependant, selon les circonstances, une erreur peut être souhaitée, alors sachez simplement que cela réduit au silence les valeurs de jour et de mois qui sont hors de la plage attendue.
Shawn Kovac
17

Sql Server 2012 a une fonction qui créera la date en fonction des pièces ( DATEFROMPARTS ). Pour le reste d'entre nous, voici une fonction db que j'ai créée qui déterminera la date des pièces (merci @Charles) ...

IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[func_DateFromParts]'))
    DROP FUNCTION [dbo].[func_DateFromParts]
GO

CREATE FUNCTION [dbo].[func_DateFromParts]
(
    @Year INT,
    @Month INT,
    @DayOfMonth INT,
    @Hour INT = 0,  -- based on 24 hour clock (add 12 for PM :)
    @Min INT = 0,
    @Sec INT = 0
)
RETURNS DATETIME
AS
BEGIN

    RETURN DATEADD(second, @Sec, 
            DATEADD(minute, @Min, 
            DATEADD(hour, @Hour,
            DATEADD(day, @DayOfMonth - 1, 
            DATEADD(month, @Month - 1, 
            DATEADD(Year, @Year-1900, 0))))))

END

GO

Vous pouvez l'appeler comme ça ...

SELECT dbo.func_DateFromParts(2013, 10, 4, 15, 50, DEFAULT)

Retour...

2013-10-04 15:50:00.000
Brian
la source
12

Essayez CONVERT au lieu de CAST.

CONVERT permet un troisième paramètre indiquant le format de la date.

La liste des formats est ici: http://msdn.microsoft.com/en-us/library/ms187928.aspx

Mettre à jour après qu'une autre réponse a été sélectionnée comme réponse "correcte":

Je ne comprends pas vraiment pourquoi une réponse est sélectionnée qui dépend clairement des paramètres NLS sur votre serveur, sans indiquer cette restriction.

devio
la source
Acceptez que le format doit être qualifié, par exemple CONVERT (datetime2, CAST (@year AS varchar) + '.' + CAST (@month AS varchar) + '.' + CAST (@day AS varchar), 102)
Tony Wall
9

Vous pouvez aussi utiliser

select DATEFROMPARTS(year, month, day) as ColDate, Col2, Col3 
From MyTable Where DATEFROMPARTS(year, month, day) Between @DateIni and @DateEnd

Fonctionne en SQL depuis ver.2012 et AzureSQL

Marcelo Lujan
la source
6

Il est plus sûr et plus propre d'utiliser un point de départ explicite '19000101'

create function dbo.fnDateTime2FromParts(@Year int, @Month int, @Day int, @Hour int, @Minute int, @Second int, @Nanosecond int)
returns datetime2
as
begin
    -- Note! SQL Server 2012 includes datetime2fromparts() function
    declare @output datetime2 = '19000101'
    set @output = dateadd(year      , @Year - 1900  , @output)
    set @output = dateadd(month     , @Month - 1    , @output)
    set @output = dateadd(day       , @Day - 1      , @output)
    set @output = dateadd(hour      , @Hour         , @output)
    set @output = dateadd(minute    , @Minute       , @output)
    set @output = dateadd(second    , @Second       , @output)
    set @output = dateadd(ns        , @Nanosecond   , @output)
    return @output
end
Jack
la source
Pourquoi ne pas utiliser juste declare @output datetime2 = 0et au lieu d' @Year - 1900utiliser @Year - DATEPART(year,0);? Cela fonctionne sans transtypage dans SQL Server 2008 et beaucoup plus clair.
tsionyx
Parce que ça ne marchera pas. Vous ne pouvez pas convertir 0 en datetime2. Votre code renverra "Opérande de type clash: int est incompatible avec datetime2"
Jack
4

Si vous ne voulez pas en garder les chaînes, cela fonctionne aussi (mettez-le dans une fonction):

DECLARE @Day int, @Month int, @Year int
SELECT @Day = 1, @Month = 2, @Year = 2008

SELECT DateAdd(dd, @Day-1, DateAdd(mm, @Month -1, DateAdd(yy, @Year - 2000, '20000101')))
Robert Wagner
la source
4

J'ajoute une solution sur une seule ligne si vous avez besoin d'un datetime à partir de la date et de l'heure :

select dateadd(month, (@Year -1900)*12 + @Month -1, @DayOfMonth -1) + dateadd(ss, @Hour*3600 + @Minute*60 + @Second, 0) + dateadd(ms, @Millisecond, 0)
bleuâtre
la source
3

Essayer

CAST(STR(DATEPART(year, DATE))+'-'+ STR(DATEPART(month, DATE)) +'-'+ STR(DATEPART(day, DATE)) AS DATETIME)
Saul Guerra
la source
2

Pour les versions de SQL Server inférieures à 12, je peux recommander l'utilisation de CASTen combinaison avecSET DATEFORMAT

-- 26 February 2015
SET DATEFORMAT dmy
SELECT CAST('26-2-2015' AS DATE)

SET DATEFORMAT ymd
SELECT CAST('2015-2-26' AS DATE)

la façon dont vous créez ces chaînes dépend de vous

Konstantin
la source
1

Essayez cette requête:

    SELECT SUBSTRING(CONVERT(VARCHAR,JOINGDATE,103),7,4)AS
    YEAR,SUBSTRING(CONVERT(VARCHAR,JOINGDATE,100),1,2)AS
MONTH,SUBSTRING(CONVERT(VARCHAR,JOINGDATE,100),4,3)AS DATE FROM EMPLOYEE1

Résultat:

2014    Ja    1
2015    Ja    1
2014    Ja    1
2015    Ja    1
2012    Ja    1
2010    Ja    1
2015    Ja    1
user3141962
la source
0

Personnellement, je préfère la sous-chaîne car elle offre des options de nettoyage et la possibilité de diviser la chaîne au besoin. L'hypothèse est que les données sont au format «jj, mm, aaaa».

--2012 and above
SELECT CONCAT (
        RIGHT(REPLACE(@date, ' ', ''), 4)
        ,'-'
        ,RIGHT(CONCAT('00',SUBSTRING(REPLACE(@date, ' ', ''), CHARINDEX(',', REPLACE(@date, ' ', '')) + 1, LEN(REPLACE(@date, ' ', '')) - CHARINDEX(',', REPLACE(@date, ' ', '')) - 5)),2)
        ,'-'
        ,RIGHT(CONCAT('00',SUBSTRING(REPLACE(@date, ' ', ''), 1, CHARINDEX(',', REPLACE(@date, ' ', '')) - 1)),2)
        )

--2008 and below
SELECT   RIGHT(REPLACE(@date, ' ', ''), 4)
        +'-'
        +RIGHT('00'+SUBSTRING(REPLACE(@date, ' ', ''), CHARINDEX(',', REPLACE(@date, ' ', '')) + 1, LEN(REPLACE(@date, ' ', '')) - CHARINDEX(',', REPLACE(@date, ' ', '')) - 5),2)
        +'-'
        +RIGHT('00'+SUBSTRING(REPLACE(@date, ' ', ''), 1, CHARINDEX(',', REPLACE(@date, ' ', '')) - 1),2)

Voici une démonstration de la façon dont il peut être poursuivi si les données sont stockées dans une colonne. Inutile de dire que son idéal pour vérifier l'ensemble de résultats avant d'appliquer à la colonne

DECLARE @Table TABLE (ID INT IDENTITY(1000,1), DateString VARCHAR(50), DateColumn DATE)

INSERT INTO @Table
SELECT'12, 1, 2007',NULL
UNION
SELECT'15,3, 2007',NULL
UNION
SELECT'18, 11 , 2007',NULL
UNION
SELECT'22 , 11, 2007',NULL
UNION
SELECT'30, 12, 2007  ',NULL

UPDATE @Table
SET DateColumn = CONCAT (
        RIGHT(REPLACE(DateString, ' ', ''), 4)
        ,'-'
        ,RIGHT(CONCAT('00',SUBSTRING(REPLACE(DateString, ' ', ''), CHARINDEX(',', REPLACE(DateString, ' ', '')) + 1, LEN(REPLACE(DateString, ' ', '')) - CHARINDEX(',', REPLACE(DateString, ' ', '')) - 5)),2)
        ,'-'
        ,RIGHT(CONCAT('00',SUBSTRING(REPLACE(DateString, ' ', ''), 1, CHARINDEX(',', REPLACE(DateString, ' ', '')) - 1)),2)
        ) 

SELECT ID,DateString,DateColumn
FROM @Table
Gouri Shankar Aechoor
la source