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