【问题标题】:Get a list of dates between two dates获取两个日期之间的日期列表
【发布时间】:2010-10-05 07:57:49
【问题描述】:

使用标准 mysql 函数是否可以编写一个查询,该查询将返回两个日期之间的天数列表。

例如,给定 2009-01-01 和 2009-01-13 它将返回一个包含值的单列表:

 2009-01-01 
 2009-01-02 
 2009-01-03
 2009-01-04 
 2009-01-05
 2009-01-06
 2009-01-07
 2009-01-08 
 2009-01-09
 2009-01-10
 2009-01-11
 2009-01-12
 2009-01-13

编辑:看来我还不清楚。我想生成这个列表。我有存储在数据库中的值(按日期时间),但希望它们在左外连接上聚合到上面的日期列表中(我预计某些连接右侧的 null 几天并将处理这个)。

【问题讨论】:

标签: mysql sql date gaps-and-islands


【解决方案1】:

我将使用此存储过程将您需要的时间间隔生成到名为 time_intervals 的临时表中,然后将您的数据表与临时 time_intervals 表连接并聚合。

该过程可以生成您在其中指定的所有不同类型的间隔:

call make_intervals('2009-01-01 00:00:00','2009-01-10 00:00:00',1,'DAY')
.
select * from time_intervals  
.
interval_start      interval_end        
------------------- ------------------- 
2009-01-01 00:00:00 2009-01-01 23:59:59 
2009-01-02 00:00:00 2009-01-02 23:59:59 
2009-01-03 00:00:00 2009-01-03 23:59:59 
2009-01-04 00:00:00 2009-01-04 23:59:59 
2009-01-05 00:00:00 2009-01-05 23:59:59 
2009-01-06 00:00:00 2009-01-06 23:59:59 
2009-01-07 00:00:00 2009-01-07 23:59:59 
2009-01-08 00:00:00 2009-01-08 23:59:59 
2009-01-09 00:00:00 2009-01-09 23:59:59 
.
call make_intervals('2009-01-01 00:00:00','2009-01-01 02:00:00',10,'MINUTE')
. 
select * from time_intervals
.  
interval_start      interval_end        
------------------- ------------------- 
2009-01-01 00:00:00 2009-01-01 00:09:59 
2009-01-01 00:10:00 2009-01-01 00:19:59 
2009-01-01 00:20:00 2009-01-01 00:29:59 
2009-01-01 00:30:00 2009-01-01 00:39:59 
2009-01-01 00:40:00 2009-01-01 00:49:59 
2009-01-01 00:50:00 2009-01-01 00:59:59 
2009-01-01 01:00:00 2009-01-01 01:09:59 
2009-01-01 01:10:00 2009-01-01 01:19:59 
2009-01-01 01:20:00 2009-01-01 01:29:59 
2009-01-01 01:30:00 2009-01-01 01:39:59 
2009-01-01 01:40:00 2009-01-01 01:49:59 
2009-01-01 01:50:00 2009-01-01 01:59:59 
.
I specified an interval_start and interval_end so you can aggregate the 
data timestamps with a "between interval_start and interval_end" type of JOIN.
.
Code for the proc:
.
-- drop procedure make_intervals
.
CREATE PROCEDURE make_intervals(startdate timestamp, enddate timestamp, intval integer, unitval varchar(10))
BEGIN
-- *************************************************************************
-- Procedure: make_intervals()
--    Author: Ron Savage
--      Date: 02/03/2009
--
-- Description:
-- This procedure creates a temporary table named time_intervals with the
-- interval_start and interval_end fields specifed from the startdate and
-- enddate arguments, at intervals of intval (unitval) size.
-- *************************************************************************
   declare thisDate timestamp;
   declare nextDate timestamp;
   set thisDate = startdate;

   -- *************************************************************************
   -- Drop / create the temp table
   -- *************************************************************************
   drop temporary table if exists time_intervals;
   create temporary table if not exists time_intervals
      (
      interval_start timestamp,
      interval_end timestamp
      );

   -- *************************************************************************
   -- Loop through the startdate adding each intval interval until enddate
   -- *************************************************************************
   repeat
      select
         case unitval
            when 'MICROSECOND' then timestampadd(MICROSECOND, intval, thisDate)
            when 'SECOND'      then timestampadd(SECOND, intval, thisDate)
            when 'MINUTE'      then timestampadd(MINUTE, intval, thisDate)
            when 'HOUR'        then timestampadd(HOUR, intval, thisDate)
            when 'DAY'         then timestampadd(DAY, intval, thisDate)
            when 'WEEK'        then timestampadd(WEEK, intval, thisDate)
            when 'MONTH'       then timestampadd(MONTH, intval, thisDate)
            when 'QUARTER'     then timestampadd(QUARTER, intval, thisDate)
            when 'YEAR'        then timestampadd(YEAR, intval, thisDate)
         end into nextDate;

      insert into time_intervals select thisDate, timestampadd(MICROSECOND, -1, nextDate);
      set thisDate = nextDate;
   until thisDate >= enddate
   end repeat;

 END;

