如何创建递归查询以获取两个日期之间的所有日期

时间:2016-06-15 12:22:13

标签: sql sql-server database plsqldeveloper

我希望获得两个日期arrival dateleaving date之间的所有日期。

我尝试了递归查询,但它没有用。

;with dates as ( 
SELECT GuestID, ArrivalDate as reserveddate 
from dbo.Guest 
union all 
SELECT GuestID, dateadd (day,1,dbo.Guest. ArrivalDate) as reserveddate 
from dbo.Guest 
where dateadd (day,1,dbo.Guest. ArrivalDate) <dbo.Guest.leavingate
) 

SELECT * 
from dates 
option (maxrecursion 0)

3 个答案:

答案 0 :(得分:2)

您需要递归CTE:

int[] a = { 1, 5, 8 };
int[] b = { 6, 9 };

int aPos = 0;
int bPos = 0;
int n = a.length + b.length;

int[] result = new int[n];
for (int i = 0; i < n; i++) {
    if (aPos == a.length) {
        // at end of A, must use B
        result[i] = b[bPos++];
    } else if (bPos == b.length) {
        // at end of B, must use A
        result[i] = a[aPos++];
    } else if (a[aPos] < b[bPos]) {
        result[i] = a[aPos++];
    } else {
        result[i] = b[bPos++];
    }
}
System.out.println(Arrays.toString(result));

输出:

DECLARE  @arrival_date date = '2016-01-01',
         @leaving_date date = '2016-02-01'

;WITH cte AS (
SELECT @arrival_date as date_
UNION ALL
SELECT CAST(DATEADD(day,1,date_) as date)
FROM cte
WHERE date_ < @leaving_date
)

SELECT *
FROM cte
OPTION (MAXRECURSION 0)

<强> EDIT1

根据您的样本:

date_
2016-01-01
2016-01-02
2016-01-03
...
2016-01-30
2016-01-31
2016-02-01

<强> EDIT2

;WITH cte AS (
SELECT GuestID, CAST(ArrivalDate as date) as date_
FROM Guests
UNION ALL
SELECT c.GuestID, CAST(DATEADD(day,1,date_) as date)
FROM cte c
INNER JOIN Guests g
    ON g.GuestID = c.GuestID
WHERE date_ < g.LeavingDate
)

SELECT *
FROM cte
ORDER BY GuestID, date_
OPTION (MAXRECURSION 0)

输出:

;WITH Guests AS (
SELECT  1 as GuestID,
        '2016-01-01' ArrivalDate,
        '2016-01-05' LeavingDate
UNION ALL
SELECT  2 ,
        '2016-06-17',
        '2016-06-20'
), cte AS (
SELECT GuestID, CAST(ArrivalDate as date) as date_
FROM Guests
UNION ALL
SELECT c.GuestID, CAST(DATEADD(day,1,date_) as date)
FROM cte c
INNER JOIN Guests g
    ON g.GuestID = c.GuestID
WHERE date_ < g.LeavingDate
)

SELECT *
FROM cte
ORDER BY GuestID, date_
OPTION (MAXRECURSION 0)

答案 1 :(得分:1)

我喜欢这种类型的东西是使用计数表。我对每个看起来像这样的系统都有所了解。

data.frame

通过这种方式,我总能随时随地拿着一张理货桌。您可以在此处阅读有关计数表以及它们如何替换循环的更多信息。 http://www.sqlservercentral.com/articles/T-SQL/62867/

要解决您当前的问题,这变得相当简单。首先,我们需要一个表格和一些样本数据。

create View [dbo].[cteTally] as

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 N from cteTally
GO

这是理货桌真正显示的力量所在。为挑战生成输出就像这段代码一样简单。

create table #Guest
(
    GuestID int identity
    , ArrivalDate date
    , DepartureDate date
)

insert #Guest
select '2016-06-01', '2016-06-15' union all
select '2016-07-01', '2016-07-12'

这种方法相对于递归cte的另一大优势是,您使用的是所谓的三角形连接,可能会导致一些性能挑战。以下是有关三角形连接的更多信息。 http://www.sqlservercentral.com/articles/T-SQL/61539/

答案 2 :(得分:0)

试试这个

    DECLARE @DateFrom DateTime = DATEADD(DAY,DATEDIFF(DAY,0,GETDATE())-5,0)
    DECLARE @DateTo DateTime = CONVERT(DATE,GETDATE())

    ;WITH Numbers (Number) AS (
         SELECT row_number() OVER (ORDER BY object_id)
         FROM sys.all_objects
    )

    SELECT  dateadd(DAY, number-1, @DateFrom)           
    FROM    Numbers
    WHERE   number <= datediff(DAY, @DateFrom-1, @DateTo-1)

您可以通过在选择日期和日期中更改DAY到WEEK,MONTH,YEAR等来更改日期间隔