สร้างวันที่จากวันที่เดือนและปีด้วย T-SQL


264

ฉันพยายามแปลงวันที่ด้วยส่วนต่างๆเช่น 12, 1, 2007 เป็นวันที่ใน SQL Server 2005 ฉันได้ลองต่อไปนี้:

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

แต่ผลลัพธ์นี้ในวันที่ไม่ถูกต้อง วิธีที่ถูกต้องในการเปลี่ยนค่าวันที่สามค่าเป็นรูปแบบวันที่และเวลาที่ถูกต้องคืออะไร


8
โปรดลองเปลี่ยนคำตอบที่คุณยอมรับ weblogs.sqlteam.com/jeffs/archive/2007/09/10/…
Brian Webster

DATEFROMPARTS (ปี, เดือน, วัน)
Kate

คำตอบ:


174

สมมติว่าy, m, dเป็นทั้งหมดintวิธีการเกี่ยวกับ:

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

โปรดดูคำตอบอื่น ๆ ของฉันสำหรับ SQL Server 2012 ขึ้นไป


ไม่ดีอย่างหนึ่ง เขียนฉันตั้งแต่วันที่ 1 มกราคม 0001
Oleg Dok

24
Oleg SQL Server DateTime ไม่ต้องไปไกลกว่านั้นอีกแล้ว 1753-01-01
CodeMonkey

2
คำตอบนี้ขึ้นอยู่กับการตั้งค่ารูปแบบวันที่ซึ่งขึ้นอยู่กับการตั้งค่าภูมิภาคของเซิร์ฟเวอร์ของคุณหากคุณไม่ได้ระบุ yyyymmddรูปแบบการทำงานโดยไม่คำนึงถึงการตั้งค่าเหล่านั้น "สตริงหกหรือแปดหลักจะตีความเป็นymdเสมอ" docs.microsoft.com/en-us/sql/t-sql/data-types/… ดูคำตอบนี้: stackoverflow.com/a/46064419/2266979
Riley Major

339

ลองสิ่งนี้:

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

มันทำงานได้ดีเพิ่มประโยชน์ของการไม่ทำการแปลงสตริงใด ๆ ดังนั้นจึงเป็นการประมวลผลทางคณิตศาสตร์อย่างรวดเร็ว (เร็วมาก) และไม่ได้ขึ้นอยู่กับรูปแบบวันที่ใด ๆ นี่เป็นการใช้ประโยชน์จากข้อเท็จจริงที่ว่า SQL Server แทนค่าภายใน ค่าส่วนหนึ่งส่วนแรกซึ่งเป็นจำนวนเต็มแทนจำนวนวันตั้งแต่ 1 มกราคม 1900 และส่วนที่สองคือเศษส่วนทศนิยมแทนส่วนเศษส่วนของหนึ่งวัน (สำหรับเวลา) --- ดังนั้นค่าจำนวนเต็ม 0 (ศูนย์ ) แปลตรงสู่เที่ยงคืนของวันที่ 1 มกราคม 1900 เสมอ ...

หรือขอบคุณคำแนะนำจาก @brinary

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

แก้ไขตุลาคม 2014 ตามที่บันทึกไว้โดย @cade Roux ตอนนี้ SQL 2012 มีฟังก์ชันในตัว:
DATEFROMPARTS(year, month, day)
ซึ่งทำสิ่งเดียวกัน

แก้ไขแล้ว 3 ต.ค. 2559 (ขอบคุณ @bambams ที่สังเกตเห็นสิ่งนี้และ @brinary สำหรับการแก้ไข), ทางออกสุดท้ายที่เสนอโดย @brinary ดูเหมือนจะไม่ทำงานเป็นปีอธิกสุรทินเว้นแต่จะมีการเพิ่มปีเป็นครั้งแรก

select dateadd(month, @Month - 1, 
     dateadd(year, @Year-1900, @DayOfMonth - 1)); 

36
@Brandon คุณควรทำเครื่องหมายเป็นคำตอบนี้แทน มันเป็นสิ่งที่ดีที่สุด ทำมันเป็นบริการสำหรับผู้อ่าน StackOverflow รายอื่น
Bill Paetzke

3
ใช้งานได้สำหรับ leap ปี: เลือก Dateadd (mm, (@ y-1900) * 12 + @m - 1,0) + (@ d-1)
ซ่อน

8
ผลลัพธ์ในค่าวันที่ถูกต้องยังปลอมเมื่อผ่านการรวมกันที่ไม่ถูกต้องของค่าเช่น@Year = 2001, @Month = 13และผลในการ@DayOfMonth = 32 2002-02-01T00:00:00.000คำตอบที่ยอมรับ (โดย Cade Roux) สร้างข้อผิดพลาดซึ่งมีประโยชน์มากกว่า
oneday

6
คุณไม่ต้องเริ่มต้นด้วยศูนย์และเพิ่มวัน คุณสามารถเริ่มต้นโดยตรงกับ @ DayOfMonth-1 จากนั้นเพิ่มเดือนและปี นั่นคือ DateAdd ที่น้อยลงหนึ่งรายการ!
ไบรอันรี่

2
หัวของฉันยังคงหมุน - มีวิธีการทำ neater นี้จริง ๆ หรือไม่ (ฉันได้รับมอบหมายให้แก้ไขแบบสอบถามใน SQL Server 2005)
Peter Perháč

241

SQL Server 2012 มีฟังก์ชั่น DATEFROMPARTS ใหม่ที่ยอดเยี่ยมและรอคอยมานาน (ซึ่งจะเพิ่มข้อผิดพลาดหากวันที่ไม่ถูกต้อง - การคัดค้านหลักของฉันไปยังโซลูชันที่ใช้ DATEADD กับปัญหานี้):

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