this post 底部的类似示例数据场景,我在其中为 SQL Server 构建了类似的函数。

【讨论】:

  • 我个人希望得到类似于 PostgreSQL 中生成的序列的东西。
  • 如果您只生成一次数据,您甚至可以使用永久表格并使用此脚本来填充缺失的日期。
  • 我必须在创建过程语句之前更改分隔符以使其通过 phpMyAdmin 工作(以避免声明语句的语法错误)[code] DECLARE // [/code]
  • 这个解决方案非常强大和灵活,不幸的是在 mariaDB 上我得到了错误 #1067 - Invalid default value for 'interval_end' when running call make_intervals('2009-01-01 00:00:00 ','2009-01-10 00:00:00',1,'DAY');我在版本 5.7.28
  • @shelbypereira 设置默认值:interval_start timestamp NULLinterval_end timestamp NULL
【解决方案2】:

对于 MSSQL,您可以使用它。非常快。

您可以将其包装在表值函数或存储过程中,并将开始和结束日期解析为变量。

DECLARE @startDate DATETIME
DECLARE @endDate DATETIME

SET @startDate = '2011-01-01'
SET @endDate = '2011-01-31';

WITH dates(Date) AS 
(
    SELECT @startdate as Date
    UNION ALL
    SELECT DATEADD(d,1,[Date])
    FROM dates 
    WHERE DATE < @enddate
)

SELECT Date
FROM dates
OPTION (MAXRECURSION 0)
GO

编辑 2021/01(V 博士): 我喜欢这个解决方案并使它适用于 mySQL V8。这是代码,将其包装成一个过程:

DELIMITER //

CREATE PROCEDURE dates_between (IN from_date DATETIME,
                               IN to_date DATETIME) BEGIN
    WITH RECURSIVE dates(Date) AS
    (
        SELECT from_date as Date
        UNION ALL
        SELECT DATE_ADD(Date, INTERVAL 1 day) FROM dates WHERE Date < to_date
    )
    SELECT DATE(Date) FROM dates;
END//

DELIMITER ;

【讨论】:

  • 这里的OPTION(MAXRECURSION 0)有什么用?
【解决方案3】:

你可以像这样使用 MySQL 的user variables

SET @num = -1;
SELECT DATE_ADD( '2009-01-01', interval @num := @num+1 day) AS date_sequence, 
your_table.* FROM your_table
WHERE your_table.other_column IS NOT NULL
HAVING DATE_ADD('2009-01-01', interval @num day) <= '2009-01-13'

@num 是 -1,因为您在第一次使用它时添加了它。此外,您不能使用“HAVING date_sequence”,因为这会使用户变量每行增加两次。

