Utwórz datę z miesiąca miesiąca i roku za pomocą T-SQL

264

Próbuję przekonwertować datę z poszczególnymi częściami, takimi jak 12, 1, 2007, na datę i godzinę w SQL Server 2005. Próbowałem:

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

ale powoduje to niewłaściwą datę. Jaki jest prawidłowy sposób przekształcenia trzech wartości daty we właściwy format daty i godziny.

Brandon
źródło
8
Proszę rozważyć zmianę zaakceptowanej odpowiedzi weblogs.sqlteam.com/jeffs/archive/2007/09/10/...
Brian Webster
DATAFROMPARTS (rok, miesiąc, dzień)
Kate

Odpowiedzi:

174

Zakładając, że y, m, dwszystkie int, a może:

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

Zobacz moją inną odpowiedź dla SQL Server 2012 i nowszych

Cade Roux
źródło
Ten zły. Skomponuj mnie z daty 1 stycznia 0001
Oleg Dok
24
Oleg SQL Server DateTime nie iść dalej niż 1753-01-01 coś.
CodeMonkey
2
Ta odpowiedź zależy od ustawień formatu daty, które zależą od ustawień regionalnych twojego serwera, jeśli nie określisz. yyyymmddFormat działa niezależnie od tych ustawień. „Sześcio- lub ośmiocyfrowy ciąg jest zawsze interpretowany jako ymd ”. docs.microsoft.com/en-us/sql/t-sql/data-types/... Zobacz tę odpowiedź: stackoverflow.com/a/46064419/2266979
Riley Major
339

Spróbuj tego:

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

Działa również, ma dodatkową zaletę braku wykonywania konwersji ciągów, więc jest to czyste przetwarzanie arytmetyczne (bardzo szybkie) i nie jest zależne od żadnego formatu daty. Wykorzystuje to fakt, że wewnętrzna reprezentacja SQL Server dla wartości datetime i smalldatetime to dwa wartość części, której pierwsza część jest liczbą całkowitą reprezentującą liczbę dni od 1 stycznia 1900 r., a druga część jest ułamkiem dziesiętnym reprezentującym ułamkową część jednego dnia (dla czasu) --- Więc liczba całkowita 0 (zero ) zawsze przekłada się bezpośrednio na północ o północy 1 stycznia 1900 roku ...

lub, dzięki sugestii @brinary,

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

Edytowany w październiku 2014 r. Jak zauważył @cade Roux, SQL 2012 ma teraz wbudowaną funkcję:
DATEFROMPARTS(year, month, day)
robi to samo.

Edytowano 3 października 2016 r. (Podziękowania dla @bambams za zauważenie tego i @brinary za naprawienie tego), ostatnie rozwiązanie zaproponowane przez @brinary. nie wydaje się działać przez lata przestępne, chyba że dodawanie lat zostanie wykonane jako pierwsze

select dateadd(month, @Month - 1, 
     dateadd(year, @Year-1900, @DayOfMonth - 1)); 
Charles Bretana
źródło
36
@Brandon, powinieneś zaznaczyć to jako odpowiedź. To jest najlepsze. Zrób to jako usługa dla innych czytników StackOverflow.
Bill Paetzke,
3
Działa na lata przestępne: wybierz dateadd (mm, (@ y-1900) * 12 + @m - 1,0) + (@ d-1)
ukryty
8
Wyniki w ważnej jeszcze pozornej wartości daty, kiedy przeszedł nieprawidłową kombinację wartości np @Year = 2001, @Month = 13a @DayOfMonth = 32wyniki w 2002-02-01T00:00:00.000. Zaakceptowana odpowiedź (autor: Cade Roux) generuje błąd, który jest bardziej przydatny.
poniedziałek
6
Nie musisz zaczynać od zera i dodawać dni. Możesz zacząć bezpośrednio od @ DayOfMonth-1, a następnie dodać miesiące i lata. To o jeden mniej DateAdd ()!
Brianary
2
moja głowa wciąż się kręci - naprawdę nie ma lepszego sposobu na zrobienie tego? (Mam zadanie naprawić zapytanie w SQL Server 2005)
Peter Perháč
241

SQL Server 2012 ma wspaniałą i długo oczekiwaną nową funkcję DATEFROMPARTS (która wywoła błąd, jeśli data jest nieprawidłowa - mój główny sprzeciw wobec rozwiązania tego problemu opartego na DATEADD):

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

