IBM DB2:生成两个日期之间的日期列表
IBM DB2: Generate list of dates between two dates
我需要一个查询来输出两个给定日期之间的日期列表。
例如,如果我的开始日期是 2016 年 2 月 23 日,结束日期是 2016 年 2 月 3 日,我期望得到以下输出:
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' 语句或表格)。请帮忙。
此解决方案不使用 WITH,但它确实使用 WHILE 和一个温度 table...希望它仍然能满足您的需求?
编辑——我在 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
要生成行,需要 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')
)
select * 来自温度
无论出于何种原因,都应避免使用 CTE(使用 WITH)。
一个可能的解决方法是设置
db2set DB2_COMPATIBILITY_VECTOR=8
这使得 Oracle 样式回避与 CONNECT BY
一起使用
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 后需要重启实例。
我假设 AS400 不支持递归 CTE,这就是为什么您想要没有它们的解决方案。我不知道它是否支持以下任何结构,但它可能值得一试。首先,我们需要一个生成器,任何具有足够行数的 table 都可以。如果您没有足够大的 table 来满足您想要的天数,您可以创建笛卡尔积。示例:
select row_number() over ()
from a_table
cross join a_table
扩展域的另一种方法是使用按立方体分组创建 table 的幂集,见下文。
假设我们可以通过某种方式创建足够大的行集。您可以生成如下日期:
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
如果出于某种原因您不想使用现有的 table,按多维数据集分组将生成基数等于属性幂集的关系。这里我使用 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 行,你需要 group by cube 子句中的 7(因为 2^7=128)属性和获取前 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
我使用的主要是 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;
如果您不想只执行一次查询,您应该考虑创建一个真正的 table,其中包含循环的值:
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);
这取决于您喜欢使用它的原因,但您甚至可以创建 table 让我们说 100 年。它只有 100*365 = 36500 行,只有一个日期字段,因此 table 对于联接来说非常小且速度很快。
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 查询可能如下所示:
SELECT
*
FROM
dummy_days
WHERE
date BETWEEN(:startDate, :endDate);
编辑 2:感谢@Lennart 的建议,我已将 TABLE(VALUES(..,..,..)) 更改为 VALES(.., ..,..) 因为正如他所说 TABLE 是 LATERAL 的同义词,这对我来说真是一个惊喜。
编辑 3:感谢 @godric7gt 我已经删除了 TIMESTAMPDIFF 并将从我的所有脚本中删除,因为正如文档中所说:
These assumptions are used when converting the information in the second argument, which is a timestamp duration, to the interval type specified in the first argument. The returned estimate may vary by a number of days. For example, if the number of days (interval 16) is requested for the difference between '1997-03-01-00.00.00' and '1997-02-01-00.00.00', the result is 30. This is because the difference between the timestamps is 1 month, and the assumption of 30 days in a month applies.
这真的是一个惊喜,因为我一直相信这个功能有天差地别。
我需要一个查询来输出两个给定日期之间的日期列表。
例如,如果我的开始日期是 2016 年 2 月 23 日,结束日期是 2016 年 2 月 3 日,我期望得到以下输出:
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' 语句或表格)。请帮忙。
此解决方案不使用 WITH,但它确实使用 WHILE 和一个温度 table...希望它仍然能满足您的需求?
编辑——我在 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
要生成行,需要 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')
)
select * 来自温度
无论出于何种原因,都应避免使用 CTE(使用 WITH)。 一个可能的解决方法是设置
db2set DB2_COMPATIBILITY_VECTOR=8
这使得 Oracle 样式回避与 CONNECT BY
一起使用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 后需要重启实例。
我假设 AS400 不支持递归 CTE,这就是为什么您想要没有它们的解决方案。我不知道它是否支持以下任何结构,但它可能值得一试。首先,我们需要一个生成器,任何具有足够行数的 table 都可以。如果您没有足够大的 table 来满足您想要的天数,您可以创建笛卡尔积。示例:
select row_number() over ()
from a_table
cross join a_table
扩展域的另一种方法是使用按立方体分组创建 table 的幂集,见下文。
假设我们可以通过某种方式创建足够大的行集。您可以生成如下日期:
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
如果出于某种原因您不想使用现有的 table,按多维数据集分组将生成基数等于属性幂集的关系。这里我使用 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 行,你需要 group by cube 子句中的 7(因为 2^7=128)属性和获取前 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
我使用的主要是 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;
如果您不想只执行一次查询,您应该考虑创建一个真正的 table,其中包含循环的值:
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);
这取决于您喜欢使用它的原因,但您甚至可以创建 table 让我们说 100 年。它只有 100*365 = 36500 行,只有一个日期字段,因此 table 对于联接来说非常小且速度很快。
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 查询可能如下所示:
SELECT
*
FROM
dummy_days
WHERE
date BETWEEN(:startDate, :endDate);
编辑 2:感谢@Lennart 的建议,我已将 TABLE(VALUES(..,..,..)) 更改为 VALES(.., ..,..) 因为正如他所说 TABLE 是 LATERAL 的同义词,这对我来说真是一个惊喜。
编辑 3:感谢 @godric7gt 我已经删除了 TIMESTAMPDIFF 并将从我的所有脚本中删除,因为正如文档中所说:
These assumptions are used when converting the information in the second argument, which is a timestamp duration, to the interval type specified in the first argument. The returned estimate may vary by a number of days. For example, if the number of days (interval 16) is requested for the difference between '1997-03-01-00.00.00' and '1997-02-01-00.00.00', the result is 30. This is because the difference between the timestamps is 1 month, and the assumption of 30 days in a month applies.
这真的是一个惊喜,因为我一直相信这个功能有天差地别。