Erstellen Sie mit T-SQL ein Datum aus Tag, Monat und Jahr

264

Ich versuche, ein Datum mit einzelnen Teilen wie 12, 1, 2007 in eine Datums- / Uhrzeitangabe in SQL Server 2005 zu konvertieren. Ich habe Folgendes versucht:

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

Dies führt jedoch zu einem falschen Datum. Was ist der richtige Weg, um die drei Datumswerte in ein geeignetes Datum / Uhrzeit-Format umzuwandeln?

Brandon
quelle
8
Bitte erwägen Sie, Ihre akzeptierte Antwort zu ändern weblogs.sqlteam.com/jeffs/archive/2007/09/10/…
Brian Webster
DATEFROMPARTS (Jahr, Monat, Tag)
Kate

Antworten:

174

Angenommen, y, m, dalle sind int, wie wäre es mit:

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

Weitere Informationen finden Sie in meiner anderen Antwort für SQL Server 2012 und höher

Cade Roux
quelle
Schlechter. Verfassen Sie mich aus Ints am 1. Januar 0001
Oleg Dok
24
Oleg SQL Server DateTime geht nicht weiter zurück als 1753-01-01 etwas.
CodeMonkey
2
Diese Antwort hängt von den Datumsformateinstellungen ab, die von den regionalen Einstellungen Ihres Servers abhängen, wenn Sie keine angeben. Das yyyymmddFormat funktioniert unabhängig von diesen Einstellungen. "Eine sechs- oder achtstellige Zeichenfolge wird immer als ymd interpretiert ." docs.microsoft.com/en-us/sql/t-sql/data-types/… Siehe diese Antwort: stackoverflow.com/a/46064419/2266979
Riley Major
339

Versuche dies:

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

Es funktioniert auch, hat den zusätzlichen Vorteil, dass keine Zeichenfolgenkonvertierungen durchgeführt werden. Es ist also eine reine arithmetische Verarbeitung (sehr schnell) und hängt nicht von einem Datumsformat ab. Dies nutzt die Tatsache, dass die interne Darstellung von SQL Server für datetime- und smalldatetime-Werte zwei ist Teilewert, dessen erster Teil eine Ganzzahl ist, die die Anzahl der Tage seit dem 1. Januar 1900 darstellt, und deren zweiter Teil ein Dezimalbruch ist, der den Bruchteil eines Tages (für die Zeit) darstellt --- Also der ganzzahlige Wert 0 (Null) ) übersetzt immer direkt in den Mitternachtsmorgen des 1. Januar 1900 ...

oder dank des Vorschlags von @brinary

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

Bearbeitet im Oktober 2014. Wie von @cade Roux erwähnt, verfügt SQL 2012 jetzt über eine integrierte Funktion:
DATEFROMPARTS(year, month, day)
Das gleiche.

Bearbeitet am 3. Oktober 2016 (Dank an @bambams für das Erkennen und @brinary für das Beheben), Die letzte von @brinary vorgeschlagene Lösung. scheint für Schaltjahre nicht zu funktionieren, es sei denn, die Addition von Jahren wird zuerst durchgeführt

select dateadd(month, @Month - 1, 
     dateadd(year, @Year-1900, @DayOfMonth - 1)); 
Charles Bretana
quelle
36
@Brandon, du solltest dies stattdessen als diese Antwort markieren. Es ist das beste. Tun Sie dies als Service für andere StackOverflow-Lesegeräte.
Bill Paetzke
3
Funktioniert für Schaltjahre: Wählen Sie dateadd (mm, (@ y-1900) * 12 + @m - 1,0) + (@ d-1)
versteckt
8
Ergebnisse in einem gültigen noch falschen Datumswert , wenn ungültige Kombination von Werten zB vergangen @Year = 2001, @Month = 13und @DayOfMonth = 32in den Ergebnissen 2002-02-01T00:00:00.000. Die akzeptierte Antwort (von Cade Roux) erzeugt einen Fehler, der nützlicher ist.
Tag, wenn
6
Sie müssen nicht mit Null beginnen und Tage hinzufügen. Sie können direkt mit @ DayOfMonth-1 beginnen und dann die Monate und Jahre hinzufügen. Das ist ein DateAdd () weniger!
Brian
2
Mein Kopf dreht sich immer noch - gibt es wirklich keinen besseren Weg, dies zu tun? (Ich bin beauftragt, eine Abfrage in SQL Server 2005 zu beheben)
Peter Perháč
241

SQL Server 2012 verfügt über eine wunderbare und lang erwartete neue DATEFROMPARTS-Funktion (die bei ungültigem Datum einen Fehler auslöst - mein Hauptargument gegen eine DATEADD-basierte Lösung für dieses Problem):

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

