Cree una fecha a partir del día mes y año con T-SQL

265

Estoy tratando de convertir una fecha con partes individuales como 12, 1, 2007 en una fecha y hora en SQL Server 2005. He intentado lo siguiente:

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

pero esto da como resultado una fecha incorrecta. ¿Cuál es la forma correcta de convertir los tres valores de fecha en un formato de fecha y hora adecuado?

Brandon
fuente
8
Considere cambiar su respuesta aceptada weblogs.sqlteam.com/jeffs/archive/2007/09/10/…
Brian Webster
DATEFROMPARTS (año, mes, día)
Kate

Respuestas:

175

Suponiendo que y, m, dson todos int, ¿qué tal:

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

Consulte mi otra respuesta para SQL Server 2012 y superior

Cade Roux
fuente
Uno malo. Redactame desde ints la fecha del 1 de enero de 0001
Oleg Dok
24
Oleg SQL Server DateTime no va más allá de 1753-01-01 algo.
CodeMonkey
2
Esta respuesta depende de la configuración del formato de fecha, que depende de la configuración regional de su servidor si no lo especifica. El yyyymmddformato funciona independientemente de esa configuración. "Una cadena de seis u ocho dígitos siempre se interpreta como ymd ". docs.microsoft.com/en-us/sql/t-sql/data-types/… Vea esta respuesta: stackoverflow.com/a/46064419/2266979
Riley Major
339

Prueba esto:

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

Funciona también, tiene la ventaja añadida de no realizar conversiones de cadenas, por lo que es un procesamiento aritmético puro (muy rápido) y no depende de ningún formato de fecha. Esto aprovecha el hecho de que la representación interna de SQL Server para los valores de fecha y hora es un doble valor de la parte cuya primera parte es un número entero que representa el número de días desde el 1 de enero de 1900, y la segunda parte es una fracción decimal que representa la parte fraccional de un día (para el tiempo) --- Entonces el valor entero 0 (cero ) siempre se traduce directamente a la medianoche del 1 de enero de 1900 ...

o, gracias a la sugerencia de @brinary,

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

Editado en octubre de 2014. Como señaló @cade Roux, SQL 2012 ahora tiene una función incorporada:
DATEFROMPARTS(year, month, day)
hace lo mismo.

Editado el 3 de octubre de 2016, (Gracias a @bambams por notar esto, y @brinary por arreglarlo), La última solución, propuesta por @brinary. no parece funcionar durante los años bisiestos a menos que se realice primero la adición de años

select dateadd(month, @Month - 1, 
     dateadd(year, @Year-1900, @DayOfMonth - 1)); 
Charles Bretana
fuente
36
@ Brandon, deberías marcar esto como esta respuesta. Es el mejor Hazlo como un servicio a otros lectores de StackOverflow.
Bill Paetzke
3
Funciona para años bisiestos: seleccione dateadd (mm, (@ y-1900) * 12 + @m - 1,0) + (@ d-1)
oculto el
8
Da como resultado un valor de fecha válido pero falso cuando se pasa una combinación de valores no válida @Year = 2001, por ejemplo , @Month = 13y @DayOfMonth = 32da como resultado 2002-02-01T00:00:00.000. La respuesta aceptada (por Cade Roux) genera un error, que es más útil.
cuando el
66
No tiene que comenzar con cero y agregar días. Puede comenzar directamente con @ DayOfMonth-1, luego agregar los meses y años. Ese es uno menos DateAdd ()!
brianary
2
mi cabeza todavía está girando, ¿realmente no hay una forma más ordenada de hacer esto? (Tengo la tarea de arreglar una consulta en SQL Server 2005)
Peter Perháč
241

SQL Server 2012 tiene una nueva y maravillosa función DATEFROMPARTS (que generará un error si la fecha no es válida; mi principal objeción a una solución basada en DATEADD para este problema):

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

DATEFROMPARTS(ycolumn, mcolumn, dcolumn)

o

DATEFROMPARTS(@y, @m, @d)
Cade Roux
fuente
11
Además, en referencia a la pregunta original, donde se mencionó el objeto Datetime, también hay una función llamada DATETIMEFROMPARTS: msdn.microsoft.com/pl-pl/library/hh213233%28v=sql.110%29.aspx
Maciej Jaśniaczyk
116

O usando solo una única función 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)
Alcaudón
fuente
44
mejor respuesta de la OMI. Tiene todas las ventajas de la respuesta de Charles y es mucho más corta.
Michael
1
Este es, con mucho, el más limpio y simple. Y tampoco arroja un error cuando los valores del día están fuera de rango. Aunque dependiendo de las circunstancias, puede desearse un error, así que tenga en cuenta que esto silencia los valores de día y mes que están fuera del rango esperado.
Shawn Kovac,
17

SQL Server 2012 tiene una función que creará la fecha en función de las partes ( DATEFROMPARTS ). Para el resto de nosotros, aquí hay una función db que creé que determinará la fecha de las partes (gracias @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

Puedes llamarlo así ...

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

Devoluciones...

2013-10-04 15:50:00.000
Brian
fuente
12

Prueba CONVERT en lugar de CAST.

CONVERT permite un tercer parámetro que indica el formato de fecha.

La lista de formatos está aquí: http://msdn.microsoft.com/en-us/library/ms187928.aspx

Actualice después de que se haya seleccionado otra respuesta como la respuesta "correcta":

Realmente no entiendo por qué se selecciona una respuesta que depende claramente de la configuración de NLS en su servidor, sin indicar esta restricción.

devio
fuente
De acuerdo, el formato debe ser calificado, por ejemplo, CONVERTIR (datetime2, CAST (@year AS varchar) + '.' + CAST (@month AS varchar) + '.' + CAST (@day AS varchar), 102)
Tony Wall
9

También puedes usar

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

Funciona en SQL desde ver.2012 y AzureSQL

Marcelo Lujan
fuente
6

Es más seguro y ordenado usar un punto de partida explícito '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
fuente
¿Por qué no usar solo declare @output datetime2 = 0y en lugar de @Year - 1900usar @Year - DATEPART(year,0);? Esto funciona sin ninguna conversión en SQL Server 2008 y mucho más claro.
tsionyx
Porque eso no funcionará. No puedes lanzar 0 a datetime2. Su código devolverá "Operand type clash: int es incompatible con datetime2"
Jack
4

Si no desea mantener las cadenas fuera de esto, esto también funciona (póngalo en una función):

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
fuente
4

Agrego una solución de una línea si necesita una fecha y hora de las partes de fecha y hora :

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

Tratar

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

Para las versiones de SQL Server inferiores a 12, puedo recomendar el uso CASTen combinación 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)

cómo crear esas cadenas depende de usted

Konstantin
fuente
1

Prueba esta consulta:

    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

Resultado:

2014    Ja    1
2015    Ja    1
2014    Ja    1
2015    Ja    1
2012    Ja    1
2010    Ja    1
2015    Ja    1
usuario3141962
fuente
0

Personalmente, prefiero Substring, ya que proporciona opciones de limpieza y la capacidad de dividir la cadena según sea necesario. Se supone que los datos tienen el formato 'dd, 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)

Aquí hay una demostración de cómo se puede demandar si los datos se almacenan en una columna. No es necesario decir que es ideal verificar el conjunto de resultados antes de aplicar a la columna.

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
fuente