【问题标题】:Generate a range of dates using SQL使用 SQL 生成日期范围
【发布时间】:2010-09-29 22:03:50
【问题描述】:

我有一个带有日期参数的 SQL 查询(如果我要将它放入函数中),我需要在去年的每一天运行它。

如何生成最近 365 天的列表,以便我可以使用直接 SQL 来执行此操作?

显然,生成一个列表 0..364 也可以,因为我总是可以:

SELECT SYSDATE - val FROM (...);

【问题讨论】:

  • 别忘了不是每一年都是一年365天:P
  • 是的,但鉴于每年运行一次,我认为他们可以自己进行调整
  • 直到下一个闰年,他们忘记了在运行之前必须调整流程......
  • 从 Oracle 11.2 开始 Recursive Subquery Factoring(又名递归 WITH)可用于查看示例 here

标签: sql oracle


【解决方案1】:

啊哈哈哈,这是我刚刚想到的一个有趣的方法:

select SYSDATE - ROWNUM
from shipment_weights sw
where ROWNUM < 365;

其中 shipping_weights 是任何大表;

【讨论】:

    【解决方案2】:

    为了好玩,这里有一些代码应该可以在 SQL Server、Oracle 或 MySQL 中运行:

    SELECT current_timestamp - CAST(d1.digit + d2.digit + d3.digit as int)
    FROM 
    (
        SELECT digit
        FROM
        (
            select '1' as digit
            union select '2'
            union select '3'
            union select '4'
            union select '5'
            union select '6'
            union select '7'
            union select '8'
            union select '9'
            union select '0'
        ) digits
    ) d1
    CROSS JOIN
    (
        SELECT digit
        FROM
        (
            select '1' as digit
            union select '2'
            union select '3'
            union select '4'
            union select '5'
            union select '6'
            union select '7'
            union select '8'
            union select '9'
            union select '0'
        ) digits
    ) d2
    CROSS JOIN
    (
        SELECT digit
        FROM
        (
            select '1' as digit
            union select '2'
            union select '3'
            union select '4'
            union select '5'
            union select '6'
            union select '7'
            union select '8'
            union select '9'
            union select '0'
        ) digits
    ) d3
    WHERE CAST(d1.digit + d2.digit + d3.digit as int) < 365
    ORDER BY d1.digit, d2.digit, d3.digit -- order not really needed here
    

    如果你能给我一个跨平台的语法来重复使用数字表,那就加分。

    【讨论】:

    • 在 Oracle 上,您必须为每个“联合选择”添加 FROM DUAL
    【解决方案3】:

    我没有重复使用数字表的答案,但这里有一个代码示例,它至少可以在 SQL Server 中工作,而且速度更快。

    print("code sample");
    
    select  top 366 current_timestamp - row_number() over( order by l.A * r.A) as DateValue
    from (
    select  1 as 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 union
    select  10 union
    select  11 union
    select  12 union
    select  13 union
    select  14 union
    select  15 union
    select  16 union
    select  17 union
    select  18 union
    select  19 union
    select  20 union
    select  21 
    ) l
    cross join (
    select 1 as 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 union
    select 10 union
    select 11 union
    select 12 union
    select 13 union
    select 14 union
    select 15 union
    select 16 union
    select 17 union
    select 18
    ) r
    print("code sample");
    

    【讨论】:

      【解决方案4】:

      Oracle 特定,不依赖于预先存在的大表或数据字典对象的复杂系统视图。

      SELECT c1 from dual
        MODEL DIMENSION BY (1 as rn)  MEASURES (sysdate as c1)
        RULES ITERATE (365) 
        (c1[ITERATION_NUMBER]=SYSDATE-ITERATION_NUMBER)
      order by 1
      

      【讨论】:

        【解决方案5】:

        Oracle 中非常常用的一种方法是这样的:

        select trunc(sysdate)-rn
        from
        (   select rownum rn
            from   dual
            connect by level <= 365)
        /
        

        就个人而言,如果应用程序需要日期列表,那么我只需使用它们创建一个表,或者创建一个包含一系列整数的表,最多可用于一百万个可用于此类的可笑整数的东西。

        【讨论】:

          【解决方案6】:

          无需使用超大表或 ALL_OBJECTS 表:

          SELECT TRUNC (SYSDATE - ROWNUM) dt
            FROM DUAL CONNECT BY ROWNUM < 366
          

          会成功的。

          【讨论】:

          【解决方案7】:

          大约晚了一年半,但为了后代,这里有一个 Teradata 版本:

          SELECT calendar_date 
          FROM SYS_CALENDAR.Calendar
          WHERE SYS_CALENDAR.Calendar.calendar_date between '2010-01-01' (date) and '2010-01-03' (date)
          

          【讨论】:

          • 后人没什么;这帮助我找出了一些难以追踪的 TD sql 命令。为什么你不能像在其他任何地方一样在 TD 中找到带有 sql 命令列表的东西?
          【解决方案8】:

          此查询生成一个日期列表,其中包含未来 4000 天和过去 5000 天的日期(受 http://blogs.x2line.com/al/articles/207.aspx 启发):

          SELECT * FROM (SELECT
              (CONVERT(SMALLDATETIME, CONVERT(CHAR,GETDATE() ,103)) + 4000 -
                          n4.num * 1000 -
                          n3.num * 100 -
                          n2.num * 10 -
                          n1.num) AS Date, 
              year(CONVERT(SMALLDATETIME, CONVERT(CHAR,GETDATE() ,103)) + 4000 -
                          n4.num * 1000 -
                          n3.num * 100 -
                          n2.num * 10 -
                          n1.num) as Year,
              month(CONVERT(SMALLDATETIME, CONVERT(CHAR,GETDATE() ,103)) + 4000 -
                          n4.num * 1000 -
                          n3.num * 100 -
                          n2.num * 10 -
                          n1.num) as Month,
              day(CONVERT(SMALLDATETIME, CONVERT(CHAR,GETDATE() ,103)) + 4000 -
                          n4.num * 1000 -
                          n3.num * 100 -
                          n2.num * 10 -
                          n1.num) as Day
                     FROM (SELECT 0 AS num 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) n1
                         ,(SELECT 0 AS num 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) n2
                         ,(SELECT 0 AS num 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) n3  
                         ,(SELECT 0 AS num 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) n4
                  ) GenCalendar  ORDER BY 1
          

          【讨论】:

            【解决方案9】:
             SELECT (sysdate-365 + (LEVEL -1)) AS DATES
             FROM DUAL connect by level <=( sysdate-(sysdate-365))
            

            如果替换 'from' 和 'to' 日期来代替 sysdate 和 sysdate-365,则输出将是 from 和 to 之间的日期范围。

            【讨论】:

            • 我喜欢你不使用rownum,这在子查询中可能会出现问题。
            【解决方案10】:

            我也有同样的要求——我只是用这个。用户输入他/她希望将日历范围限制到的天数。

              SELECT DAY, offset
                FROM (SELECT to_char(SYSDATE, 'DD-MON-YYYY') AS DAY, 0 AS offset
                        FROM DUAL
                      UNION ALL
                      SELECT to_char(SYSDATE - rownum, 'DD-MON-YYYY'), rownum
                        FROM all_objects d)
                        where offset <= &No_of_days
            

            我将上述结果集用作LEFT OUTER JOIN 中的驾驶视图,以及其他涉及具有日期的表的视图。

            【讨论】:

              【解决方案11】:

              最近我遇到了类似的问题,并通过这个简单的查询解决了它:

              SELECT
                (to_date(:p_to_date,'DD-MM-YYYY') - level + 1) AS day
              FROM
                dual
              CONNECT BY LEVEL <= (to_date(:p_to_date,'DD-MM-YYYY') - to_date(:p_from_date,'DD-MM-YYYY') + 1);
              

              例子

              SELECT
                (to_date('01-05-2015','DD-MM-YYYY') - level + 1) AS day
              FROM
                dual
              CONNECT BY LEVEL <= (to_date('01-05-2015','DD-MM-YYYY') - to_date('01-04-2015','DD-MM-YYYY') + 1);
              

              结果

              01-05-2015 00:00:00
              30-04-2015 00:00:00
              29-04-2015 00:00:00
              28-04-2015 00:00:00
              27-04-2015 00:00:00
              26-04-2015 00:00:00
              25-04-2015 00:00:00
              24-04-2015 00:00:00
              23-04-2015 00:00:00
              22-04-2015 00:00:00
              21-04-2015 00:00:00
              20-04-2015 00:00:00
              19-04-2015 00:00:00
              18-04-2015 00:00:00
              17-04-2015 00:00:00
              16-04-2015 00:00:00
              15-04-2015 00:00:00
              14-04-2015 00:00:00
              13-04-2015 00:00:00
              12-04-2015 00:00:00
              11-04-2015 00:00:00
              10-04-2015 00:00:00
              09-04-2015 00:00:00
              08-04-2015 00:00:00
              07-04-2015 00:00:00
              06-04-2015 00:00:00
              05-04-2015 00:00:00
              04-04-2015 00:00:00
              03-04-2015 00:00:00
              02-04-2015 00:00:00
              01-04-2015 00:00:00
              

              【讨论】:

                【解决方案12】:

                6 个月前的一周

                SELECT (date'2015-08-03' + (LEVEL-1)) AS DATES
                 FROM DUAL 
                 where ROWNUM < 8
                 connect by level <= (sysdate-date'2015-08-03'); 
                

                如果省略 ROWNUM,则只会得到 50 行,与值无关。

                【讨论】:

                  【解决方案13】:

                  1996 年 12 月 31 日至 2020 年 12 月 31 日之间的日期范围

                  SELECT dt, to_char(dt, 'MM/DD/YYYY') as date_name, 
                    EXTRACT(year from dt) as year, 
                    EXTRACT(year from fiscal_dt) as fiscal_year,
                    initcap(to_char(dt, 'MON')) as month,
                    to_char(dt, 'YYYY')        || ' ' || initcap(to_char(dt, 'MON')) as year_month,
                    to_char(fiscal_dt, 'YYYY') || ' ' || initcap(to_char(dt, 'MON')) as fiscal_year_month,
                    EXTRACT(year from dt)*100        + EXTRACT(month from dt) as year_month_id,
                    EXTRACT(year from fiscal_dt)*100 + EXTRACT(month from fiscal_dt) as fiscal_year_month_id,
                    to_char(dt, 'YYYY')        || ' Q' || to_char(dt, 'Q') as quarter,
                    to_char(fiscal_dt, 'YYYY') || ' Q' || to_char(fiscal_dt, 'Q') as fiscal_quarter
                    --, EXTRACT(day from dt) as day_of_month, to_char(dt, 'YYYY-WW') as week_of_year, to_char(dt, 'D') as day_of_week
                    FROM (
                      SELECT dt, add_months(dt, 6) as fiscal_dt --starts July 1st
                      FROM (
                        SELECT TO_DATE('12/31/1996', 'mm/dd/yyyy') + ROWNUM as dt 
                        FROM DUAL CONNECT BY ROWNUM < 366 * 30 --30 years
                      )
                      WHERE dt <= TO_DATE('12/31/2020', 'mm/dd/yyyy')
                    )
                  

                  【讨论】:

                    【解决方案14】:

                    迟到总比没有好。这是我设计的一种方法(在阅读这篇文章后),用于返回日期列表,其中包括:(a) 本月的第 1 天到今天,加上 (b) 过去两个月的所有日期:

                    select (sysdate +1 - rownum) dt 
                    from dual 
                     connect by rownum <= (sysdate - add_months(sysdate - extract(day from sysdate),-2));
                    

                    “-2”是要包括的先前完整月份的日期数。例如,在 7 月 10 日,此 SQL 会返回从 5 月 1 日到 7 月 10 日的所有日期的列表 - 即前两个完整月份加上当前的部分月份。

                    【讨论】:

                    • 发布代码时,请将代码放在代码块中,以便于阅读,谢谢
                    【解决方案15】:

                    从今天开始计算 365 天的另一种简单方法是:

                    SELECT (TRUNC(sysdate) + (LEVEL-366)) AS DATE_ID
                    FROM DUAL connect by level <=( (sysdate)-(sysdate-366));
                    

                    【讨论】:

                      【解决方案16】:

                      我经常为我工作的调度应用程序这样做,我创建了一个流水线表函数。有时我需要几天、几小时或 15 分钟的间隔。这与我使用的函数不完全相同,因为我的代码在一个包中。但是,在这里,我在 2020 年 1 月 1 日到 2020 年 1 月 10 日之间有几天:

                      SELECT
                          days.date_time
                      FROM
                          table(between_times(TO_DATE('2020-01-01'),TO_DATE('2020-01-10'),(60*24), 'Y')) days
                      

                      流水线函数:

                      function between_times(i_start_time TIMESTAMP, i_end_time TIMESTAMP, i_interval_in_minutes NUMBER, include_end_time VARCHAR2 := 'N')
                        RETURN DateTableType  PIPELINED
                        AS
                          time_counter TIMESTAMP := i_start_time;
                        BEGIN
                          IF i_start_time IS NULL OR i_end_time IS NULL or i_start_time > i_end_time OR i_interval_in_minutes IS NULL OR
                            i_interval_in_minutes <= 0 THEN
                              RETURN;
                            END IF;
                            LOOP
                      
                              -- by default does not include end time
                              if (include_end_time = 'Y') THEN
                                exit when time_counter > i_end_time;
                              ELSE
                                exit when time_counter >= i_end_time;
                              END IF;
                              
                              
                              pipe row(DateType( time_counter ));     
                              time_counter := time_counter + i_interval_in_minutes/(60*24);
                              
                            END LOOP;
                            
                            EXCEPTION  WHEN NO_DATA_NEEDED THEN NULL;      
                        END;
                      

                      【讨论】:

                        【解决方案17】:
                        WITH Date_Table (Dates, Heading) AS  -- Using Oracle SQL
                        (SELECT  TRUNC(SYSDATE) Dates, '  Start' as Heading FROM dual
                        UNION ALL
                        SELECT TRUNC(DATES-1) ,  '  Inside recursion' as Heading FROM Date_Table
                        WHERE Dates > sysdate-365 )  -- Go back one year
                        SELECT TO_CHAR(Dates,'MM/DD/YYYY') 
                        FROM Date_Table
                        ORDER BY Dates DESC;
                        

                        【讨论】:

                        • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。
                        猜你喜欢
                        • 1970-01-01
                        • 2012-02-21
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多