函数/ While循环中的动态SQL使用动态SQL

时间:2017-05-02 17:17:23

标签: sql function tsql dynamic

我编写了以下查询...但是我很难将其转换为函数,因为有几个原因......(1)因为它包含动态SQL而且我不确定函数是否允许动态SQL,以及(2)因为我使用临时表来存储输出数据。我尝试将它转换为表变量而没有运气。查询的要点是采用任意两个日期时间范围和增量类型(月,日,年,小时等),并吐出一个包含所提供范围的日期范围表。

查询的原因是因为我必须为报表服务器编写大量报表,并且报表请求多次要按小时,日,周,月等进行分解,而且我经常要编写相同的报表反复查询以生成日期范围表。我使用这些表来保持连接,以便我的最终输出不会跳过任何日期范围,以防没有该范围的数据,它仍将正确填写零和图表/图表。

我认为没有办法让这个代码在一个函数中运行,除非有一个完全不同的编写方式(编辑,请看更新)...我敢肯定我可以去长期复杂有一些长的案例陈述的路线,而不是只有每个增量类型(秒,分钟,小时,日,周,月,季,年),然后我可以消除动态SQL。也许我可以使用递归CTE并消除while循环。但我对这些没有任何经验。

以下是代码:

DECLARE @DateFrom DATETIME = '2017-01-01',
        @DateTo DATETIME = '2017-02-02 23:59:59.997',
        @Increment VARCHAR(20) = 'mm'

DECLARE @SQL NVARCHAR(MAX) = NULL

IF OBJECT_ID('tempdb..#DateRange') IS NOT NULL DROP TABLE #DateRange --SELECT * FROM #DateRange
CREATE TABLE #DateRange (BeginDate DATETIME, EndDate DATETIME)

SELECT @SQL = '
    DECLARE @TargetDate DATETIME = ''' + CONVERT(VARCHAR, @DateFrom, 121) + '''
    IF DATEDIFF('+ @Increment +', ''' + CONVERT(VARCHAR, @DateFrom, 121) + ''', ''' + CONVERT(VARCHAR, @DateTo, 121) + ''') > 2000 RETURN

    WHILE (1=1)
    BEGIN
        INSERT INTO #DateRange
        SELECT BeginDate = DATEADD('+ @Increment +', DATEDIFF('+ @Increment +', 0, @TargetDate)    , 0)
            , EndDate =    DATEADD(ms, -3, DATEADD('+ @Increment +', DATEDIFF('+ @Increment +', 0, @TargetDate) + 1, 0))

        SET @TargetDate = DATEADD('+ @Increment +', 1, @TargetDate)
        IF @TargetDate > ''' + CONVERT(VARCHAR, @DateTo, 121) + ''' BREAK
    END'

EXEC sp_executesql @SQL

SELECT * FROM #DateRange

编辑:这是一个修改过的版本,不确定这是否会被认为是“更好”,但至少它会消除动态SQL,我可以将它作为一个函数使用。

EDIT2:由于运行速度更快,我将限制更改为返回的5000条记录。我也改变了它运行的方向,所以它从DateTo开始并向后工作。这样当它达到极限时,它只会停止到目前为止。我还添加了一些安全性,比如检查增量(不确定还有什么叫它?)选项值。我讨厌这个巨大的案例陈述,但我没有看到任何其他方法去做。