DATEFROMPARTS(ycolumn, mcolumn, dcolumn)

lub

DATEFROMPARTS(@y, @m, @d)
Cade Roux
źródło
11
Ponadto, odnosząc się do pierwotnego pytania, w którym wspomniano o obiekcie Datetime, istnieje również funkcja o nazwie DATETIMEFROMPARTS: msdn.microsoft.com/pl-pl/library/hh213233%28v=sql.110%29.aspx
Maciej Jaśniaczyk
116

Lub używając tylko jednej funkcji 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)
Dzierzba
źródło
4
najlepsza odpowiedź IMO. Ma wszystkie zalety odpowiedzi Charlesa i jest znacznie krótszy.
Michael
1
Jest to zdecydowanie najczystsze i najprostsze. Nie generuje też błędu, gdy wartości dzienne są poza zakresem. Mimo to w zależności od okoliczności może być pożądany błąd, więc należy pamiętać, że wycisza to wartości dzienne i miesięczne, które są poza oczekiwanym zakresem.
Shawn Kovac
16

Sql Server 2012 ma funkcję, która utworzy datę na podstawie części ( DATEFROMPARTS ). Dla reszty z nas utworzyłem funkcję db, która określi datę na podstawie części (dzięki @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

Możesz to tak nazwać ...

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

Zwroty...

2013-10-04 15:50:00.000
Brian
źródło
12

Spróbuj CONVERT zamiast CAST.

CONVERT pozwala na trzeci parametr wskazujący format daty.

Lista formatów znajduje się tutaj: http://msdn.microsoft.com/en-us/library/ms187928.aspx

Zaktualizuj po wybraniu innej odpowiedzi jako „poprawnej”:

Naprawdę nie rozumiem, dlaczego wybrano odpowiedź, która wyraźnie zależy od ustawień NLS na twoim serwerze, bez wskazania tego ograniczenia.

devio
źródło
Zgadzam się, że format musi zostać zakwalifikowany, np. CONVERT (datetime2, CAST (@year AS varchar) + '.' + CAST (@month AS varchar) + '.' + CAST (@day AS varchar), 102)
Tony Wall
9

Możesz także użyć

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

Działa w SQL od wersji 2012 i AzureSQL

Marcelo Lujan
źródło
6

Bezpieczniej i schludniej jest stosować wyraźny punkt początkowy „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
Jacek
źródło
Dlaczego nie używać tylko declare @output datetime2 = 0i zamiast @Year - 1900używać @Year - DATEPART(year,0);? Działa to bez żadnych rzutowań w SQL Server 2008 i znacznie bardziej przejrzyste.
tsionyx
Ponieważ to nie zadziała. Nie możesz rzutować 0 na datetime2. Twój kod zwróci „Zderzenie typu argumentu: int jest niezgodne z datetime2”
Jack
4

Jeśli nie chcesz, aby łańcuchy były poza tym, działa to również (Umieść w funkcji):

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
źródło
4

Dodaję rozwiązanie jednowierszowe, jeśli potrzebujesz daty i godziny z części daty i godziny :

select dateadd(month, (@Year -1900)*12 + @Month -1, @DayOfMonth -1) + dateadd(ss, @Hour*3600 + @Minute*60 + @Second, 0) + dateadd(ms, @Millisecond, 0)
niebieskawy
źródło
3

Próbować

CAST(STR(DATEPART(year, DATE))+'-'+ STR(DATEPART(month, DATE)) +'-'+ STR(DATEPART(day, DATE)) AS DATETIME)
Saul Guerra
źródło
2

W przypadku wersji SQL Server poniżej 12 mogę zalecić użycie CASTw połączeniu zSET DATEFORMAT

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

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

jak utworzysz te ciągi, zależy od ciebie

Konstantin
źródło
1

Spróbuj tego zapytania:

    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

Wynik:

2014    Ja    1
2015    Ja    1
2014    Ja    1
2015    Ja    1
2012    Ja    1
2010    Ja    1
2015    Ja    1
użytkownik3141962
źródło
0

Ja osobiście wolę Substring, ponieważ zapewnia opcje czyszczenia i możliwość podziału łańcucha w razie potrzeby. Zakłada się, że dane mają format „dd, mm, rrrr”.

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

Oto demonstracja, w jaki sposób można go pozwać, jeśli dane są przechowywane w kolumnie. Nie trzeba dodawać, że idealnie sprawdza się zestaw wyników przed zastosowaniem do kolumny

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
źródło