Создать дату из месяца, месяца и года с помощью T-SQL

265

Я пытаюсь преобразовать дату с отдельными частями, такими как 12, 1, 2007, в дату и время в SQL Server 2005. Я пробовал следующее:

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

но это приводит к неправильной дате. Как правильно преобразовать три значения даты в правильный формат даты и времени?

Brandon
источник
8
Пожалуйста, подумайте об изменении вашего принятого ответа weblogs.sqlteam.com/jeffs/archive/2007/09/10/…
Брайан Вебстер
DATEFROMPARTS (год, месяц, день)
Кейт

Ответы:

175

Предполагая y, m, d, что все int, как насчет:

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

Пожалуйста, смотрите мой другой ответ для SQL Server 2012 и выше

Кейд Ру
источник
Плохой. Сочиняй меня из недр даты 1 января 0001 года
Олег Док
24
Олег 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
Райли Майор,
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 для значений datetime и smalldatetime является двумя. значение части, первая часть которой представляет собой целое число, представляющее количество дней с 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 октября 2016 г. (Спасибо @bambams за это замечание и @brinary за исправление), Последнее решение, предложенное @brinary. не работает в високосные годы, если сначала не выполняется сложение лет

select dateadd(month, @Month - 1, 
     dateadd(year, @Year-1900, @DayOfMonth - 1)); 
Чарльз Бретана
источник
36
@ Брэндон, вы должны пометить это как ответ. Это самый лучший. Сделайте это как услугу другим читателям StackOverflow.
Билл Паецке
3
Работает в високосные годы: выберите дату и время (мм, (@ y-1900) * 12 + @m - 1,0) + (@ d-1)
скрыто
8
Приводит к действительному, но ложному значению даты при передаче недопустимой комбинации значений, например @Year = 2001, @Month = 13и @DayOfMonth = 32приводит к 2002-02-01T00:00:00.000. Принятый ответ (от Cade Roux) выдает ошибку, которая более полезна.
Однажды, когда
6
Вам не нужно начинать с нуля и добавлять дни. Вы можете начать прямо с @ DayOfMonth-1, затем добавить месяцы и годы. Это на единицу меньше DateAdd ()!
brianary
2
моя голова все еще кружится - неужели нет более аккуратного способа сделать это? (Мне поручено исправить запрос в 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
лучший ответ ИМО. Имеет все преимущества ответа Чарльза и намного короче.
Майкл
1
Это, безусловно, самый чистый и простой. И это не выдает ошибку, когда дневные значения также находятся вне диапазона. Хотя в зависимости от обстоятельств может потребоваться ошибка, поэтому следует помнить, что при этом значения дня и месяца отключаются, что выходит за пределы ожидаемого диапазона.
Шон Ковач
17

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 на вашем сервере, без указания этого ограничения.

devio
источник
Согласитесь, формат необходимо уточнять, например, CONVERT (datetime2, CAST (@year AS varchar) + '.' + CAST (@month AS varchar) + '.' + CAST (@day AS varchar), 102)
Тони Уолл
9

Вы также можете использовать

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

Работает в SQL с версии 2012 и AzureSQL

Марсело Лухан
источник
6

Безопаснее и аккуратнее использовать явную отправную точку «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
разъем
источник
Почему бы не использовать только 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)
голубоватый
источник
3

Пытаться

CAST(STR(DATEPART(year, DATE))+'-'+ STR(DATEPART(month, DATE)) +'-'+ STR(DATEPART(day, DATE)) AS DATETIME)
Сол Гуэрра
источник
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
user3141962
источник
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

Я лично предпочитаю подстроку, так как она обеспечивает параметры очистки и возможность разбивать строку по мере необходимости. Предполагается, что данные имеют формат «дд, мм, гггг».

--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
Гури Шанкар Ачур
источник