从日期范围生成天数

Pen*_*m10 134 mysql sql datetime between

我想运行一个类似的查询

select ... as days where `date` is between '2010-01-20' and '2010-01-24'
Run Code Online (Sandbox Code Playgroud)

并返回如下数据:

days
----------
2010-01-20
2010-01-21
2010-01-22
2010-01-23
2010-01-24

Red*_*ter 310

此解决方案不使用循环,过程或临时表.子查询生成最近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' 
Run Code Online (Sandbox Code Playgroud)

输出:

Date
----------
2010-01-24
2010-01-23
2010-01-22
2010-01-21
2010-01-20
Run Code Online (Sandbox Code Playgroud)

关于绩效的说明

这里测试,性能出奇的好:上面的查询需要0.0009秒.

如果我们扩展子查询以生成约.100,000个数字(因此大约274年的日期),它运行0.0458秒.

顺便提一下,这是一种非常便携的技术,可以对大多数数据库进行微调.

返回1,000天的SQL小提琴示例

  • 为我见过的最超现实的查询之一+1. (145认同)
  • 很高兴看到问题的答案,而不是无休止的评论如何不能或不应该这样做.大多数事情都可以完成,"应该"只在上下文中有意义,每个人都有所不同.这个答案对我有所帮助,尽管我很清楚在大多数情况下都有更好的方法. (22认同)
  • *为什么不只是指定日期并用它来完成* - 因为上面的方法允许你创建任意大的数字集(和日期),不需要创建表,这将是你建议的方式硬编码的痛苦.显然,对于5个日期来说,它是过度的; 但即便如此,如果您要加入一个您不知道提前日期的表格,而只是潜在的最小值和最大值,这是有意义的. (7认同)
  • 那些无法让这个查询起作用的人:请自己打脸,然后重新阅读OP关于此查询生成1000个日期的评论.由于2010年超过1000天,您需要相应地调整查询. (7认同)
  • 如果你将`UNION`更改为`UNION ALL`,你会看到更好的性能 - 它浪费时间检查重复项以删除不存在的重复项.虽然它过于复杂的IMO - 如果您要使用UNION构建结果集,为什么不直接指定日期并完成它? (6认同)
  • 仅使用DATETIME函数代替您已经创建的UNION语句是"痛苦的"吗?它*减轻了对你必须添加*的逻辑的需求.因此 - 您对查询过于复杂*.无论哪种方式,UNION语句都不可扩展 - 指定日期或数字,谁想要更新它以容纳20或30个日期? (2认同)
  • +1这是我用来生成日期列表的方法,或者更常见的是,在指定的时间间隔(例如15分钟)生成日期时间值列表.这种方法也适用于SQL Server.在Oracle中,我们可以使用(更简洁的)SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 1000来获取整数值列表. (2认同)
  • @xjshiya 所写的查询从当前日期(`curdate()`)开始。将“curdate()”更改为“2014-01-01”,它将从该日期返回。 (2认同)
  • 这会生成导致 CURDATE() 的历史日期。要从 CURDATE() 开始生成未来的日期,请更改为 SELECT CURDATE() + INTERVAL()。 (2认同)

Sté*_*ane 31

以下是使用视图的另一种变体:

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;
Run Code Online (Sandbox Code Playgroud)

然后你可以简单地做(看看它有多优雅?):

SELECT
  date
FROM
  dates
WHERE
  date BETWEEN '2010-01-20' AND '2010-01-24'
ORDER BY
  date
Run Code Online (Sandbox Code Playgroud)

更新

值得注意的是,您只能从当前日期开始生成过去的日期.如果要生成任何类型的日期范围(过去,将来和之间),则必须使用此视图:

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;
Run Code Online (Sandbox Code Playgroud)

  • 好的电话@ user927258.这是因为上面提到的第一个视图`dates`计算从当前日期开始的日期,这就是为什么你将无法检索将来设置的日期.来自@RedFilter的回答也有同样的设计缺陷.我在答案中添加了一个解决方法. (3认同)
  • 效果很好 (2认同)

Dmi*_*sev 21

接受的答案对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)
Run Code Online (Sandbox Code Playgroud)


Jos*_*hua 13

使用递归公用表表达式(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)
Run Code Online (Sandbox Code Playgroud)

在Microsoft SQL Server 2005上,生成所有可能日期的CTE列表时间为1:08.生成一百年不到一秒钟.


小智 7

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
Run Code Online (Sandbox Code Playgroud)

产量

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
Run Code Online (Sandbox Code Playgroud)

  • 如果我只是向下滚动了一下......叹息.无论如何,谢谢.我添加了一个CAST(<expression> AS DATE)来删除我的版本上的时间.也用在GETDATE() - 365和GETDATE()之间的a.Date ...如果你今天运行你的查询,如果你没有注意到WHERE = P中的日期,它将不会给出任何行 (2认同)

OMG*_*ies 5

在没有循环/游标的情况下执行此操作的老式解决方案是创建一个NUMBERS表,该表具有单个 Integer 列,其值从 1 开始。

CREATE TABLE  `example`.`numbers` (
  `id` int(10) unsigned NOT NULL auto_increment,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Run Code Online (Sandbox Code Playgroud)

您需要在表中填充足够的记录以满足您的需求:

INSERT INTO NUMBERS (id) VALUES (NULL);
Run Code Online (Sandbox Code Playgroud)

获得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'
Run Code Online (Sandbox Code Playgroud)

绝对的低技术解决方案是:

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
Run Code Online (Sandbox Code Playgroud)

你会用它做什么?


生成日期或数字列表以便左连接。您这样做是为了查看数据中存在间隙的位置,因为您左连接到序列数据列表 - 空值将使存在间隙的位置变得明显。