IBM DB2:生成两个日期之间的日期列表

时间:2016-02-23 13:21:55

标签: sql db2 db2-400 db2-luw

我需要一个查询,它将输出两个给定日期之间的日期列表。

例如,如果我的开始日期是2016年2月23日,结束日期是2016年3月2日,我期待以下输出:

Date
----
23/02/2016
24/02/2016
25/02/2016
26/02/2016
27/02/2016
28/02/2016
29/02/2016
01/03/2016
02/03/2016

另外,我只需要使用SQL(不使用'WITH'语句或表)。请帮忙。

4 个答案:

答案 0 :(得分:4)

我正在使用DB2 for iSeries,因此我将为您提供一个仅适用于SQL的解决方案。目前我无法访问服务器,因此查询未经过测试但应该可以正常运行。 编辑查询已经过测试并正常工作

SELECT
    d.min + num.n DAYS
FROM
    -- create inline table with min max date
    (VALUES(DATE('2015-02-28'), DATE('2016-03-01'))) AS d(min, max)
INNER JOIN
    -- create inline table with numbers from 0 to 999
    (
        SELECT
            n1.n + n10.n + n100.n AS n
        FROM
            (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS n1(n)
        CROSS JOIN
            (VALUES(0),(10),(20),(30),(40),(50),(60),(70),(80),(90)) AS n10(n)
        CROSS JOIN
            (VALUES(0),(100),(200),(300),(400),(500),(600),(700),(800),(900)) AS n100(n)
    ) AS num
ON
    d.min + num.n DAYS<= d.max
ORDER BY
    num.n;

如果您不想只执行一次查询,则应考虑创建一个包含循环值的真实表:

CREATE TABLE dummy_loop AS (
    SELECT
        n1.n + n10.n + n100.n AS n
    FROM
        (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS n1(n)
    CROSS JOIN
        (VALUES(0),(10),(20),(30),(40),(50),(60),(70),(80),(90)) AS n10(n)
    CROSS JOIN
        (VALUES(0),(100),(200),(300),(400),(500),(600),(700),(800),(900)) AS n100(n)
) WITH DATA;

ALTER TABLE dummy_loop ADD PRIMARY KEY (dummy_loop.n);

这取决于您喜欢使用它的原因,但您甚至可以创建表格让我们说100年。它只有100 * 365 = 36500行,只有一个日期字段,因此该表将非常小且连接速度快。

CREATE TABLE dummy_dates AS (
    SELECT
        DATE('1970-01-01') + (n1.n + n10.n + n100.n) DAYS AS date
    FROM
        (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS n1(n)
    CROSS JOIN
        (VALUES(0),(10),(20),(30),(40),(50),(60),(70),(80),(90)) AS n10(n)
    CROSS JOIN
        (VALUES(0),(100),(200),(300),(400),(500),(600),(700),(800),(900)) AS n100(n)
) WITH DATA;

ALTER TABLE dummy_dates ADD PRIMARY KEY (dummy_dates.date);

选择查询可能如下所示:

SELECT
    *
FROM
    dummy_days
WHERE
    date BETWEEN(:startDate, :endDate);

编辑2 :感谢@Lennart建议我已将TABLE(VALUES(..,..,..))更改为VALES(..,..,..),因为他是表示,TABLE是LATERAL的同义词,对我来说真是一个惊喜。

编辑3 :感谢@ godric7gt我删除了TIMESTAMPDIFF并将从我的所有脚本中删除,因为正如文档中所述:

  

将第二个参数(即时间戳持续时间)中的信息转换为第一个参数中指定的间隔类型时,将使用这些假设。 返回的估算值可能会有多天不等。例如,如果请求的天数(间隔16)为1997-03-01-00.00.00&#39;和&#39; 1997-02-01-00.00.00&#39;,结果是30.这是因为时间戳之间的差异是1个月,并且假设一个月中有30天。

这是一个真正的惊喜,因为我始终相信这个功能的天差。

答案 1 :(得分:2)

为了生成行,需要使用recusive SQL。 通常在DB2中看起来像这样:

with temp (date) as (
select date('23.02.2016') as date from sysibm.sysdummy1
union all
select date + 1 day from temp
where date < date('02.03.2016') 
)

从临时

中选择*

无论出于何种原因,都应避免使用CTE(使用WITH)。 可能的解决方法是设置

db2set DB2_COMPATIBILITY_VECTOR=8

可以使用CONNECT BY

来使用Oracle样式重写
SELECT date('22.02.2016') + level days  as dt
  FROM sysibm.sysdummy1 CONNECT BY date('22.02.2016') + level days <= date('02.03.2016')

请注意:在设置DB2_COMPATIBILITY_VECTOR之后,需要重新启动实例。

答案 2 :(得分:0)

此解决方案不使用WITH,但它确实使用WHILE和临时表...希望仍能满足您的需求?

编辑 - 我在SSMS 2014中构建了这个

DECLARE @Start DATE
DECLARE @End DATE

SET @Start = '2016-02-23'
SET @End = '2016-03-02'

CREATE TABLE #Dates ([Date] DATE)

WHILE @Start <= @End

BEGIN

INSERT INTO #Dates

SELECT @Start

SET @Start = DATEADD(Day,1,@Start)

END

SELECT * FROM #Dates

DROP TABLE #Dates

答案 3 :(得分:0)

我认为AS400不支持递归CTE,这就是为什么你想要一个没有它们的解决方案。我不知道它是否支持以下任何结构,但它可能值得一试。首先我们需要一个生成器,任何具有足够行数的表都可以。如果您没有足够大的表格来表示您想要的天数,您可以创建一个笛卡尔积。例如:

select row_number() over ()
from a_table
cross join a_table

扩展域的另一种方法是使用group by cube创建表的powerset,见下文。

假设我们可以通过某种方式创建足够大的行集。您可以生成如下日期:

select date('23/02/2016') + n days
from (
    select row_number() over () as n
    from a_table
) as t
where n < 100
order by n

如果由于某种原因您不想使用现有表格,则按立方体分组将产生基数等于属性的幂集的关系。在这里,我使用4列,将生成16行。

select date('2016-01-01') + row_number() over () days 
from sysibm.dual x 
group by cube(x.dummy, x.dummy, x.dummy, x.dummy)

如果你想生成100行,你需要7个组(因为2 ^ 7 = 128)属性在group by cube子句和fetch前100行:

select date('2016-01-01') + row_number() over () days 
from sysibm.dual x 
group by cube(x.dummy, x.dummy, x.dummy, x.dummy, x.dummy, x.dummy, x.dummy)
order by 1
fetch first 100 rows only