【问题标题】:generate days from date range从日期范围生成天数
【发布时间】:2011-01-10 13:35:46
【问题描述】:

我想运行类似的查询

select ... as days where `date` is between '2010-01-20' and '2010-01-24'

并返回如下数据:

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

【问题讨论】:

  • 这个问题没有其他问题。上题就是问题,掌握SQL课程。
  • 您是否只需要基于选定日期范围的日期数组?
  • 我在想一个用法,给你找个问题……如果你得到一个任务,在你的表中填写一些缺失的记录。你必须每天运行一个查询,我在想insert into table select ... as days date between '' and ''
  • 它的一个使用示例是生成统计数据,并为您没有数据的日期添加一行。如果您正在执行某种分组方式,则可以更快地实际生成 SQL 中的所有信息并将其添加到您需要的任何格式中,而不是将数据按原样转储到您的语言中,然后开始循环并添加您的清空。
  • @Nanne 这正是我保存这个问题的原因。我需要将上述内容左连接到某些日期可能不存在的数据中。

标签: sql mysql datetime between


【解决方案1】:

此解决方案不使用循环、过程或临时表。子查询会生成最近 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

性能说明

测试here,性能出奇的好:上面的查询耗时 0.0009 秒。

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

顺便说一句,这是一种非常便携的技术,只需稍作调整即可与大多数数据库一起使用。

SQL Fiddle example returning 1,000 days

【讨论】:

  • 如果您将UNION 更改为UNION ALL,您会看到更好的性能 - 检查重复项以删除不存在的内容是浪费时间。不过 IMO 过于复杂了 - 如果您要使用 UNION 构建结果集,为什么不直接指定日期并完成它?
  • 为什么不只指定日期并完成它 - 因为上述方法允许您创建任意大的数字(和日期)集,而不需要创建表,即以您建议的方式进行硬编码会很痛苦。显然,对于 5 次约会来说,这太过分了。但即便如此,如果您要加入的表格中您事先不知道日期,而只知道潜在的最小值和最大值,那还是有意义的。
  • 仅使用 DATETIME 函数代替您已经创建的 UNION 语句是“痛苦的”吗?它减轻了对您必须添加的逻辑的任何需求。因此 - 您对查询过于复杂。无论哪种方式,UNION 语句都不可扩展 - 指定日期或数字,谁想要更新它以适应 20 或 30 个日期?
  • 很高兴看到这个问题的答案,而不是无尽的 cmets 如何不能或不应该做。大多数事情都可以做到,而“应该”只在上下文中有意义,这因人而异。这个答案对我有帮助,尽管我很清楚在大多数情况下都有更好的方法。
  • 那些不能让这个查询工作的人:请打自己的脸,然后重新阅读 OP 关于这个查询生成 1000 个日期的评论。由于 2010 年是 1000 多天前,因此您需要相应地调整查询。
【解决方案2】:

这是另一个使用视图的变体:

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;

【讨论】:

  • 这并不适用于所有情况。 SELECT date FROM dates WHERE date BETWEEN '2014-12-01' AND '2014-12-28' ORDER BY date
  • 好电话@user927258。这是因为上面提到的第一个视图dates 计算从当前日期开始的日期,这就是为什么您将无法检索未来设置的日期。 @RedFilter 的回答也存在同样的设计缺陷。不过,我在答案中添加了一种解决方法。
  • 使用某些视图肯定会简化查询,并使其可重用。尽管它们本质上是在做同样的事情,但所有这些 UNION 子句在单个 SQL 语句中看起来很奇怪。
  • 好奇dates 视图中的联合消除是否会带来一些性能提升。
【解决方案3】:

已接受的答案不适用于 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)

