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

6 ответов

Решение

Согласно записи BOL о DATEDIFF (разделаргументов) для SQL Server 2005,

Эти даты и аббревиатуры не могут быть предоставлены как объявленная пользователем переменная.

Таким образом, вы, вероятно, застряли с динамическим SQL или с помощью оператора CASE. Но я бы выбрал версию CASE вместо динамического SQL.

Старый, но все еще действующий, к сожалению.

Я сделал это по-своему и просто хочу поделиться кодом, чтобы вам не приходилось делать всю надоедливую типизацию, которую мне приходилось делать. Охватывает все возможные части даты. Просто замените имя функции и функцию даты для других функций даты 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

Единственное, что вы действительно можете сделать, кроме предложенного динамического оператора sql или case, - это всегда делать датировку в гранулированном DatePart и затем преобразовывать с повышением. Однако это не является надежным доказательством, вы получите переполнение в функции, если попытаетесь использовать datediff для детализации детали на слишком большом промежутке, например, datediff(second, 0, getdate()). Но если вам просто нужно что-то вроде мелких деталей, у вас все будет хорошо (перепроверьте с максимальными значениями даты, которые вас интересуют)

Так например

select datediff(minute, 0, getdate())

Если я хочу преобразовать это в часы, дни и т. Д., Я могу просто разделить результат на соответствующую сумму. Это не будет учитывать високосные годы и т. Д.

Определенно, использование динамического запроса, такого как этот ниже, работает очень хорошо, я предполагаю, что вы планируете использовать только сокращение для @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('+@datePart+', '+MyTable.MyDate+', GETDATE()) < '+@dateParameter

exec (@SQLTemp)

Я не думаю, что есть лучшие способы, чем вы описываете. Sql Server, вероятно, компилирует запрос DATEDIFF в набор операций, которые зависят от параметра datepart. Так что вам нужен CASE или динамические запросы.

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

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
Другие вопросы по тегам