generate days from date range

后端 未结 29 2408
渐次进展
渐次进展 2020-11-21 05:19

I would like to run a query like

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

And return data like:

相关标签:
29条回答
  • 2020-11-21 06:10

    A more generic answer that works in AWS MySQL.

    select datetable.Date
    from (
        select date_format(affffdate(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
    
    0 讨论(0)
  • 2020-11-21 06:11

    Generate dates between two date fields

    If you are aware with SQL CTE query, then this solution will helps you to solve your question

    Here is example

    We have dates in one table

    Table Name: “testdate”

    STARTDATE   ENDDATE
    10/24/2012  10/24/2012
    10/27/2012  10/29/2012
    10/30/2012  10/30/2012
    

    Require Result:

    STARTDATE
    10/24/2012
    10/27/2012
    10/28/2012
    10/29/2012
    10/30/2012
    

    Solution:

    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
    

    Explanation: CTE Recursive query explanation

    • First part of query:

      SELECT DISTINCT convert(varchar(10), StartTime, 101) AS StartTime, datediff(dd, StartTime, endTime) AS diff FROM dbo.testdate

      Explanation: firstcolumn is “startdate”, second column is difference of start and end date in days and it will be consider as “diff” column

    • Second part of query:

      UNION ALL SELECT StartTime, diff-1 AS diff FROM CTE WHERE diff<>0

      Explanation: Union all will inherit result of above query until result goes null, So “StartTime” result is inherit from generated CTE query, and from diff, decrease - 1, so its looks like 3, 2, and 1 until 0

    For example

    STARTDATE   DIFF
    10/24/2012  0
    10/27/2012  0
    10/27/2012  1
    10/27/2012  2
    10/30/2012  0
    

    Result Specification

    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
    
    • 3rd Part of Query

      SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime FROM CTE

      It will add day “diff” in “startdate” so result should be as below

    Result

    STARTDATE
    10/24/2012
    10/27/2012
    10/28/2012
    10/29/2012
    10/30/2012
    
    0 讨论(0)
  • 2020-11-21 06:12

    You'd like to get the a date range.

    In your example you'd like to get the dates between '2010-01-20' and '2010-01-24'

    possible solution:

     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'
    

    Explanation

    MySQL has a date_add function so

    select date_add('2010-01-20', interval 1 day)
    

    will give you

    2010-01-21
    

    The datediff function would let you know often you'd have to repeat this

    select datediff('2010-01-24', '2010-01-20')
    

    which returns

     4
    

    Getting a list of dates in a date range boils down to creating a sequence of integer numbers see generate an integer sequence in MySQL

    The most upvoted answer here has taken a similar approach as https://stackoverflow.com/a/2652051/1497139 as a basis:

    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
    

    which will result in

    row
    1.0
    2.0
    3.0
    4.0
    

    The rows can now be used to create a list of dates from the given start date. To include the start date we start with row -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'
    
    0 讨论(0)
  • 2020-11-21 06:14

    Procedure + temporary table:

    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
    
    0 讨论(0)
  • 2020-11-21 06:16

    The old school solution for doing this without a loop/cursor is to create a NUMBERS table, which has a single Integer column with values starting at 1.

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

    You need to populate the table with enough records to cover your needs:

    INSERT INTO NUMBERS (id) VALUES (NULL);
    

    Once you have the NUMBERS table, you can use:

    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'
    

    The absolute low-tech solution would be:

    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
    

    What would you use it for?


    To generate lists of dates or numbers in order to LEFT JOIN on to. You would to this in order to see where there are gaps in the data, because you are LEFT JOINing onto a list of sequencial data - null values will make it obvious where gaps exist.

    0 讨论(0)
  • 2020-11-21 06:17

    thx Pentium10 - you made me join stackoverflow :) - this is my porting to msaccess - think it'll work on any version:

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

    referenced MSysObjects just 'cause access need a table countin' at least 1 record, in a from clause - any table with at least 1 record would do.

    0 讨论(0)
提交回复
热议问题