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.