【讨论】:

    【解决方案4】:

    我们在 BIRT 报告中遇到了类似的问题,因为我们想报告那些没有数据的日子。由于这些日期没有条目,对我们来说最简单的解决方案是创建一个简单的表来存储所有日期并使用它来获取范围或连接以获取该日期的零值。

    我们有一项每月运行的工作,以确保该表在 5 年后填充。该表是这样创建的:

    create table all_dates (
        dt date primary key
    );
    

    毫无疑问,使用不同的 DBMS 可以通过一些神奇而棘手的方法来做到这一点,但我们总是选择最简单的解决方案。表的存储需求很小,它使查询变得更加简单和可移植。从性能的角度来看,这种解决方案几乎总是更好,因为它不需要对数据进行每行计算。

    另一个选项(我们之前使用过)是确保表格中每个日期都有一个条目。我们定期扫描表格并为不存在的日期和/或时间添加零条目。在您的情况下,这可能不是一个选项,这取决于存储的数据。

    如果您真的认为填充all_dates 表很麻烦,那么可以使用存储过程,它会返回包含这些日期的数据集。这几乎肯定会更慢,因为您必须在每次调用它时计算范围,而不是从表中提取预先计算的数据。

    但是,老实说,您可以将表填充 1000 年而不会出现任何严重的数据存储问题 - 365,000 个 16 字节(例如)日期加上复制日期的索引加上 20% 的安全开销,我会粗略估计大约 14M [365,000 * 16 * 2 * 1.2 = 14,016,000 字节]),在事物方案中是一个很小的表。

    【讨论】:

      【解决方案5】:

      this 的答案中借用一个想法,您可以设置一个包含 0 到 9 的表格,并使用它来生成您的日期列表。

      CREATE TABLE num (i int);
      INSERT INTO num (i) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
      
      select adddate('2009-01-01', numlist.id) as `date` from
      (SELECT n1.i + n10.i*10 + n100.i*100 AS id
         FROM num n1 cross join num as n10 cross join num as n100) as numlist
      where adddate('2009-01-01', numlist.id) <= '2009-01-13';
      

      这将允许您生成最多 1000 个日期的列表。如果需要更大,可以在内部查询中添加另一个交叉连接。

      【讨论】:

      • 这个解决方案效果更好。但它与 UNIX_TIMESTAMP() 存在问题 - 它给出的结果类似于 1231185600.000000;小数点后的毫秒部分;而-SELECT UNIX_TIMESTAMP(ADDDATE('2009-01-01', 0)) AS date;不会导致该部分。
      【解决方案6】:

      在 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”之间的日期表。

      【讨论】:

      • "select '2019-01-01' as dt" 这个选择器是什么?是否可以只选择一个 from 和 to 字段?
      • 一个不错的解决方案,但请记住,它可能不适用于较大的日期范围,因为类似于我们过去所说的 StackOverflow 错误:) 每行(即日期)添加一个新函数调用调用堆栈。默认限制为 1000。如果您认为值得冒这种方法可能带来的风险,可以通过修改 cte_max_recursion_depth 来增加它。
      【解决方案7】:

      对于 Access(或任何 SQL 语言)

      1. 创建一个有 2 个字段的表,我们称此表为 tempRunDates
        --字段fromDatetoDate
        --然后只插入1条记录,有开始日期和结束日期。

      2. 创建另一个表:Time_Day_Ref
        --将日期列表(在excel中制作列表很容易)导入此表。
        --在我的例子中,字段名称是Greg_Dt,表示公历日期
        --我从 2009 年 1 月 1 日到 2020 年 1 月 1 日列出了我的清单。

      3. 运行查询:

        SELECT Time_Day_Ref.GREG_DT
        FROM tempRunDates, Time_Day_Ref
        WHERE Time_Day_Ref.greg_dt>=tempRunDates.fromDate And greg_dt<=tempRunDates.toDate;
        

      简单!

      【讨论】:

        【解决方案8】:

        通常人们会使用一个辅助数字表,您通常会为此目的保留一些表格:

        SELECT *
        FROM (
            SELECT DATEADD(d, number - 1, '2009-01-01') AS dt
            FROM Numbers
            WHERE number BETWEEN 1 AND DATEDIFF(d, '2009-01-01', '2009-01-13') + 1
        ) AS DateRange
        LEFT JOIN YourStuff
            ON DateRange.dt = YourStuff.DateColumn
        

        我已经看到了表值函数等的变化。

        您还可以保留一份永久的日期列表。我们的数据仓库中有这些数据以及一天中的时间列表。

        【讨论】:

          【解决方案9】:
          CREATE FUNCTION [dbo].[_DATES]
          (
              @startDate DATETIME,
              @endDate DATETIME
          )
          RETURNS 
          @DATES TABLE(
              DATE1 DATETIME
          )
          AS
          BEGIN
              WHILE @startDate <= @endDate
              BEGIN 
                  INSERT INTO @DATES (DATE1)
                      SELECT @startDate   
              SELECT @startDate = DATEADD(d,1,@startDate) 
              END
          RETURN
          END
          

          【讨论】:

            【解决方案10】:

            我们在 HRMS 系统中使用了它,您会发现它很有用

            SELECT CAST(DAYNAME(daydate) as CHAR) as dayname,daydate
                FROM
                (select CAST((date_add('20110101', interval H.i*100 + T.i*10 + U.i day) )as DATE) as daydate
                  from erp_integers as H
                cross
                  join erp_integers as T
                cross
                  join erp_integers as U
                 where date_add('20110101', interval H.i*100 + T.i*10 + U.i day ) <= '20110228'
                order
                    by daydate ASC
                    )Days
            

            【讨论】:

              【解决方案11】:

              此解决方案适用于 MySQL 5.0
              创建表 - mytable.
              架构并不重要。重要的是其中的行数。
              因此,您可以只保留一列 INT 类型的 10 行列,值从 1 到 10。

              SQL:

              set @tempDate=date('2011-07-01') - interval 1 day;
              select
              date(@tempDate := (date(@tempDate) + interval 1 day)) as theDate
              from mytable x,mytable y
              group by theDate
              having theDate <= '2011-07-31';
              

              限制: 上述查询返回的最大日期数为
              (rows in mytable)*(rows in mytable) = 10*10 = 100.

              您可以通过更改 sql 中的表单部分来增加此范围:
              来自 mytable x,mytable y, mytable z
              所以,范围是10*10*10 =1000等等。

              【讨论】:

                【解决方案12】:

                你可以用这个

                SELECT CAST(cal.date_list AS DATE) day_year
                FROM (
                  SELECT SUBDATE('2019-01-01', INTERVAL 1 YEAR) + INTERVAL xc DAY AS date_list
                  FROM (
                        SELECT @xi:=@xi+1 as xc from
                        (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc1,
                        (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc2,
                        (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc3,
                        (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc4,
                        (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc5,
                        (SELECT @xi:=-1) xc0
                    ) xxc1
                ) cal
                WHERE cal.date_list BETWEEN '2019-01-01' AND '2019-12-31'
                ORDER BY cal.date_list DESC;
                

                【讨论】:

                • 嗨@Dixon,谢谢这个!这个非常适合我的目的;相同的结果,但不使用函数、存储过程或 CTE。
                • 我们为什么要做(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xcn 谁能解释一下?
                【解决方案13】:

                没有临时表 |无程序 |没有功能

                下面的示例显示了一个月的日期范围 - 从同一个月的第 1 天到最后一天。您可以通过使用@e `INTERVAL` 值来更改日期范围。

                SET @s := CAST('2021-12-01' AS DATE); -- start at 2021-12-01
                SET @e := @s + INTERVAL 1 MONTH - INTERVAL 1 DAY; -- ends at 2021-12-31
                
                WITH RECURSIVE d_range AS
                (
                       SELECT @s AS gdate -- generated dates
                       UNION ALL
                       SELECT gdate + INTERVAL 1 day
                       FROM   d_range
                       WHERE  gdate < @e
                )
                SELECT *
                FROM   d_range;
                

                结果:

                | gdate       |
                +------------+
                | 2021-12-01 |
                | 2021-12-02 |
                | 2021-12-03 |
                | 2021-12-04 |
                | 2021-12-05 |
                | 2021-12-06 |
                | 2021-12-07 |
                | 2021-12-08 |
                | 2021-12-09 |
                | 2021-12-10 |
                | 2021-12-11 |
                | 2021-12-12 |
                | 2021-12-13 |
                | 2021-12-14 |
                | 2021-12-15 |
                | 2021-12-16 |
                | 2021-12-17 |
                | 2021-12-18 |
                | 2021-12-19 |
                | 2021-12-20 |
                | 2021-12-21 |
                | 2021-12-22 |
                | 2021-12-23 |
                | 2021-12-24 |
                | 2021-12-25 |
                | 2021-12-26 |
                | 2021-12-27 |
                | 2021-12-28 |
                | 2021-12-29 |
                | 2021-12-30 |
                | 2021-12-31 |
                +------------+
                -> 31 rows in set (0.037 sec)
                

                注意。此查询在此处使用 RECURSION,因为它在选择较长的日期范围时非常慢 31 rows in set (0.037 sec) 它会变得更慢。

                【讨论】:

                  【解决方案14】:

                  创建一个带有两个参数 a_begin 和 a_end 的存储过程。 在其中创建一个名为 t 的临时表,声明一个变量 d,将 a_begin 分配给 d,然后运行 ​​WHILE 循环 INSERTing d 到 t 并调用 ADDDATE 函数来增加值 d。最后SELECT * FROM t

                  【讨论】:

                  • 实际上我相信永久表会更好地提高执行速度(存储要求最低)。将日期预先计算到表格中并在需要时提取比每次需要时都创建范围要快。
                  • @Pax,这取决于获得的速度提升是否值得表维护。例如,如果报告生成需要 3 秒,而运行 WHILE 循环来生成日期需要 0.001 秒,我认为性能提升是可以忽略的。 Knuth:过早的优化是万恶之源。
                  • @eed3si9n,过早的优化,是的,不是所有的优化:-) 如果你的例子是正确的,那么是的,我同意你的观点,但应该确定它。如果您按照我的建议在 1000 年后生成表维护(一次性成本),那么表维护就消失了,但是这些秒数会加起来。
                  【解决方案15】:

                  我会使用类似的东西:

                  DECLARE @DATEFROM AS DATETIME
                  DECLARE @DATETO AS DATETIME
                  DECLARE @HOLDER TABLE(DATE DATETIME)
                  
                  SET @DATEFROM = '2010-08-10'
                  SET @DATETO = '2010-09-11'
                  
                  INSERT INTO
                      @HOLDER
                          (DATE)
                  VALUES
                      (@DATEFROM)
                  
                  WHILE @DATEFROM < @DATETO
                  BEGIN
                  
                      SELECT @DATEFROM = DATEADD(D, 1, @DATEFROM)
                      INSERT 
                      INTO
                          @HOLDER
                              (DATE)
                      VALUES
                          (@DATEFROM)
                  END
                  
                  SELECT 
                      DATE
                  FROM
                      @HOLDER
                  

                  然后@HOLDER 变量表保存这两个日期之间按天递增的所有日期,随时可以加入。

                  【讨论】:

                  • 您好,我正在尝试使用代码并将输出插入到我定义的表中那请?注意:在这里使用 SQL Server 谢谢 :)
                  【解决方案16】:

                  我已经为此奋斗了很长一段时间。由于这是我在搜索解决方案时在 Google 上的第一次点击,所以让我发布一下我到目前为止所获得的信息。

                  SET @d := '2011-09-01';
                  SELECT @d AS d, cast( @d := DATE_ADD( @d , INTERVAL 1 DAY ) AS DATE ) AS new_d
                    FROM [yourTable]
                    WHERE @d <= '2012-05-01';
                  

                  [yourTable] 替换为数据库中的表。诀窍是您选择的表中的行数必须 >= 您要返回的日期数。我尝试使用表格占位符 DUAL,但它只会返回一行。

                  【讨论】:

                  • 有趣的方法。但是..这本质上不是 Vihang 上面建议的吗?)?
                  【解决方案17】:
                  select * from table_name where col_Date between '2011/02/25' AND DATEADD(s,-1,DATEADD(d,1,'2011/02/27'))
                  

                  在这里,首先在当前的 endDate 上加一天,即 2011-02-28 00:00:00,然后减去一秒,使结束日期为 2011-02-27 23:59:59。通过这样做,您可以获得给定间隔之间的所有日期。

                  输出:
                  2011/02/25
                  2011/02/26
                  2011/02/27

                  【讨论】:

                    【解决方案18】:
                    DELIMITER $$  
                    CREATE PROCEDURE popula_calendario_controle()
                       BEGIN
                          DECLARE a INT Default 0;
                          DECLARE first_day_of_year DATE;
                          set first_day_of_year = CONCAT(DATE_FORMAT(curdate(),'%Y'),'-01-01');
                          one_by_one: LOOP
                             IF dayofweek(adddate(first_day_of_year,a)) <> 1 THEN
                                INSERT INTO calendario.controle VALUES(null,150,adddate(first_day_of_year,a),adddate(first_day_of_year,a),1);
                             END IF;
                             SET a=a+1;
                             IF a=365 THEN
                                LEAVE one_by_one;
                             END IF;
                          END LOOP one_by_one;
                    END $$
                    

                    此过程将插入从年初到现在的所有日期,只需替换“开始”和“结束”的日期,就可以开始了!

                    【讨论】:

                    • IF a=365 THEN受闰年影响如何?
                    • 另外,第 9 行的数字 150 有什么意义?这段代码是一个没有太多实际解释的“pat answer”。
                    【解决方案19】:

                    我需要一个包含两个日期之间所有月份的列表来进行统计。这两个日期是订阅的开始日期和结束日期。 所以列表显示了所有月份和每月的订阅量。

                    MYSQL

                    CREATE PROCEDURE `get_amount_subscription_per_month`()
                    BEGIN
                       -- Select the ultimate start and enddate from subscribers
                       select @startdate := min(DATE_FORMAT(a.startdate, "%Y-%m-01")), 
                              @enddate := max(DATE_FORMAT(a.enddate, "%Y-%m-01")) + interval 1 MONTH
                       from subscription a;
                    
                       -- Tmp table with all months (dates), you can always format them with DATE_FORMAT) 
                       DROP TABLE IF EXISTS tmp_months;
                       create temporary table tmp_months (
                          year_month date,
                          PRIMARY KEY (year_month)
                       );
                    
                    
                       set @tempDate=@startdate;  #- interval 1 MONTH;
                    
                       -- Insert every month in tmp table
                       WHILE @tempDate <= @enddate DO
                         insert into tmp_months (year_month) values (@tempDate);
                         set @tempDate = (date(@tempDate) + interval 1 MONTH);
                       END WHILE;
                    
                       -- All months
                       select year_month from tmp_months;
                    
                       -- If you want the amount of subscription per month else leave it out
                       select mnd.year_month, sum(subscription.amount) as subscription_amount
                       from tmp_months mnd
                       LEFT JOIN subscription ON mnd.year_month >= DATE_FORMAT(subscription.startdate, "%Y-%m-01") and mnd.year_month <= DATE_FORMAT(subscription.enddate, "%Y-%m-01")
                       GROUP BY mnd.year_month;
                    
                     END
                    

                    【讨论】:

                      【解决方案20】:

                      没有函数,没有过程

                      SET @s := '2020-01-01';
                      SET @e := @s + INTERVAL 1 YEAR - INTERVAL 1 DAY; -- set end date to select
                      
                      SELECT CAST((DATE(@s)+INTERVAL (H+T+U) DAY) AS DATE) d -- generate a list of 400 days starting from @s date so @e can not be more then @s + 
                      FROM ( SELECT 0 H
                          UNION ALL SELECT 100 UNION ALL SELECT 200 UNION ALL SELECT 300
                        ) H CROSS JOIN ( SELECT 0 T
                          UNION ALL SELECT  10 UNION ALL SELECT  20 UNION ALL SELECT  30
                          UNION ALL SELECT  40 UNION ALL SELECT  50 UNION ALL SELECT  60
                          UNION ALL SELECT  70 UNION ALL SELECT  80 UNION ALL SELECT  90
                        ) T CROSS JOIN ( SELECT 0 U
                          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
                        ) U
                       -- generated lenght of date list can be calculated as fallow 1 * H(3 + 1) * T(9 + 1) * U(9 + 1) = 400
                       -- it is crucial to preserve the numbering convention as 1, 2 ... 20, 30..., 100, 200, ... to retrieve chronological date selection
                       -- add more UNION 400, 500, 600, ... H(6 + 1) if you want to select from more than 700 days!
                      WHERE
                        (DATE(@s+INTERVAL (H+T+U) DAY)) <= DATE((@e))
                      ORDER BY d;
                      

                      UNIONS 定义可供选择的可用日期的长度和范围。因此,请务必确保记录足够多。

                      SELECT 1 * (3 + 1) * (9 + 1) * (9 + 1); -- 400 days starting from from @s
                      

                      基于此article
                      经过测试并按预期工作

                      【讨论】:

                        【解决方案21】:

                        改进@brad 答案,我使用了带有光标的 CTE:

                        DECLARE cursorPeriod CURSOR FOR SELECT * FROM (
                           WITH RECURSIVE period as
                                (
                                     SELECT '2019-01-01' as dt
                                     UNION
                                     SELECT DATE_ADD(period.dt, INTERVAL 1 DAY) FROM period WHERE DATE_ADD(period.dt, INTERVAL 1 DAY) <= '2019-04-30'
                                )
                           SELECT DATE(dt) FROM period ORDER BY dt ) AS derived_table;
                        
                        DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
                        

                        【讨论】:

                          【解决方案22】:

                          我正在使用 Server version: 5.7.11-log MySQL Community Server (GPL)

                          现在我们将用一种简单的方式解决这个问题。

                          我创建了一个名为 "datetable"

                          的表
                          mysql> describe datetable;
                          +---------+---------+------+-----+---------+-------+
                          | Field   | Type    | Null | Key | Default | Extra |
                          +---------+---------+------+-----+---------+-------+
                          | colid   | int(11) | NO   | PRI | NULL    |       |
                          | coldate | date    | YES  |     | NULL    |       |
                          +---------+---------+------+-----+---------+-------+
                          2 rows in set (0.00 sec)
                          

                          现在,我们将看到其中插入的记录。

                          mysql> select * from datetable;
                          +-------+------------+
                          | colid | coldate    |
                          +-------+------------+
                          |   101 | 2015-01-01 |
                          |   102 | 2015-05-01 |
                          |   103 | 2016-01-01 |
                          +-------+------------+
                          3 rows in set (0.00 sec)
                          

                          这里我们的查询是获取两个日期内的记录,而不是那些日期。

                          mysql> select * from datetable where coldate > '2015-01-01' and coldate < '2016-01-01';
                          +-------+------------+
                          | colid | coldate    |
                          +-------+------------+
                          |   102 | 2015-05-01 |
                          +-------+------------+
                          1 row in set (0.00 sec)
                          

                          希望这会对很多人有所帮助。

                          【讨论】:

                          • 否决,没有任何理由,这个 SQL 很好,并且在工作场景中。
                          • 它没有回答问题
                          猜你喜欢
                          • 2018-09-17
                          • 1970-01-01
                          • 2018-01-11
                          • 1970-01-01
                          • 1970-01-01
                          • 2010-11-25
                          • 1970-01-01
                          • 1970-01-01
                          • 2017-06-12
                          相关资源
                          最近更新 更多