Crea una data dal giorno mese e anno con T-SQL


265

Sto cercando di convertire una data con singole parti come 12, 1, 2007 in un datetime in SQL Server 2005. Ho provato quanto segue:

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

ma questo si traduce in una data sbagliata. Qual è il modo corretto di trasformare i tre valori di data in un formato di data e ora adeguato.


8
Ti preghiamo di considerare di cambiare la tua risposta accettata weblogs.sqlteam.com/jeffs/archive/2007/09/10/…
— Brian Webster

DATEFROMPARTS (anno, mese, giorno)
— Kate

Risposte:


175

Supponendo che y, m, dsiano tutti int, che ne dici di:

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

Si prega di consultare la mia altra risposta per SQL Server 2012 e versioni successive


Cattivo. Mi componga da ints la data del 1 ° gennaio 0001
— Oleg Dok

24
Oleg SQL Server DateTime non va più indietro del 1753-01-01 qualcosa.
— CodeMonkey

2
Questa risposta dipende dalle impostazioni del formato della data, che dipendono dalle impostazioni internazionali del server se non specificato. Il yyyymmddformato funziona indipendentemente da tali impostazioni. "Una stringa di sei o otto cifre viene sempre interpretata come ymd ." docs.microsoft.com/en-us/sql/t-sql/data-types/… Vedi questa risposta: stackoverflow.com/a/46064419/2266979
— Riley Major

339

Prova questo:

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)))

Funziona anche, ha aggiunto il vantaggio di non fare conversioni di stringhe, quindi è pura elaborazione aritmetica (molto veloce) e non dipende da alcun formato data Questo capitalizza il fatto che la rappresentazione interna di SQL Server per i valori datetime e smalldatetime è un due valore della parte la cui prima parte è un numero intero che rappresenta il numero di giorni dal 1 ° gennaio 1900 e la seconda parte è una frazione decimale che rappresenta la parte frazionaria di un giorno (per il momento) --- Quindi il valore intero 0 (zero ) si traduce sempre direttamente in mezzanotte del 1 ° gennaio 1900 ...

o, grazie al suggerimento di @brinary,

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

Modificato ottobre 2014. Come notato da @cade Roux, SQL 2012 ora ha una funzione integrata:
DATEFROMPARTS(year, month, day)
fa la stessa cosa.

Modificato il 3 ottobre 2016, (grazie a @bambams per averlo notato e @brinary per averlo risolto), l'ultima soluzione, proposta da @brinary. non sembra funzionare per anni bisestili a meno che non venga eseguita per prima l'aggiunta di anni

select dateadd(month, @Month - 1, 
     dateadd(year, @Year-1900, @DayOfMonth - 1)); 

36
@Brandon, dovresti contrassegnarlo come questa risposta. È il migliore. Fallo come servizio per altri lettori StackOverflow.
— Bill Paetzke,

3
Funziona per gli anni bisestili: seleziona dateadd (mm, (@ y-1900) * 12 + @m - 1,0) + (@ d-1)
— nascosto

8
Produce un valore data valido ma spurio quando viene trasmessa una combinazione di valori non valida @Year = 2001, ad esempio , @Month = 13e @DayOfMonth = 32risulta 2002-02-01T00:00:00.000. La risposta accettata (da Cade Roux) genera un errore, che è più utile.
— giorno

6
Non devi iniziare con zero e aggiungere giorni. Puoi iniziare direttamente con @ DayOfMonth-1, quindi aggiungere i mesi e gli anni. Questo è un DateAdd () in meno!
— brianary

2
la mia testa gira ancora - non c'è davvero un modo più pulito per farlo? (Ho il compito di correggere una query in SQL Server 2005)
— Peter Perháč,

241

SQL Server 2012 ha una nuova funzione DATEFROMPARTS meravigliosa e tanto attesa (che genererà un errore se la data non è valida - la mia obiezione principale a una soluzione basata su DATEADD a questo problema):

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

DATEFROMPARTS(ycolumn, mcolumn, dcolumn)

o

DATEFROMPARTS(@y, @m, @d)

11
Inoltre, facendo riferimento alla domanda originale, in cui è stato menzionato l'oggetto Datetime, esiste anche una funzione chiamata DATETIMEFROMPARTS: msdn.microsoft.com/pl-pl/library/hh213233%28v=sql.110%29.aspx
— Maciej Jaśniaczyk

116

O usando solo una singola funzione 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)

4
migliore risposta IMO. Ha tutti i vantaggi della risposta di Charles ed è molto più breve.
— Michael,

1
Questo è di gran lunga il più pulito e semplice. E non genera nemmeno un errore quando i valori del giorno non sono compresi nell'intervallo. Inoltre, a seconda della circostanza, può essere desiderato un errore, quindi basta essere consapevoli del fatto che questo mette a tacere i valori di giorno e mese che non rientrano nell'intervallo previsto.
— Shawn Kovac

17

SQL Server 2012 ha una funzione che creerà la data in base alle parti ( DATEFROMPARTS ). Per il resto di noi, ecco una funzione db che ho creato che determinerà la data dalle parti (grazie @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

Puoi chiamarlo così ...

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

Ritorna...

2013-10-04 15:50:00.000

12

Prova CONVERT invece di CAST.

CONVERT consente un terzo parametro che indica il formato della data.

L'elenco dei formati è qui: http://msdn.microsoft.com/en-us/library/ms187928.aspx

Aggiorna dopo aver selezionato un'altra risposta come risposta "corretta":

Non capisco davvero perché sia ​​selezionata una risposta che dipende chiaramente dalle impostazioni NLS sul tuo server, senza indicare questa restrizione.


Concordare che il formato deve essere qualificato, ad esempio CONVERT (datetime2, CAST (@year AS varchar) + '.' + CAST (@month AS varchar) + '.' + CAST (@day AS varchar), 102)
— Tony Wall

9

Puoi anche usare

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

Funziona in SQL dal ver.2012 e AzureSQL


6

È più sicuro e ordinato utilizzare un punto di partenza esplicito "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

Perché non usare solo declare @output datetime2 = 0e invece di @Year - 1900usare @Year - DATEPART(year,0);? Funziona senza alcun cast in SQL Server 2008 e molto più chiaro.
— Tsionyx,

Perché non funzionerà. Non puoi trasmettere 0 a datetime2. Il codice restituirà "Scontro di tipo operando: int non è compatibile con datetime2"
— Jack

4

Se non vuoi tenerne le stringhe, funziona anche questo (mettilo in una funzione):

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')))

4

Aggiungo una soluzione a una riga se hai bisogno di un datetime da parti di data e ora :

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

3

Provare

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

2

Per le versioni di SQL Server inferiori a 12, posso consigliarne l'uso CASTin combinazione conSET DATEFORMAT

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

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

come crei quelle stringhe dipende da te


1

Prova questa query:

    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

Risultato:

2014    Ja    1
2015    Ja    1
2014    Ja    1
2015    Ja    1
2012    Ja    1
2010    Ja    1
2015    Ja    1


0

Personalmente preferisco il sottostringa in quanto fornisce opzioni di pulizia e possibilità di dividere la stringa secondo necessità. Il presupposto è che i dati siano nel formato 'gg, 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)

Ecco una dimostrazione di come può essere citato in giudizio se i dati sono archiviati in una colonna. Inutile dire che è l'ideale per controllare il set di risultati prima di applicare alla colonna

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.