Crie uma data do dia mês e ano com o T-SQL

265

Estou tentando converter uma data com partes individuais, como 12, 1, 2007, em um datetime no SQL Server 2005. Tentei o seguinte:

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

mas isso resulta na data errada. Qual é a maneira correta de transformar os três valores de data em um formato de data e hora adequado.

Brandon
fonte
8
Por favor, considere alterar sua resposta aceita weblogs.sqlteam.com/jeffs/archive/2007/09/10/…
Brian Webster
DATEFROMPARTS (ano, mês, dia)
Kate

Respostas:

175

Supondo que y, m, dsejam todos int, que tal:

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

Por favor, veja minha outra resposta para o SQL Server 2012 e acima

Cade Roux
fonte
Mau. Escreva-me a partir de ints na data de 1º de janeiro de 0001
Oleg Dok
24
Oleg SQL Server DateTime não vá mais longe, então 1753-01-01 algo.
CodeMonkey 01/07/2013
2
Esta resposta depende das configurações de formato de data, que dependem das configurações regionais do seu servidor, se você não especificar. O yyyymmddformato funciona independentemente dessas configurações. "Uma cadeia de seis ou oito dígitos é sempre interpretada como ymd ." docs.microsoft.com/en-us/sql/t-sql/data-types/… Consulte esta resposta: stackoverflow.com/a/46064419/2266979
Riley Major
339

Tente o seguinte:

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 também, tem o benefício adicional de não fazer nenhuma conversão de string, por isso é um processamento aritmético puro (muito rápido) e não depende de nenhum formato de data. valor da parte cuja primeira parte é um número inteiro que representa o número de dias desde 1 de janeiro de 1900 e a segunda parte é uma fração decimal que representa a parte fracionária de um dia (para a hora) --- Portanto, o valor inteiro 0 (zero ) sempre se traduz diretamente na manhã da meia-noite de 1 de janeiro de 1900 ...

ou, graças à sugestão do @brinary,

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

Editado em outubro de 2014. Conforme observado pelo @cade Roux, o SQL 2012 agora tem uma função interna:
DATEFROMPARTS(year, month, day)
isso faz a mesma coisa.

Editado em 3 de outubro de 2016, (Obrigado a @bambams por perceber isso e @brinary por corrigi-lo), A última solução, proposta por @brinary. parece não funcionar por anos bissextos, a menos que a adição de anos seja realizada primeiro

select dateadd(month, @Month - 1, 
     dateadd(year, @Year-1900, @DayOfMonth - 1)); 
Charles Bretana
fonte
36
@Brandon, você deve marcar isso como esta resposta. É o melhor. Faça isso como um serviço para outros leitores do StackOverflow.
Bill Paetzke
3
Funciona por anos bissextos: selecione dateadd (mm, (@ y-1900) * 12 + @m - 1,0) + (@ d-1)
oculto
8
Resulta em um valor de data válido, porém falso, quando passado uma combinação inválida de valores @Year = 2001, por exemplo , @Month = 13e @DayOfMonth = 32resulta em 2002-02-01T00:00:00.000. A resposta aceita (por Cade Roux) gera um erro, o que é mais útil.
onedaywhen
6
Você não precisa começar com zero e adicionar dias. Você pode começar diretamente com @ DayOfMonth-1 e adicionar os meses e anos. Isso é menos um DateAdd ()!
Brianary
2
minha cabeça ainda está girando - realmente não há uma maneira mais clara de fazer isso? (Eu estou encarregado de fixar uma consulta no SQL Server 2005)
Peter Perháč
241

O SQL Server 2012 possui uma nova e maravilhosa função DATEFROMPARTS (que gerará um erro se a data for inválida - minha principal objeção a uma solução baseada em DATEADD para esse problema):

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

DATEFROMPARTS(ycolumn, mcolumn, dcolumn)

ou

DATEFROMPARTS(@y, @m, @d)
Cade Roux
fonte
11
Além disso, referindo-se à pergunta original, onde o objeto Datetime foi mencionado, também há uma função chamada DATETIMEFROMPARTS: msdn.microsoft.com/pl-pl/library/hh213233%28v=sql.110%29.aspx
Maciej Jaśniaczyk
116

Ou usando apenas uma única função 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)
Picanço
fonte
4
melhor resposta IMO. Tem todas as vantagens da resposta de Charles e é muito mais curto.
Michael
1
Este é de longe o mais limpo e mais simples. E isso não gera um erro quando os valores do dia também estão fora da faixa. Embora, dependendo da circunstância, um erro possa ser desejado, lembre-se de que isso silencia valores de dia e mês que estão fora do intervalo esperado.
Shawn Kovac
17

O Sql Server 2012 possui uma função que criará a data com base nas partes ( DATEFROMPARTS ). Para o resto de nós, aqui está uma função db que criei que determinará a data das partes (obrigado @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

Você pode chamar assim ...

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

Retorna ...

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

Tente CONVERT em vez de CAST.

CONVERT permite um terceiro parâmetro indicando o formato da data.

A lista de formatos está aqui: http://msdn.microsoft.com/en-us/library/ms187928.aspx

Atualizar após outra resposta ter sido selecionada como a resposta "correta":

Realmente não entendo por que uma resposta é selecionada que depende claramente das configurações de NLS do seu servidor, sem indicar essa restrição.

devio
fonte
Concorde que o formato precisa ser qualificado, por exemplo, CONVERT (datetime2, CAST (@ano AS varchar) + '.' + CAST (@month AS varchar) + '.' + CAST (@day AS varchar), 102)
Tony Wall
9

Você também pode usar

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

Funciona no SQL desde a versão 2012 e AzureSQL

Marcelo Lujan
fonte
6

É mais seguro e organizado usar um ponto 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
fonte
Por que não usar apenas declare @output datetime2 = 0e em vez de @Year - 1900usar @Year - DATEPART(year,0);? Isso funciona sem nenhuma conversão no SQL Server 2008 e muito mais claro.
Tsionyx #
Porque isso não vai funcionar. Você não pode converter 0 em datetime2. Seu código retornará "Operand type clash: int é incompatível com datetime2"
Jack
4

Se você não deseja manter as strings fora dele, isso também funciona (coloque-o em uma função):

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

Eu adiciono uma solução de uma linha se você precisar de um datetime de partes de data e hora :

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

Experimentar

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

Para versões do SQL Server abaixo de 12, recomendo o uso CASTem combinação comSET DATEFORMAT

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

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

como você cria essas strings é com você

Konstantin
fonte
1

Tente 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
user3141962
fonte
0

Pessoalmente, prefiro o Substring, pois fornece opções de limpeza e capacidade de dividir o texto conforme necessário. A suposição é que os dados tenham o 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)

Aqui está uma demonstração de como ele pode ser processado se os dados forem armazenados em uma coluna. Escusado será dizer que é ideal verificar o conjunto de resultados antes de aplicar na coluna

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
fonte