SQL Dynamic DatePart при использовании DateDiff
Есть ли способ передать параметр DatePart DateDiff в качестве переменной?
Так что я могу написать код, похожий на этот?
DECLARE @datePart VARCHAR(2)
DECLARE @dateParameter INT
SELECT @datePart = 'dd'
SELECT @dateParameter = 28
SELECT
*
FROM
MyTable
WHERE
DATEDIFF(@datePart, MyTable.MyDate, GETDATE()) < @dateParameter
Единственные способы, с помощью которых я могу это сделать, - это оператор CASE, проверяющий значение параметра или построение SQL как строки и запуск его в EXEC.
Есть ли у кого-нибудь "лучшие" предложения? Платформа MS SQL Server 2005
Ответы
Ответ 1
Согласно записи BOL на DATEDIFF (раздел аргументов) для SQL Server 2005,
Эти даты и аббревиатуры не могут быть представлены как объявленная пользователем переменная.
Итак, вы, вероятно, застряли в Dynamic SQL или используете оператор CASE.
Но я бы выбрал версию CASE вместо динамического SQL.
Ответ 2
Старый, но все же действительный к сожалению.
Я сделал это в том случае и просто хочу поделиться кодом, поэтому вам не нужно делать все неприятное печатание, которое я должен был сделать. Обложка всех возможных деталей даты. Просто замените имя функции и функцию даты для реализации для других функций даты T-SQL.
Копировать и вставить раздел
-- SELECT dbo.fn_DateAddFromStringPart('year', 1, GETDATE())
CREATE FUNCTION fn_DateAddFromStringPart
(
@Interval VARCHAR(11),
@Increment INT,
@Date SMALLDATETIME
)
RETURNS DATETIME
AS
BEGIN
-- Declare the return variable here
DECLARE @NewDate DATETIME
-- Add the T-SQL statements to compute the return value here
SELECT @NewDate = CASE
WHEN @Interval IN ('year', 'yy', 'yyyy') THEN DATEADD(YEAR, @Increment, @Date)
WHEN @Interval IN ('quarter', 'qq', 'q') THEN DATEADD(QUARTER, @Increment, @Date)
WHEN @Interval IN ('month', 'mm', 'm') THEN DATEADD(MONTH, @Increment, @Date)
WHEN @Interval IN ('dayofyear', 'dy', '') THEN DATEADD(DAYOFYEAR, @Increment, @Date)
WHEN @Interval IN ('day', 'dd', 'd') THEN DATEADD(DAY, @Increment, @Date)
WHEN @Interval IN ('week', 'wk', 'ww') THEN DATEADD(WEEK, @Increment, @Date)
WHEN @Interval IN ('weekday', 'dw', 'w') THEN DATEADD(WEEKDAY, @Increment, @Date)
WHEN @Interval IN ('hour', 'hh') THEN DATEADD(HOUR, @Increment, @Date)
WHEN @Interval IN ('minute', 'mi', 'n') THEN DATEADD(MINUTE, @Increment, @Date)
WHEN @Interval IN ('second', 'ss', 's') THEN DATEADD(SECOND, @Increment, @Date)
WHEN @Interval IN ('millisecond', 'ms') THEN DATEADD(MILLISECOND, @Increment, @Date)
WHEN @Interval IN ('microsecond', 'mcs') THEN DATEADD(MICROSECOND, @Increment, @Date)
WHEN @Interval IN ('nanosecond', 'ns') THEN DATEADD(NANOSECOND, @Increment, @Date)
END
-- Return the result of the function
RETURN @NewDate
END
GO
Ответ 3
Единственное, что вы действительно можете сделать в стороне от предлагаемого динамического SQL или case-заявления, - это всегда делать датированный в гранулярном DatePart, а затем преобразовывать. Это не является доказательством дурака, но вы получите переполнение в функции, если вы попробуете датифик, чтобы детализировать часть слишком большого диапазона, например. dateiff (во-вторых, 0, getdate()). Но если вам просто нужно что-то вроде минутных деталей, вы должны быть в порядке (двойная проверка с максимальными значениями даты, о которых вы заботитесь).
Итак, например
select datediff(minute, 0, getdate())
Если я хочу преобразовать это в часы, дни и т.д., я могу просто разделить результат на соответствующую сумму. Он не будет учитывать високосные годы и т.д.
Ответ 4
Определенно использование динамического запроса, такого как это ниже, работает очень хорошо, я предполагаю, что вы планируете использовать только аббревиатуру для @datePart. Я бы рекомендовал использовать минимум VARCHAR (4) для datepart, который будет обрабатывать такие сокращения, как yyyy и mcs. Счастливое кодирование:
DECLARE @datePart VARCHAR(2)
DECLARE @dateParameter INT
DECLARE @SQLTemp varchar(MAX)
SELECT @datePart = 'dd'
SELECT @dateParameter = 28
set @SQLTemp ='SELECT * FROM MyTable
WHERE DATEDIFF('[email protected]+', '+MyTable.MyDate+', GETDATE()) < '[email protected]
exec (@SQLTemp)
Ответ 5
Я не думаю, что есть лучшие способы, которые вы описываете. Сервер Sql, вероятно, компилирует запрос DATEDIFF к набору операций, которые зависят от параметра datepart. Таким образом, вам понадобится CASE или динамические запросы.
Ответ 6
Я создал функцию Scalar values для этого. Он основан на решениях, упомянутых выше, но проще реализовать в вашем коде
CREATE FUNCTION [dbo].[dynamic_dateadd]
(
@unit varchar(5),
@number int,
@dt datetime
)
RETURNS datetime
AS
BEGIN
declare @result datetime
if (@unit='M') BEGIN SET @result=(select DATEADD(M,@number,@dt)) END
if (@unit='WW') BEGIN SET @result=(select DATEADD(WW,@number,@dt)) END
if (@unit='D') BEGIN SET @result=(select DATEADD(D,@number,@dt)) END
if (@unit='H') BEGIN SET @result=(select DATEADD(HH,@number,@dt)) END
return(@result)
END
В запросе вы можете использовать эту функцию следующим образом:
select startdate, validity_unit, validity_number,
dbo.dynamic_dateadd(valididy_unit,validity_number,startdate) as enddate
from SALES_subscription