SQL Dynamic DatePart при использовании DateDiff - PullRequest
19 голосов
/ 07 мая 2009

Есть ли способ передать параметр 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 ]

19 голосов
/ 07 мая 2009

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

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

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

16 голосов
/ 11 июля 2013

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

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

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

Так например

select datediff(minute, 0, getdate())

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

1 голос
/ 25 января 2013

Определенно, использование динамического запроса, такого как этот ниже, работает очень хорошо, я предполагаю, что вы планируете использовать только сокращение для @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)
0 голосов
/ 13 ноября 2013

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

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
0 голосов
/ 07 мая 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...