[英]generate days from date range
我想运行一个查询
select ... as days where `date` is between '2010-01-20' and '2010-01-24'
并返回如下数据:
days ---------- 2010-01-20 2010-01-21 2010-01-22 2010-01-23 2010-01-24
此解决方案不使用循环、过程或临时表。 子查询生成过去 10,000 天的日期,并且可以扩展到任意远的后退或前进。
select a.Date
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a) ) DAY as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as d
) a
where a.Date between '2010-01-20' and '2010-01-24'
输出:
Date
----------
2010-01-24
2010-01-23
2010-01-22
2010-01-21
2010-01-20
性能注意事项
在这里测试一下,性能出奇的好:上面的查询需要 0.0009 秒。
如果我们扩展子查询以生成大约。 100,000 个数字(因此大约 274 年的日期价值),它在 0.0458 秒内运行。
顺便说一句,这是一种非常便携的技术,只需稍作调整即可与大多数数据库一起使用。
这是使用视图的另一种变体:
CREATE VIEW digits AS
SELECT 0 AS digit UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9;
CREATE VIEW numbers AS
SELECT
ones.digit + tens.digit * 10 + hundreds.digit * 100 + thousands.digit * 1000 AS number
FROM
digits as ones,
digits as tens,
digits as hundreds,
digits as thousands;
CREATE VIEW dates AS
SELECT
SUBDATE(CURRENT_DATE(), number) AS date
FROM
numbers;
然后你可以简单地做(看看它有多优雅?):
SELECT
date
FROM
dates
WHERE
date BETWEEN '2010-01-20' AND '2010-01-24'
ORDER BY
date
更新
值得注意的是,您只能从当前日期开始生成过去的日期。 如果要生成任何类型的日期范围(过去、未来和中间),则必须改用此视图:
CREATE VIEW dates AS
SELECT
SUBDATE(CURRENT_DATE(), number) AS date
FROM
numbers
UNION ALL
SELECT
ADDDATE(CURRENT_DATE(), number + 1) AS date
FROM
numbers;
接受的答案对 PostgreSQL 不起作用(“a”处或附近的语法错误)。
在 PostgreSQL 中这样做的方法是使用generate_series
函数,即:
SELECT day::date
FROM generate_series('2010-01-20', '2010-01-24', INTERVAL '1 day') day;
day
------------
2010-01-20
2010-01-21
2010-01-22
2010-01-23
2010-01-24
(5 rows)
使用递归公用表表达式 (CTE),您可以生成日期列表,然后从中进行选择。 显然您通常不想创建三百万个日期,所以这只是说明了可能性。 您可以简单地限制 CTE 内的日期范围,并从使用 CTE 的 select 语句中省略 where 子句。
with [dates] as (
select convert(datetime, '1753-01-01') as [date] --start
union all
select dateadd(day, 1, [date])
from [dates]
where [date] < '9999-12-31' --end
)
select [date]
from [dates]
where [date] between '2013-01-01' and '2013-12-31'
option (maxrecursion 0)
在 Microsoft SQL Server 2005 上,生成所有可能日期的 CTE 列表需要 1:08。 生成一百年只用了不到一秒钟。
MSSQL 查询
select datetable.Date
from (
select DATEADD(day,-(a.a + (10 * b.a) + (100 * c.a)),getdate()) AS Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) datetable
where datetable.Date between '2014-01-20' and '2014-01-24'
order by datetable.Date DESC
输出
Date
-----
2014-01-23 12:35:25.250
2014-01-22 12:35:25.250
2014-01-21 12:35:25.250
2014-01-20 12:35:25.250
在没有循环/游标的情况下执行此操作的老派解决方案是创建一个NUMBERS
表,该表具有单个 Integer 列,其值从 1 开始。
CREATE TABLE `example`.`numbers` (
`id` int(10) unsigned NOT NULL auto_increment,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
您需要使用足够的记录填充表以满足您的需求:
INSERT INTO NUMBERS (id) VALUES (NULL);
拥有NUMBERS
表后,您可以使用:
SELECT x.start_date + INTERVAL n.id-1 DAY
FROM NUMBERS n
JOIN (SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d') AS start_date
FROM DUAL) x
WHERE x.start_date + INTERVAL n.id-1 DAY <= '2010-01-24'
绝对的低技术解决方案是:
SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d')
FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-21', '%Y-%m-%d')
FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-22', '%Y-%m-%d')
FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-23', '%Y-%m-%d')
FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-24', '%Y-%m-%d')
FROM DUAL
生成日期或数字列表以便左连接。 您会这样做是为了查看数据中存在差距的位置,因为您是左连接到序列数据列表 - 空值将使存在差距的地方变得显而易见。
对于 Access 2010 - 需要多个步骤; 我遵循与上面发布的相同的模式,但认为我可以在 Access 中帮助某人。 对我来说效果很好,我不必保留种子日期表。
创建一个名为 DUAL 的表(类似于 Oracle DUAL 表的工作方式)
创建一个名为“ZeroThru9Q”的查询; 手动输入以下语法:
SELECT 0 AS a
FROM dual
UNION ALL
SELECT 1
FROM dual
UNION ALL
SELECT 2
FROM dual
UNION ALL
SELECT 3
FROM dual
UNION ALL
SELECT 4
FROM dual
UNION ALL
SELECT 5
FROM dual
UNION ALL
SELECT 6
FROM dual
UNION ALL
SELECT 7
FROM dual
UNION ALL
SELECT 8
FROM dual
UNION ALL
SELECT 9
FROM dual;
创建一个名为“TodayMinus1KQ”的查询(用于今天之前的日期); 手动输入以下语法:
SELECT date() - (a.a + (10 * b.a) + (100 * c.a)) AS MyDate
FROM
(SELECT *
FROM ZeroThru9Q) AS a,
(SELECT *
FROM ZeroThru9Q) AS b,
(SELECT *
FROM ZeroThru9Q) AS c
创建一个名为“TodayPlus1KQ”的查询(用于今天之后的日期); 手动输入以下语法:
SELECT date() + (a.a + (10 * b.a) + (100 * c.a)) AS MyDate
FROM
(SELECT *
FROM ZeroThru9Q) AS a,
(SELECT *
FROM ZeroThru9Q) AS b,
(SELECT *
FROM ZeroThru9Q) AS c;
创建一个名为“TodayPlusMinus1KQ”的联合查询(对于日期 +/- 1000 天):
SELECT MyDate
FROM TodayMinus1KQ
UNION
SELECT MyDate
FROM TodayPlus1KQ;
现在您可以使用查询:
SELECT MyDate
FROM TodayPlusMinus1KQ
WHERE MyDate BETWEEN #05/01/2014# and #05/30/2014#
程序+临时表:
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `days`(IN dateStart DATE, IN dateEnd DATE)
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS date_range (day DATE);
WHILE dateStart <= dateEnd DO
INSERT INTO date_range VALUES (dateStart);
SET dateStart = DATE_ADD(dateStart, INTERVAL 1 DAY);
END WHILE;
SELECT * FROM date_range;
DROP TEMPORARY TABLE IF EXISTS date_range;
END
谢谢 Pentium10 - 你让我加入 stackoverflow :) - 这是我移植到 msaccess - 认为它可以在任何版本上运行:
SELECT date_value
FROM (SELECT a.espr1+(10*b.espr1)+(100*c.espr1) AS integer_value,
dateadd("d",integer_value,dateserial([start_year], [start_month], [start_day])) as date_value
FROM (select * from
(
select top 1 "0" as espr1 from MSysObjects
union all
select top 1 "1" as espr2 from MSysObjects
union all
select top 1 "2" as espr3 from MSysObjects
union all
select top 1 "3" as espr4 from MSysObjects
union all
select top 1 "4" as espr5 from MSysObjects
union all
select top 1 "5" as espr6 from MSysObjects
union all
select top 1 "6" as espr7 from MSysObjects
union all
select top 1 "7" as espr8 from MSysObjects
union all
select top 1 "8" as espr9 from MSysObjects
union all
select top 1 "9" as espr9 from MSysObjects
) as a,
(
select top 1 "0" as espr1 from MSysObjects
union all
select top 1 "1" as espr2 from MSysObjects
union all
select top 1 "2" as espr3 from MSysObjects
union all
select top 1 "3" as espr4 from MSysObjects
union all
select top 1 "4" as espr5 from MSysObjects
union all
select top 1 "5" as espr6 from MSysObjects
union all
select top 1 "6" as espr7 from MSysObjects
union all
select top 1 "7" as espr8 from MSysObjects
union all
select top 1 "8" as espr9 from MSysObjects
union all
select top 1 "9" as espr9 from MSysObjects
) as b,
(
select top 1 "0" as espr1 from MSysObjects
union all
select top 1 "1" as espr2 from MSysObjects
union all
select top 1 "2" as espr3 from MSysObjects
union all
select top 1 "3" as espr4 from MSysObjects
union all
select top 1 "4" as espr5 from MSysObjects
union all
select top 1 "5" as espr6 from MSysObjects
union all
select top 1 "6" as espr7 from MSysObjects
union all
select top 1 "7" as espr8 from MSysObjects
union all
select top 1 "8" as espr9 from MSysObjects
union all
select top 1 "9" as espr9 from MSysObjects
) as c
) as d)
WHERE date_value
between dateserial([start_year], [start_month], [start_day])
and dateserial([end_year], [end_month], [end_day]);
引用 MSysObjects 只是“因为访问需要一个表计数”至少 1 条记录,在 from 子句中 - 任何至少有 1 条记录的表都可以。
在 MariaDB >= 10.3 和 MySQL >= 8.0 中使用新递归(公用表表达式)功能的优雅解决方案。
WITH RECURSIVE t as (
select '2019-01-01' as dt
UNION
SELECT DATE_ADD(t.dt, INTERVAL 1 DAY) FROM t WHERE DATE_ADD(t.dt, INTERVAL 1 DAY) <= '2019-04-30'
)
select * FROM t;
以上返回了一个介于 '2019-01-01' 和 '2019-04-30' 之间的日期表。 它也相当快。 在我的机器上返回 1000 年的日期(~365,000 天)大约需要 400 毫秒。
正如已经给出的许多精彩答案所述(或至少提到),一旦您有一组数字可以使用,这个问题就很容易解决。
注意:以下是 T-SQL,但它只是我在这里和整个互联网上已经提到的一般概念的特定实现。 将代码转换为您选择的方言应该相对简单。
如何? 考虑这个查询:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
以上产生的日期范围是 1/22/0001 - 1/27/0001 并且非常简单。 上述查询中有两条关键信息:开始日期0001-01-22
和偏移量5
。 如果我们将这两条信息结合起来,那么我们显然就有了结束日期。 因此,给定两个日期,生成一个范围可以这样分解:
找到两个给定日期之间的差异(偏移量),简单:
-- Returns 125 SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))
在此处使用ABS()
可确保日期顺序无关紧要。
生成一组有限的数字,也很简单:
-- Returns the numbers 0-2 SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1 FROM(SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A')
请注意,我们实际上并不关心FROM
这里选择什么。 我们只需要一个可以使用的集合,以便我们计算其中的行数。 我个人使用 TVF,有些使用 CTE,有些使用数字表,你懂的。 我提倡使用您也理解的最高效的解决方案。
结合这两种方法将解决我们的问题:
DECLARE @date1 DATE = '9001-11-21';
DECLARE @date2 DATE = '9001-11-23';
SELECT D = DATEADD(d, N, @date1)
FROM (
SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1
FROM (SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A') S
) Numbers
WHERE N <= ABS(DATEDIFF(d, @date1, @date2));
上面的例子是可怕的代码,但展示了一切是如何结合在一起的。
更多乐趣
这种事情我做的比较多,所以把逻辑封装成两个TVF。 第一个生成一系列数字,第二个使用此功能生成一系列日期。 数学是确保输入顺序无关紧要,因为我想使用GenerateRangeSmallInt
可用的完整数字范围。
以下函数需要大约 16 毫秒的 CPU 时间来返回 65536 个日期的最大范围。
CREATE FUNCTION dbo.GenerateRangeDate (
@date1 DATE,
@date2 DATE
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN (
SELECT D = DATEADD(d, N + 32768, CASE WHEN @date1 <= @date2 THEN @date1 ELSE @date2 END)
FROM dbo.GenerateRangeSmallInt(-32768, ABS(DATEDIFF(d, @date1, @date2)) - 32768)
);
GO
CREATE FUNCTION dbo.GenerateRangeSmallInt (
@num1 SMALLINT = -32768
, @num2 SMALLINT = 32767
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN (
WITH Numbers(N) AS (
SELECT N FROM(VALUES
(1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 16
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 32
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 48
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 64
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 80
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 96
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 112
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 128
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 144
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 160
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 176
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 192
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 208
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 224
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 240
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 256
) V (N)
)
SELECT TOP(ABS(CAST(@num1 AS INT) - CAST(@num2 AS INT)) + 1)
N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + CASE WHEN @num1 <= @num2 THEN @num1 ELSE @num2 END - 1
FROM Numbers A
, Numbers B
);
尝试这个。
SELECT TO_DATE('20160210','yyyymmdd') - 1 + LEVEL AS start_day
from DUAL
connect by level <= (TO_DATE('20160228','yyyymmdd') + 1) - TO_DATE('20160210','yyyymmdd') ;
对于想要将其作为保存视图的任何人(MySQL 不支持视图中的嵌套选择语句):
create view zero_to_nine as
select 0 as n union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9;
create view date_range as
select curdate() - INTERVAL (a.n + (10 * b.n) + (100 * c.n)) DAY as date
from zero_to_nine as a
cross join zero_to_nine as b
cross join zero_to_nine as c;
然后你可以做
select * from date_range
要得到
date
---
2017-06-06
2017-06-05
2017-06-04
2017-06-03
2017-06-02
...
您想获取日期范围。
在您的示例中,您希望获取 '2010-01-20' 和 '2010-01-24' 之间的日期
可能的解决方案:
select date_add('2010-01-20', interval row day) from
(
SELECT @row := @row + 1 as row FROM
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2,
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3,
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4,
(SELECT @row:=-1) r
) sequence
where date_add('2010-01-20', interval row day) <= '2010-01-24'
解释
MySQL 有一个date_add函数所以
select date_add('2010-01-20', interval 1 day)
会给你
2010-01-21
datediff函数会让你知道你必须经常重复这个
select datediff('2010-01-24', '2010-01-20')
返回
4
获取日期范围内的日期列表归结为创建整数序列参见在 MySQL 中生成整数序列
这里最受欢迎的答案采用了与https://stackoverflow.com/a/2652051/1497139类似的方法作为基础:
SELECT @row := @row + 1 as row FROM
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4,
(SELECT @row:=0) r
limit 4
这将导致
row
1.0
2.0
3.0
4.0
这些行现在可用于从给定的开始日期创建日期列表。 为了包括开始日期,我们从第 -1 行开始;
select date_add('2010-01-20', interval row day) from
(
SELECT @row := @row + 1 as row FROM
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4,
(SELECT @row:=-1) r
) sequence
where date_add('2010-01-20', interval row day) <= '2010-01-24'
这是例子
我们在一张桌子上有日期
表名:“测试日期”
STARTDATE ENDDATE
10/24/2012 10/24/2012
10/27/2012 10/29/2012
10/30/2012 10/30/2012
要求结果:
STARTDATE
10/24/2012
10/27/2012
10/28/2012
10/29/2012
10/30/2012
解决方案:
WITH CTE AS
(SELECT DISTINCT convert(varchar(10),StartTime, 101) AS StartTime,
datediff(dd,StartTime, endTime) AS diff
FROM dbo.testdate
UNION ALL SELECT StartTime,
diff - 1 AS diff
FROM CTE
WHERE diff<> 0)
SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime
FROM CTE
说明:CTE递归查询说明
查询的第一部分:
SELECT DISTINCT convert(varchar(10), StartTime, 101) AS StartTime, datediff(dd, StartTime, endTime) AS diff FROM dbo.testdate
说明:第一列是“开始日期”,第二列是开始和结束日期的天数差异,它将被视为“差异”列
查询的第二部分:
UNION ALL SELECT StartTime, diff-1 AS diff FROM CTE WHERE diff<>0
说明:Union all 将继承上述查询的结果,直到结果为 null,所以“StartTime”结果是从生成的 CTE 查询继承的,并且从 diff 开始减少 - 1,所以它看起来像 3、2 和 1 直到 0
例如
STARTDATE DIFF
10/24/2012 0
10/27/2012 0
10/27/2012 1
10/27/2012 2
10/30/2012 0
结果说明
STARTDATE Specification
10/24/2012 --> From Record 1
10/27/2012 --> From Record 2
10/27/2012 --> From Record 2
10/27/2012 --> From Record 2
10/30/2012 --> From Record 3
查询的第三部分
SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime FROM CTE
它将在“startdate”中添加日期“diff”,因此结果应如下所示
结果
STARTDATE
10/24/2012
10/27/2012
10/28/2012
10/29/2012
10/30/2012
如果您需要超过几天的时间,则需要一张桌子。
然后,
select from days.day, count(mytable.field) as fields from days left join mytable on day=date where date between x and y;
比接受的答案更短,同样的想法:
(SELECT TRIM('2016-01-05' + INTERVAL a + b DAY) date
FROM
(SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9 ) d,
(SELECT 0 b UNION SELECT 10 UNION SELECT 20
UNION SELECT 30 UNION SELECT 40) m
WHERE '2016-01-05' + INTERVAL a + b DAY <= '2016-01-21')
动态生成这些日期是个好主意。 但是,我觉得在相当大的范围内这样做并不舒服,所以我最终得到了以下解决方案:
CREATE TABLE DatesNumbers (
i MEDIUMINT NOT NULL,
PRIMARY KEY (i)
)
COMMENT='Used by Dates view'
;
INSERT INTO DatesNumbers
SELECT
a.i + (10 * b.i) + (100 * c.i) + (1000 * d.i) + (10000 * e.i) - 59999 AS i
FROM
(SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a,
(SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b,
(SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS c,
(SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS d,
(SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS e
;
SELECT
i,
CURRENT_DATE() + INTERVAL i DAY AS Date
FROM
DatesNumbers
就是这样。
适用于 AWS MySQL 的更通用的答案。
select datetable.Date
from (
select date_format(adddate(now(),-(a.a + (10 * b.a) + (100 * c.a))),'%Y-%m-%d') AS Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) datetable
where datetable.Date between now() - INTERVAL 14 Day and Now()
order by datetable.Date DESC
对于 Oracle,我的解决方案是:
select trunc(sysdate-dayincrement, 'DD')
from dual, (select level as dayincrement
from dual connect by level <= 30)
Sysdate 可以更改为特定日期,级别编号可以更改以提供更多日期。
好吧..试试这个: http : //www.devshed.com/c/a/MySQL/Delving-Deeper-into-MySQL-50/
http://dev.mysql.com/doc/refman/5.0/en/loop-statement.html
http://www.roseindia.net/sql/mysql-example/mysql-loop.shtml
使用它来生成临时表,然后在临时表上执行 select * 。 或者一次输出一个结果。
你说你想做的事情不能用 SELECT statement 完成,但它可能适用于特定于 MySQL 的事情。
再说一次,也许你需要游标: http : //dev.mysql.com/doc/refman/5.0/en/cursors.html
set language 'SPANISH'
DECLARE @table table(fechaDesde datetime , fechaHasta datetime )
INSERT @table VALUES('20151231' , '20161231');
WITH x AS
(
SELECT DATEADD( m , 1 ,fechaDesde ) as fecha FROM @table
UNION ALL
SELECT DATEADD( m , 1 ,fecha )
FROM @table t INNER JOIN x ON DATEADD( m , 1 ,x.fecha ) <= t.fechaHasta
)
SELECT LEFT( CONVERT( VARCHAR, fecha , 112 ) , 6 ) as Periodo_Id
,DATEPART ( dd, DATEADD(dd,-(DAY(fecha)-1),fecha)) Num_Dia_Inicio
,DATEADD(dd,-(DAY(fecha)-1),fecha) Fecha_Inicio
,DATEPART ( mm , fecha ) Mes_Id
,DATEPART ( yy , fecha ) Anio
,DATEPART ( dd, DATEADD(dd,-(DAY(DATEADD(mm,1,fecha))),DATEADD(mm,1,fecha))) Num_Dia_Fin
,DATEADD(dd,-(DAY(DATEADD(mm,1,fecha))),DATEADD(mm,1,fecha)) ultimoDia
,datename(MONTH, fecha) mes
,'Q' + convert(varchar(10), DATEPART(QUARTER, fecha)) Trimestre_Name
FROM x
OPTION(MAXRECURSION 0)
DELIMITER $$
CREATE PROCEDURE GenerateRangeDates(IN dateStart DATE, IN dateEnd DATE)
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS dates (day DATE);
loopDate: LOOP
INSERT INTO dates(day) VALUES (dateStart);
SET dateStart = DATE_ADD(dateStart, INTERVAL 1 DAY);
IF dateStart <= dateEnd
THEN ITERATE loopDate;
ELSE LEAVE loopDate;
END IF;
END LOOP loopDate;
SELECT day FROM dates;
DROP TEMPORARY TABLE IF EXISTS dates;
END
$$
-- Call procedure
call GenerateRangeDates(
now() - INTERVAL 40 DAY,
now()
);
SQLite版 RedFilters 顶级解决方案
select d.Date
from (
select
date(julianday('2010-01-20') + (a.a + (10 * b.a) + (100 * c.a))) as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) d
where
d.Date between '2010-01-20' and '2010-01-24'
order by d.Date
改进了工作日加入自定义假日表microsoft MSSQL 2012 for powerpivot 日期表https://gist.github.com/josy1024/cb1487d66d9e0ccbd420bc4a23b6e90e
with [dates] as (
select convert(datetime, '2016-01-01') as [date] --start
union all
select dateadd(day, 1, [date])
from [dates]
where [date] < '2018-01-01' --end
)
select [date]
, DATEPART (dw,[date]) as Wochentag
, (select holidayname from holidaytable
where holidaytable.hdate = [date])
as Feiertag
from [dates]
where [date] between '2016-01-01' and '2016-31-12'
option (maxrecursion 0)
使用递归公用表表达式的 mysql 8.0.1 和 mariadb 10.2.2 的另一种解决方案:
with recursive dates as (
select '2010-01-20' as date
union all
select date + interval 1 day from dates where date < '2010-01-24'
)
select * from dates;
WITH
Digits AS (SELECT 0 D UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9),
Dates AS (SELECT adddate('1970-01-01',t4.d*10000 + t3.d*1000 + t2.d*100 + t1.d*10 +t0.d) AS date FROM Digits AS t0, Digits AS t1, Digits AS t2, Digits AS t3, Digits AS t4)
SELECT * FROM Dates WHERE date BETWEEN '2017-01-01' AND '2017-12-31'
也可以创建一个程序来创建具有与日期不同的时间戳的日历表。 如果您想要每个季度的表格
例如
2019-01-22 08:45:00
2019-01-22 09:00:00
2019-01-22 09:15:00
2019-01-22 09:30:00
2019-01-22 09:45:00
2019-01-22 10:00:00
您可以使用
CREATE DEFINER=`root`@`localhost` PROCEDURE `generate_calendar_table`()
BEGIN
select unix_timestamp('2014-01-01 00:00:00') into @startts;
select unix_timestamp('2025-01-01 00:00:00') into @endts;
if ( @startts < @endts ) then
DROP TEMPORARY TABLE IF EXISTS calendar_table_tmp;
CREATE TEMPORARY TABLE calendar_table_tmp (ts int, dt datetime);
WHILE ( @startts < @endts)
DO
SET @startts = @startts + 900;
INSERT calendar_table_tmp VALUES (@startts, from_unixtime(@startts));
END WHILE;
END if;
END
然后通过操纵
select ts, dt from calendar_table_tmp;
这也给你 ts
'1548143100', '2019-01-22 08:45:00'
'1548144000', '2019-01-22 09:00:00'
'1548144900', '2019-01-22 09:15:00'
'1548145800', '2019-01-22 09:30:00'
'1548146700', '2019-01-22 09:45:00'
'1548147600', '2019-01-22 10:00:00'
从这里您可以开始添加其他信息,例如
select ts, dt, weekday(dt) as wd from calendar_table_tmp;
或使用create table 语句创建一个真实的表
类似于D'Arcy Rittich的答案,但适用于 SQL SERVER
;WITH t AS
(
SELECT n = a.n * 10 + b.n * 100 + c.n
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) a(n)
CROSS JOIN (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) b(n)
CROSS JOIN (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) c(n)
)
SELECT DATEADD(DAY, t.n, '2022-01-01')
FROM t
ORDER BY T.n;
或没有 CTE
SELECT DATEADD(DAY, nums.n, '2022-01-01') AS d FROM (
SELECT n = a.n * 10 + b.n * 100 + c.n
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) a(n)
CROSS JOIN (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) b(n)
CROSS JOIN (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) c(n)
) nums
order by d
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.