DATEFROMPARTS(ycolumn, mcolumn, dcolumn)

oder

DATEFROMPARTS(@y, @m, @d)
Cade Roux
quelle
11
In Bezug auf die ursprüngliche Frage, in der das Datetime-Objekt erwähnt wurde, gibt es außerdem eine Funktion namens DATETIMEFROMPARTS: msdn.microsoft.com/pl-pl/library/hh213233%28v=sql.110%29.aspx
Maciej Jaśniaczyk
116

Oder verwenden Sie nur eine einzige dateadd-Funktion:

DECLARE @day int, @month int, @year int
SELECT @day = 4, @month = 3, @year = 2011

SELECT dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1)
Würger
quelle
4
beste Antwort IMO. Hat alle Vorteile von Charles 'Antwort und ist viel kürzer.
Michael
1
Dies ist bei weitem das sauberste und einfachste. Und es wird auch kein Fehler ausgegeben, wenn die Tageswerte außerhalb des Bereichs liegen. Abhängig von den Umständen kann jedoch ein Fehler erwünscht sein. Beachten Sie daher, dass dadurch Tag- und Monatswerte stummgeschaltet werden, die außerhalb des erwarteten Bereichs liegen.
Shawn Kovac
16

SQL Server 2012 verfügt über eine Funktion, die das Datum basierend auf den Teilen erstellt ( DATEFROMPARTS ). Für den Rest von uns ist hier eine Datenbankfunktion, die ich erstellt habe und die das Datum aus den Teilen bestimmt (danke @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

Sie können es so nennen ...

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

Kehrt zurück...

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

Versuchen Sie CONVERT anstelle von CAST.

CONVERT ermöglicht einen dritten Parameter, der das Datumsformat angibt.

Eine Liste der Formate finden Sie hier: http://msdn.microsoft.com/en-us/library/ms187928.aspx

Aktualisieren, nachdem eine andere Antwort als "richtige" Antwort ausgewählt wurde:

Ich verstehe nicht wirklich, warum eine Antwort ausgewählt wird, die eindeutig von den NLS-Einstellungen auf Ihrem Server abhängt, ohne auf diese Einschränkung hinzuweisen.

devio
quelle
Stimmen Sie zu, dass das Format qualifiziert werden muss, z. B. CONVERT (datetime2, CAST (@year AS varchar) + '.' + CAST (@month AS varchar) + '.' + CAST (@day AS varchar), 102)
Tony Wall
9

Sie können auch verwenden

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

Funktioniert in SQL seit Version 2012 und AzureSQL

Marcelo Lujan
quelle
6

Es ist sicherer und ordentlicher, einen expliziten Ausgangspunkt '19000101' zu verwenden.

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
quelle
Warum nicht einfach declare @output datetime2 = 0und statt @Year - 1900verwenden @Year - DATEPART(year,0);? Dies funktioniert ohne Umwandlungen in SQL Server 2008 und viel klarer.
Tsionyx
Weil das nicht funktioniert. Sie können 0 nicht in datetime2 umwandeln. Ihr Code gibt "Operandentyp clash: int ist nicht kompatibel mit datetime2" zurück
Jack
4

Wenn Sie keine Zeichenfolgen fernhalten möchten, funktioniert dies ebenfalls (fügen Sie es in eine Funktion ein):

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

Ich füge eine einzeilige Lösung hinzu, wenn Sie eine Datums- / Uhrzeitangabe für Datums- und Zeitangaben benötigen :

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

Versuchen

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

Für SQL Server-Versionen unter 12 kann ich die Verwendung CASTin Kombination mit empfehlenSET DATEFORMAT

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

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

Wie Sie diese Zeichenfolgen erstellen, liegt bei Ihnen

Konstantin
quelle
1

Versuchen Sie diese Abfrage:

    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

Ergebnis:

2014    Ja    1
2015    Ja    1
2014    Ja    1
2015    Ja    1
2012    Ja    1
2010    Ja    1
2015    Ja    1
user3141962
quelle
0

Ich persönlich bevorzuge Teilzeichenfolgen, da sie Reinigungsoptionen und die Möglichkeit bieten, die Zeichenfolge nach Bedarf zu teilen. Die Annahme ist, dass die Daten das Format 'TT, MM, JJJJ' haben.

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

Hier ist eine Demonstration, wie es verklagt werden kann, wenn die Daten in einer Spalte gespeichert sind. Es ist unnötig zu erwähnen, dass es ideal ist, die Ergebnismenge zu überprüfen, bevor Sie sie auf die Spalte anwenden

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
quelle