【讨论】:

    【解决方案4】:

    使用递归公用表表达式 (CTE),您可以生成日期列表,然后从中进行选择。显然您通常不想创建 300 万个日期,所以这只是说明了可能性。您可以简单地限制 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。产生一百年的时间不到一秒。

    【讨论】:

      【解决方案5】:

      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
      

      【讨论】:

      • 如果我再向下滚动一点... 叹息。无论如何,谢谢你。我添加了一个 CAST( AS DATE) 来删除我版本上的时间。也用于 GETDATE() - 365 AND GETDATE() 之间的 a.Date ...如果您今天运行查询,如果您没有注意到 WHERE =P 中的日期,它将不提供任何行
      【解决方案6】:

      在没有循环/光标的情况下执行此操作的老式解决方案是创建一个 NUMBERS 表,该表有一个值从 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
      

      你会用它做什么?


      生成日期或数字列表以便 LEFT JOIN 到。您将这样做是为了查看数据中的哪些地方存在差距,因为您将左连接到一个顺序数据列表中 - 空值将使存在差距的地方变得明显。

      【讨论】:

      • Oracle 和 MySQL 支持将DUAL 表用作FROM 子句中的替代表。它不存在,从中选择值将返回任何值。这个想法是有替身,因为 SELECT 查询需要一个 FROM 子句指定至少一个表。
      • +1 用于实际创建永久数字表,而不是让 RDBMS 每次需要查询时都构建它。辅助表并不邪恶,伙计们!
      【解决方案7】:

      对于 Access 2010 - 需要多个步骤;我遵循与上面发布的相同模式,但认为我可以帮助 Access 中的某个人。对我来说效果很好,我不必保留种子日期表。

      创建一个名为 DUAL 的表(类似于 Oracle DUAL 表的工作方式)

      • ID(自动编号)
      • DummyColumn(文本)
      • 添加一行值 (1,"DummyRow")

      创建一个名为“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#
      

      【讨论】:

        【解决方案8】:

        程序+临时表:

        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
        

        【讨论】:

          【解决方案9】:

          thx 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 只是因为在 from 子句中“访问需要一个表计数”至少 1 条记录 - 任何具有至少 1 条记录的表都可以。

          【讨论】:

            【解决方案10】:

            在 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 毫秒。

            【讨论】:

              【解决方案11】:

              正如已经给出的许多精彩答案中所述(或至少暗示),一旦你有一组数字可供使用,这个问题就很容易解决。

              注意:以下是 T-SQL,但它只是我对这里和整个 Internet 上已经提到的一般概念的特定实现。将代码转换为您选择的方言应该相对简单。

              如何?考虑这个查询:

              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
              );
              

              【讨论】:

                【解决方案12】:

                试试这个。

                SELECT TO_DATE('20160210','yyyymmdd') - 1 + LEVEL AS start_day 
                from DUAL
                connect by level <= (TO_DATE('20160228','yyyymmdd') + 1) - TO_DATE('20160210','yyyymmdd') ;
                

                【讨论】:

                  【解决方案13】:

                  对于希望将此作为保存视图的任何人(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
                  ...
                  

                  【讨论】:

                    【解决方案14】:

                    您想要获取日期范围。

                    在您的示例中,您希望获取“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
                    

                    获取日期范围内的日期列表归结为创建整数序列,请参阅generate an integer sequence in 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'
                    

                    【讨论】:

                      【解决方案15】:

                      如果您需要几天以上的时间,您需要一张桌子。

                      Create a date range in mysql

                      那么,

                      select from days.day, count(mytable.field) as fields from days left join mytable on day=date where date between x and y;
                      

                      【讨论】:

                      • 为什么要发这个,因为上面的回复不需要表格并提供了解决方案?
                      【解决方案16】:

                      在两个日期字段之间生成日期

                      如果您了解 SQL CTE 查询,那么此解决方案将帮助您解决问题

                      这里是例子

                      我们在一张表中有日期

                      表名:“testdate”

                      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&lt;&gt;0

                        说明:Union all 将继承上述查询的结果,直到结果为空, 所以“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
                      

                      【讨论】:

                        【解决方案17】:

                        比接受的答案短,同样的想法:

                        (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')
                        

                        【讨论】:

                          【解决方案18】:

                          动态生成这些日期是个好主意。但是,我觉得自己在相当大的范围内这样做并不自在,所以我最终得到了以下解决方案:

                          1. 创建了一个表格“DatesNumbers”,其中包含用于日期计算的数字:
                          CREATE TABLE DatesNumbers (
                              i MEDIUMINT NOT NULL,
                              PRIMARY KEY (i)
                          )
                          COMMENT='Used by Dates view'
                          ;
                          
                          1. 使用上述技术填充表格,数字从 -59999 到 40000。这个范围将为我提供从落后 59999 天(~164 年)到提前 40000 天(109 年)的日期:
                          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
                          ;
                          
                          1. 创建了一个视图“日期”:
                          SELECT
                                i,
                                CURRENT_DATE() + INTERVAL i DAY AS Date
                          FROM
                              DatesNumbers
                          

                          就是这样。

                          • (+) 易于阅读的查询
                          • (+) 没有即时数字世代
                          • (+) 给出过去和未来的日期,在 this post 中没有任何 UNION 视图。
                          • (+) 可以使用WHERE i &lt; 0WHERE i &gt; 0 (PK) 过滤“仅在过去”或“仅在未来”日期
                          • (-) 使用了“临时”表和视图

                          【讨论】:

                            【解决方案19】:

                            适用于 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
                            

                            【讨论】:

                              【解决方案20】:

                              好吧..试试这个: 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 语句来完成,但它可能对 MySQL 特定的东西是可行的。
                              再说一次,也许你需要游标:http://dev.mysql.com/doc/refman/5.0/en/cursors.html

                              【讨论】:

                                【解决方案21】:

                                对于 Oracle,我的解决方案是:

                                select trunc(sysdate-dayincrement, 'DD') 
                                  from dual, (select level as dayincrement 
                                                from dual connect by level <= 30)
                                

                                系统日期可以更改为特定日期,级别编号可以更改为更多日期。

                                【讨论】:

                                  【解决方案22】:

                                  如果你想要两个日期之间的日期列表:

                                  create table #dates ([date] smalldatetime)
                                  while @since < @to
                                  begin
                                       insert into #dates(dateadd(day,1,@since))
                                       set @since = dateadd(day,1,@since)
                                  end
                                  select [date] from #dates
                                  

                                  *这里的小提琴:http://sqlfiddle.com/#!6/9eecb/3469

                                  【讨论】:

                                    【解决方案23】:
                                    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)
                                    

                                    【讨论】:

                                      【解决方案24】:
                                      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()
                                          );
                                      

                                      【讨论】:

                                        【解决方案25】:

                                        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
                                        

                                        【讨论】:

                                          【解决方案26】:

                                          改进了工作日和加入自定义假期表 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)
                                          

                                          【讨论】:

                                            【解决方案27】:

                                            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;
                                            

                                            【讨论】:

                                              【解决方案28】:
                                              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'
                                              

                                              【讨论】:

                                                【解决方案29】:

                                                还可以创建一个程序来创建 日历表,其时间图不同于一天。 如果您想要每个季度的表格

                                                例如

                                                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语句

                                                创建一个真实的表

                                                【讨论】:

                                                  猜你喜欢
                                                  • 1970-01-01
                                                  • 1970-01-01
                                                  • 2019-08-25
                                                  • 1970-01-01
                                                  • 1970-01-01
                                                  相关资源
                                                  最近更新 更多