DATEFROMPARTS(ycolumn, mcolumn, dcolumn)

หรือ

DATEFROMPARTS(@y, @m, @d)

11
นอกจากนี้การอ้างถึงคำถามเดิมที่กล่าวถึงวัตถุ Datetime นอกจากนี้ยังมีฟังก์ชันที่เรียกว่า DATETIMEFROMPARTS: msdn.microsoft.com/pl-pl/library/hh213233%28v=sql.110%29.aspx
Maciej Jaśniaczyk

116

หรือใช้เพียงฟังก์ชัน 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
คำตอบที่ดีที่สุด IMO มีข้อดีทั้งหมดของคำตอบของ Charles และสั้นกว่ามาก
ไมเคิล

1
นี่คือไกลโดยที่สะอาดและง่ายที่สุด และจะไม่โยนข้อผิดพลาดเมื่อค่าวันอยู่นอกช่วงเช่นกัน แม้ว่าจะเกิดข้อผิดพลาดขึ้นอยู่กับสถานการณ์ก็ตามดังนั้นโปรดทราบว่าค่าวันและเดือนนี้ไม่อยู่ในช่วงที่คาดหวัง
Shawn Kovac

16

SQL Server 2012 มีฟังก์ชั่นที่จะสร้างวันที่ขึ้นอยู่กับชิ้นส่วน ( DATEFROMPARTS ) สำหรับพวกเราที่เหลือนี่คือฟังก์ชั่น db ที่ฉันสร้างขึ้นซึ่งจะกำหนดวันที่จากชิ้นส่วน (ขอบคุณ @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

คุณสามารถโทรแบบนี้ ...

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

ผลตอบแทน ...

2013-10-04 15:50:00.000

12

ลองใช้ CONVERT แทน CAST

CONVERT อนุญาตให้พารามิเตอร์ที่สามระบุรูปแบบวันที่

รายการรูปแบบอยู่ที่นี่: http://msdn.microsoft.com/en-us/library/ms187928.aspx

อัปเดตหลังจากเลือกคำตอบอื่นเป็นคำตอบ "ถูกต้อง":

ฉันไม่เข้าใจว่าเหตุใดจึงเลือกคำตอบที่ชัดเจนขึ้นอยู่กับการตั้งค่า NLS บนเซิร์ฟเวอร์ของคุณโดยไม่ระบุข้อ จำกัด นี้


ยอมรับรูปแบบที่ต้องผ่านการรับรองเช่น CONVERT (datetime2, CAST (@year AS varchar) + '.' + CAST (@month AS varchar) + '.' + CAST (@day AS varchar), 102)
Tony Wall

9

คุณยังสามารถใช้

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

ทำงานใน SQL ตั้งแต่ ver.2012 และ AzureSQL


6

การใช้จุดเริ่มต้นที่ชัดเจนคือ '19000101' ปลอดภัยกว่าและเป็น neater

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

ทำไมไม่ใช้เพียงdeclare @output datetime2 = 0และแทนที่จะ@Year - 1900ใช้@Year - DATEPART(year,0);? สิ่งนี้ทำงานได้โดยไม่ต้องมีการปลดเปลื้องใน SQL Server 2008 และชัดเจนกว่า
tsionyx

เพราะมันไม่ทำงาน คุณไม่สามารถส่ง 0 ถึง datetime2 รหัสของคุณจะส่งคืน "ตัวถูกดำเนินการประเภท: int เข้ากันไม่ได้กับ datetime2"
แจ็ค

4

หากคุณไม่ต้องการที่จะไม่ใช้สายอักขระสิ่งนี้ก็ใช้ได้เช่นกัน (ใส่ไว้ในฟังก์ชั่น):

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

ฉันเพิ่มโซลูชันหนึ่งบรรทัดหากคุณต้องการวันที่และเวลาจากทั้งวันที่และเวลา :

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


2

สำหรับ SQL Server เวอร์ชันต่ำกว่า 12 ฉันสามารถแนะนำให้ใช้CASTร่วมกับSET DATEFORMAT

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

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

วิธีที่คุณสร้างสตริงเหล่านั้นขึ้นอยู่กับคุณ


1

ลองใช้แบบสอบถามนี้:

    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

ผลลัพธ์:

2014    Ja    1
2015    Ja    1
2014    Ja    1
2015    Ja    1
2012    Ja    1
2010    Ja    1
2015    Ja    1

1

ฉันรู้ว่า OP กำลังขอคำตอบ SQL 2005 แต่คำถามนั้นค่อนข้างเก่าดังนั้นถ้าคุณใช้ SQL 2012 ขึ้นไปคุณสามารถใช้สิ่งต่อไปนี้

SELECT DATEADD(DAY, 1, EOMONTH(@somedate, -1))

การอ้างอิง: https://docs.microsoft.com/en-us/sql/t-sql/functions/eomonth-transact-sql?view=sql-server-2017&viewFallbackFrom=sql-server-previousversions


0

ฉันชอบซับสตริงส่วนตัวเนื่องจากมีตัวเลือกการล้างข้อมูลและความสามารถในการแยกสตริงได้ตามต้องการ สมมติฐานคือข้อมูลเป็นรูปแบบ 'dd, mm, yyyy'

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

นี่คือการสาธิตวิธีที่จะถูกฟ้องร้องหากข้อมูลถูกเก็บไว้ในคอลัมน์ ไม่จำเป็นต้องพูดมันเหมาะที่จะตรวจสอบชุดผลลัพธ์ก่อนนำไปใช้กับคอลัมน์

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.