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

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

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

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

Пожалуйста, подумайте об изменении принятого ответа weblogs.sqlteam.com/jeffs/archive/2007/09/10/…

Brian Webster 19.04.2013 19:46

DATEFROMPARTS (год, месяц, день)

Kate 17.06.2020 01:23
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
267
2
416 881
15
Перейти к ответу Данный вопрос помечен как решенный

Ответы 15

Попробуйте 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 21.08.2013 18:11
Ответ принят как подходящий

Предполагая, что все 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 04.03.2013 16:56

Олег SQL Server DateTime дальше 1753-01-01 что-то не ходи.

CodeMonkey 01.07.2013 15:25

Этот ответ зависит от настроек формата даты, которые, если вы не укажете, зависят от региональных настроек вашего сервера. Формат yyyymmdd работает независимо от этих настроек. «Шести- или восьмизначная строка всегда интерпретируется как ymd». docs.microsoft.com/en-us/sql/t-sql/data-types/… См. Этот ответ: stackoverflow.com/a/46064419/2266979

Riley Major 07.09.2017 19:16

Если вы не хотите хранить в нем строки, это тоже работает (поместите в функцию):

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

Попробуй это:

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

@Brandon, вы должны отметить это как этот ответ. Это лучший. Сделайте это как услугу для других читателей StackOverflow.

Bill Paetzke 07.09.2010 22:37

Работает для високосных лет: выберите dateadd (mm, (@ y-1900) * 12 + @m - 1,0) + (@ d-1)

hidden 19.09.2011 01:02

Приводит к действительному, но ложному значению даты при передаче недопустимой комбинации значений, например. @Year = 2001, @Month = 13 и @DayOfMonth = 32 приводят к 2002-02-01T00:00:00.000. Принятый ответ (от Cade Roux) вызывает ошибку, что более полезно.

onedaywhen 14.12.2011 21:22

Необязательно начинать с нуля и добавлять дни. Вы можете начать напрямую с @ DayOfMonth-1, а затем добавить месяцы и годы. На DateAdd () на один меньше!

brianary 23.12.2011 03:31

у меня все еще кружится голова - неужели нет более аккуратного способа сделать это? (Мне поручено исправить запрос в SQL Server 2005)

Peter Perháč 20.05.2013 17:59

@ Питер, что значит «аккуратнее»? мне кажется, что Select DateAdd(yy, @Year-1900, DateAdd(m, @Month - 1, @DayOfMonth - 1)) довольно лаконичен и лаконичен.

Charles Bretana 26.09.2013 19:27

Будьте осторожны, если вы инициализируете свои переменные с использованием сокращенного синтаксиса DAY () MONTH () YEAR () DATEPART. Они возвращают INT и должны быть ОТЛИЧНЫ в TINYINT. Это одна из тех затрат на преобразование или размер разветвителей для волос с переменным размером.

Mathew A. 03.12.2013 00:04

@BillPaetzke Я тоже предпочитаю вариант DateAdd (), поскольку он не зависит от региона. Просто имейте в виду, что вы можете вводить НЕДЕЙСТВИТЕЛЬНЫЕ даты - например, ввод (2015, 2, 31) принимается и преобразуется в «3 марта 2015 года».

SQL Police 21.09.2015 09:28

Следует отметить, что в високосные годы это не работает: например, 29 февраля 2008 г .: SELECT DATEADD(yy, 2008 - 1900, DATEADD(mm, 2 - 1, 29 - 1));.

bambams 03.10.2016 22:45

Исправление: первоначальное предложение, похоже, работает и в високосные годы. Решение, предложенное @brianary, не работает.

bambams 03.10.2016 22:52

Проголосовал против, пока решение не устранит или не отметит ошибку високосного года в среднем решении ...

bambams 03.10.2016 22:57

@bambams Интересно, это потому, что 1900 год не високосный. Это исправляет изменение порядка добавления: select dateadd(month, 2 - 1, dateadd(year, 2008 - 1900, 29 - 1)); → 2008-02-29.

brianary 03.10.2016 23:05

@CharlesBretana - это социальный эксперимент, чтобы увидеть, сколько неправильных ответов можно проголосовать за лучший?

David Sopko 14.03.2019 04:28

Или используя только одну функцию 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)

лучший ответ ИМО. Имеет все преимущества ответа Чарльза и намного короче.

Michael 09.02.2012 22:39

Это, безусловно, самый чистый и простой способ. И он не выдает ошибку, когда значения дня выходят за пределы допустимого диапазона. Однако, в зависимости от обстоятельств, может возникнуть ошибка, поэтому просто имейте в виду, что при этом отключаются значения дня и месяца, выходящие за пределы ожидаемого диапазона.

Shawn Kovac 07.05.2014 22:12

В SQL Server 2012 есть замечательная и долгожданная новая функция DATEFROMPARTS (которая вызовет ошибку, если дата недействительна - мое основное возражение против решения этой проблемы на основе DATEADD):

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

DATEFROMPARTS(ycolumn, mcolumn, dcolumn)

или же

DATEFROMPARTS(@y, @m, @d)

Кроме того, ссылаясь на исходный вопрос, где упоминался объект Datetime, существует также функция под названием DATETIMEFROMPARTS: msdn.microsoft.com/pl-pl/library/hh213233%28v=sql.110%29.asp‌ x

Maciej Jaśniaczyk 23.10.2015 13:45

Безопаснее и аккуратнее использовать явную отправную точку "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 05.09.2012 16:33

Потому что это не сработает. Вы не можете преобразовать 0 в datetime2. Ваш код вернет "Конфликт типов операндов: int несовместим с datetime2"

Jack 07.09.2012 10:45

Пытаться

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

В Sql Server 2012 есть функция, которая будет создавать дату на основе частей (ДАТА ЧАСТИ). Для остальных из нас вот созданная мной функция 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

Я добавляю однострочное решение, если вам нужно datetime из частей даты и времени:

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

Для версий 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)

как вы создаете эти строки, зависит от вас

Попробуйте этот запрос:

    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

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

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

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

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

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

Я знаю, что 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

Другие вопросы по теме