2015-11-04 77 views
1

在SQL Server 2008 R2,在存儲過程中我有2個DECLARE和遞歸CTE中的腳本:在SQL Server中轉換一個遞歸CTE的功能

DECLARE @MaxActivityDate DATETIME = (SELECT MAX(ActivityDate) FROM tbl) 
DECLARE @MinActivityDate DATETIME = (SELECT MIN(ActivityDate) FROM tbl) 

-- Generate all the dates between the minimum and the maximum 
WITH DateRange (DateValue) AS 
(
    SELECT @MinActivityDate DateValue 
    UNION ALL 
     SELECT DateValue + 1 
     FROM DateRange 
     WHERE DateValue + 1 <= @MaxActivityDate 
) 

SELECT DateValue 
FROM DateRange 
[...] 

我現在需要使用此代碼另一個存儲過程。我不想要任何代碼重複。我怎樣才能把它轉換成一個函數在兩個腳本中使用?

+0

把這個變成一個功能時要小心 - 它是「隱藏」的事實,有一個在這個功能的數據庫訪問,並多次調用此代碼較大'SELECT'內(返回大量的行)可能會導致糟糕的表現。 –

+0

我已經隱藏了一些代碼(爲了簡化問題),它調整了MIN,MAX數字,以便它們相距1年。 – Adam

+0

https://www.mssqltips.com/sqlservertip/2800/sql-server-function-to-return-a-range-of-dates/ – JamieD77

回答

2

請注意這種類型的cte。即使沒有成爲更大的查詢的一部分,它也是隱藏的RBAR。這與使用cte進行計數的問題相同。這裏有一篇很棒的文章解釋了這個問題。 http://www.sqlservercentral.com/articles/T-SQL/74118/

爲了更好的方法,你應該使用一個理貨表。這是一篇這樣的文章,更深入到這種技術。 http://www.sqlservercentral.com/articles/T-SQL/62867/

這是一個基於集合的內聯表值函數來做這種事情。

create function GetDateRange 
(
    @MinActivityDate datetime 
    , @MaxActivityDate DATETIME 
) RETURNS TABLE AS 
    RETURN 

    WITH 
    E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)), 
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows 
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max 
    cteTally(N) AS 
    (
     SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4 
    ) 

    select DATEADD(DAY, N - 1, @MinActivityDate) as DateValue 
    from cteTally t 
    where t.N <= DATEDIFF(DAY, @MinActivityDate, @MaxActivityDate) 

現在使用這是非常簡單的。這是您可以在當前示例中使用的一種方法。

select dr.DateValue 
from GetDateRange(myDate.MinDate, myDate.MaxDate) dr 
cross apply 
(
    select MIN(ActivityDate) as MinDate 
     , MAX(ActivityDate) as MaxDate 
    from tbl 
) myDate