CREATE FUNCTION dbo.uf_DateRange (  
    @DateFrom DATETIME,
    @DateTo DATETIME,
    @Increment VARCHAR(20)
)
RETURNS @Return TABLE  (
    BeginDate       DATETIME,
    EndDate         DATETIME
)
AS
BEGIN
    IF @Increment NOT IN ('year','yy','yyyy','quarter','qq','q','month','mm','m','week','wk','ww','day','dd','d','hour','hh','minute','mi','n','second','ss','s') RETURN

    DECLARE @TargetDate DATETIME = @DateTo,
            @LoopLimit INT = 5000,
            @Counter INT = 0

    DECLARE @DateRange TABLE (BeginDate DATETIME, EndDate DATETIME)

    WHILE (@Counter < @LoopLimit)
    BEGIN
        INSERT INTO @Return (BeginDate, EndDate)
        SELECT BeginDate    =   CASE WHEN @Increment IN ('year'   , 'yy', 'yyyy') THEN DATEADD(yy, DATEDIFF(yy, 0, @TargetDate), 0)
                                     WHEN @Increment IN ('quarter', 'qq', 'q'   ) THEN DATEADD(qq, DATEDIFF(qq, 0, @TargetDate), 0)
                                     WHEN @Increment IN ('month'  , 'mm', 'm'   ) THEN DATEADD(mm, DATEDIFF(mm, 0, @TargetDate), 0)
                                     WHEN @Increment IN ('week'   , 'wk', 'ww'  ) THEN DATEADD(ww, DATEDIFF(ww, 0, @TargetDate), 0)
                                     WHEN @Increment IN ('day'    , 'dd', 'd'   ) THEN DATEADD(dd, DATEDIFF(dd, 0, @TargetDate), 0)
                                     WHEN @Increment IN ('hour'   , 'hh'        ) THEN DATEADD(hh, DATEDIFF(hh, 0, @TargetDate), 0)
                                     WHEN @Increment IN ('minute' , 'mi', 'n'   ) THEN DATEADD(mi, DATEDIFF(mi, 0, @TargetDate), 0)
                                     WHEN @Increment IN ('second' , 'ss', 's'   ) THEN DATEADD(ss, DATEDIFF(ss, 0, @TargetDate), 0)
                                END
            , EndDate       =   DATEADD(ms, -3,
                                CASE WHEN @Increment IN ('year'   , 'yy', 'yyyy') THEN DATEADD(yy, DATEDIFF(yy, 0, @TargetDate) + 1, 0)
                                     WHEN @Increment IN ('quarter', 'qq', 'q'   ) THEN DATEADD(qq, DATEDIFF(qq, 0, @TargetDate) + 1, 0)
                                     WHEN @Increment IN ('month'  , 'mm', 'm'   ) THEN DATEADD(mm, DATEDIFF(mm, 0, @TargetDate) + 1, 0)
                                     WHEN @Increment IN ('week'   , 'wk', 'ww'  ) THEN DATEADD(ww, DATEDIFF(ww, 0, @TargetDate) + 1, 0)
                                     WHEN @Increment IN ('day'    , 'dd', 'd'   ) THEN DATEADD(dd, DATEDIFF(dd, 0, @TargetDate) + 1, 0)
                                     WHEN @Increment IN ('hour'   , 'hh'        ) THEN DATEADD(hh, DATEDIFF(hh, 0, @TargetDate) + 1, 0)
                                     WHEN @Increment IN ('minute' , 'mi', 'n'   ) THEN DATEADD(mi, DATEDIFF(mi, 0, @TargetDate) + 1, 0)
                                     WHEN @Increment IN ('second' , 'ss', 's'   ) THEN DATEADD(ss, DATEDIFF(ss, 0, @TargetDate) + 1, 0)
                                END)        
        SET @TargetDate     =   CASE WHEN @Increment IN ('year'   , 'yy', 'yyyy') THEN DATEADD(yy, -1, @TargetDate)
                                     WHEN @Increment IN ('quarter', 'qq', 'q'   ) THEN DATEADD(qq, -1, @TargetDate)
                                     WHEN @Increment IN ('month'  , 'mm', 'm'   ) THEN DATEADD(mm, -1, @TargetDate)
                                     WHEN @Increment IN ('week'   , 'wk', 'ww'  ) THEN DATEADD(ww, -1, @TargetDate)
                                     WHEN @Increment IN ('day'    , 'dd', 'd'   ) THEN DATEADD(dd, -1, @TargetDate)
                                     WHEN @Increment IN ('hour'   , 'hh'        ) THEN DATEADD(hh, -1, @TargetDate)
                                     WHEN @Increment IN ('minute' , 'mi', 'n'   ) THEN DATEADD(mi, -1, @TargetDate)
                                     WHEN @Increment IN ('second' , 'ss', 's'   ) THEN DATEADD(ss, -1, @TargetDate)
                                END
        IF @TargetDate > @DateTo BREAK
        SET @Counter += 1
    END

    RETURN
END
GO

1 个答案:

答案 0 :(得分:0)

你将在这里遇到一系列限制。我的理解是一个函数不能执行一个sproc,所以这是一个很大的显示停止。此外,您不能使用函数执行INSERT(或UPDATE或DELETE),因此还有另一个大问题。

是否有特定原因您不能将其作为程序留下?