2017-01-05 3 views
0

У меня есть огромные вычисления, происходящие в SQL на основе дат и лет. Когда я добавляю месяцы к дате, это не добавление по дням, в основном добавление 3 месяцев (это функциональность этих функций).Проблема с функцией DATEADD

например SELECT DATEADD(month, 4, '2016-10-25'). Это выбор того, что я ожидаю, что 2017-02-25

но когда я это сделаю DATEADD(month, 4, '2016-10-30'). Его выбор 2017-02-28. Это не то, чего я ожидаю. Я знаю, что эта функция просто добавляет несколько месяцев и доводит ее до последнего дня того месяца.

В этом случае, если бы я хотел видеть вывод как 2016-02-30, это было бы возможно, потому что я знаю, что дата не существует. или мы сможем запрограммировать его на возврат 2017-03-01 вместо 2017-02-28. (Это становится большой проблемой в течение високосного года, так как у нас есть 29 февраля)

Я очень ценю ваш ответ на это. Спасибо.

+1

но что бы результат, который вы на самом деле хотите ?. Возможно, вам захочется добавить дни вместо этого, но вам придется иметь дело с несколькими месяцами, у которых есть 28, другие 30, 31 .... – Lamak

+2

, пожалуйста, укажите, почему вы добавляете месяцы и какова цель с результатом, поэтому вы можете получить подходящий ответ – Tanner

+0

'2016-02-30' недействительная дата. Он должен вернуться 29-го февраля в високосный год, хотя ... вы пробовали это? 2016 - високосный год, а не 2017. Я согласен с подходом к дням, если это то, что вам нужно. несколько месяцев имеют 31 день назад (июль и август, декабрь и январь), поэтому месяцы не кажутся постоянным интервалом, если вы ожидаете этой функциональности. – ps2goat

ответ

0

Очевидно, что никакая разумная программная система не собирается производить 2017-02-30. Этого слишком много, чтобы просить.

Если вы хотите, чтобы перейти на следующий месяц, а не идти в последний день месяца, вы можете сделать:

select (case when day(d) <= 28 or day(d) = day(dateadd(month, 4, d)) 
      then dateadd(month, 4, d) 
      else dateadd(month, 5, dateadd(day, 1 - day(d), day) 
     end) 

Это говорит:

  1. Добавление 4 месяца нормально так поскольку день через четыре месяца совпадает с днем ​​месяца.
  2. В противном случае перейдите к началу месяца 5 месяцев позже.
1

Вы могли бы сделать некоторые расчеты, основанные на день числа месяца добавляется к и добавить дополнительные дни, когда адресат месяц не имеет, что много дней:

CREATE TABLE #dates (val DATE); 

INSERT INTO #dates 
     (val) 
VALUES ('20160131'), 
     ('20160130'), 
     ('20160129'); 

SELECT val , 
     DATEADD(MONTH, 1, val) StandardMonthAdd , 
     CASE WHEN DATEPART(DAY, val) != DATEPART(DAY, DATEADD(MONTH, 1, val)) 
      THEN DATEADD(DAY, 
          DATEPART(DAY, val) - DATEPART(DAY, 
                 DATEADD(MONTH, 1, val)), 
          DATEADD(MONTH, 1, val)) 
      ELSE DATEADD(MONTH, 1, val) 
     END CalculatedMonthAdd 
FROM #dates; 

DROP TABLE #dates; 

Производит:

val   StandardMonthAdd CalculatedMonthAdd 
2016-01-31 2016-02-29   2016-03-02 
2016-01-30 2016-02-29   2016-03-01 
2016-01-29 2016-02-29   2016-02-29 

Это предполагает, что для записи 1, поскольку в феврале нет 31 дня, вы хотите добавить 2 дня, а для записи 2 - 1 день.

+0

Благодарим вас за это решение. Похоже, этот подход обеспечивает то, что ожидаемый – Naga

0

Попробуйте это для 31-го февраля и т.д.

-- ================================================ 
-- Template generated from Template Explorer using: 
-- Create Scalar Function (New Menu).SQL 
-- 
-- Use the Specify Values for Template Parameters 
-- command (Ctrl-Shift-M) to fill in the parameter 
-- values below. 
-- 
-- This block of comments will not be included in 
-- the definition of the function. 
-- ================================================ 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
-- ============================================= 
-- Author:  <Author,,Name> 
-- Create date: <Create Date, ,> 
-- Description: <Description, ,> 
-- ============================================= 
create FUNCTION fn_Add_Full_Months 
(
    -- Add the parameters for the function here 
    @DATE as datetime, 
    @MONTHADD as int 
) 
RETURNS nvarchar(10) 
AS 
BEGIN 
    -- Declare the return variable here 

    DECLARE @DAYS as int = DAY(@DATE); 
    DECLARE @MONTHS as int = MONTH(@DATE); 
    DECLARE @YEARS as int = YEAR(@DATE); 

    DECLARE @TRIAL as int = @YEARS*12 + @MONTHS - 1 + @MONTHADD; 

    RETURN CAST(@TRIAL/12 AS nvarchar(4)) + '-' + 
       RIGHT('0' + CAST(@TRIAL % 12 + 1 AS nvarchar(4)), 2) + '-' + 
       RIGHT('0' + CAST(@DAYS AS nvarchar(4)), 2); 

END 
GO 

затем

select dbo.fn_Add_Full_Months('19960131', 1); 
+0

Спасибо, Катон, но я не смог бы преобразовать его в дату, потому что он не существует. Мне нужно будет использовать это как строку? – Naga

+0

@naga - это было бы правильно, если бы вы попытались измените одну из строк с несуществующей датой в ней, вы получите ошибку - кроме того, отображение неправильных дат для пользователя может быть плохой идеей - вы можете быть лучше с одной из других идей. – Cato

Смежные вопросы