根据oracle中的年份获取记录

时间:2013-12-24 06:54:29

标签: database oracle oracle11g database-performance date-arithmetic

我正在创建一个查询,根据年份给出两天之间的天数。实际上我有以下类型的日期范围

From Date: TO_DATE('01-Jun-2011','dd-MM-yyyy') 
To Date:   TO_DATE('31-Dec-2013','dd-MM-yyyy')

我的结果应该是:

   Year         Number of day
   ------------------------------
   2011           XXX
   2012           XXX
   2013           XXX

我试过下面的查询

WITH all_dates AS
  (SELECT start_date + LEVEL - 1 AS a_date
  FROM
    (SELECT TO_DATE ('21/03/2011', 'DD/MM/YYYY') AS start_date ,
      TO_DATE ('25/06/2013', 'DD/MM/YYYY')       AS end_date
    FROM dual
    )
    CONNECT BY LEVEL <= end_date + 1 - start_date
  )
SELECT TO_CHAR ( TRUNC (a_date, 'YEAR') , 'YYYY' ) AS YEAR,
  COUNT (*) AS num_days
FROM all_dates
WHERE a_date - TRUNC (a_date, 'IW') < 7
GROUP BY TRUNC (a_date, 'YEAR')
ORDER BY TRUNC (a_date, 'YEAR') ;

我得到了确切的输出

   Year         Number of day
   ------------------------------
   2011           286
   2012           366
   2013           176

我的问题是,如果我使用connect然后查询执行需要很长时间,因为我在表中有数百万条记录,因此我不想使用connect by子句 connect by子句正在针对特定记录创建虚拟行。

非常感谢任何帮助或建议。

5 个答案:

答案 0 :(得分:4)

根据您模糊的预期结果,我认为您希望这些日期之间的记录数量,而不是天数;但它还不清楚。由于您在问题中引用了一个表格,我假设您需要与表格数据相关的内容,而不仅仅是两个日期之间的日期,而这些日期根本不依赖于表格。 (我不知道connect by子句引用意味着什么)。这应该会给你,如果你想要的话:

select extract(year from date_field), count(*)
from t42
where date_field >= to_date('01-Jun-2011', 'DD-MON-YYYY')
and date_field < to_date('31-Dec-2013') + interval '1' day
group by extract(year from date_field)
order by extract(year from date_field);

where子句与两个日期之间的预期相同;我假设您的日期字段中可能有时间(即不是全部在午夜)并且您想要计算您范围内最后一个日期的所有记录。然后根据每条记录的年份对其进行分组和计数。

SQL Fiddle

如果您想要在该范围内有记录的天数,那么您可以稍微改变计数:

select extract(year from date_field), count(distinct trunc(date_field))
...

SQL Fiddle

答案 1 :(得分:1)

您可以使用以下功能来减少虚拟行的数量,只考虑其中的年份。您可以检查SQLFIDDLE以检查性能。

  1. 首先只考虑当年的开始日期和年末之间的天数 结束日期(如果是同年)

  2. 然后考虑从开始日期的明年到结束日期之前的年份之间的年份

  3. 最后考虑从结束日期年份开始到结束日期的天数

    因此,我们不需要迭代开始日期和结束日期之间的所有日子,而只需要迭代年份

    WITH all_dates AS
    (SELECT (TO_CHAR(START_DATE,'yyyy') + LEVEL - 1) YEARS_BETWEEN,start_date,end_date 
    FROM
    (SELECT TO_DATE ('21/03/2011', 'DD/MM/YYYY') AS start_date ,
    TO_DATE ('25/06/2013', 'DD/MM/YYYY')       AS end_date
    FROM dual
    )  
    CONNECT BY LEVEL <= (TO_CHAR(end_date,'yyyy')) - (TO_CHAR(start_date,'yyyy')-1)
    ) 
    SELECT DECODE(TO_CHAR(END_DATE,'yyyy'),YEARS_BETWEEN,END_DATE
    ,to_date('31-12-'||years_between,'dd-mm-yyyy'))
    - DECODE(TO_CHAR(START_DATE,'yyyy'),YEARS_BETWEEN,START_DATE
    ,to_date('01-01-'||years_between,'dd-mm-yyyy'))+1,years_between
    FROM ALL_DATES;
    

答案 2 :(得分:0)

在Oracle中,你可以对这样的日期执行加法和减法......

  

选择   TO_DATE('31 -Dec-2013','dd-MM-yyyy') - TO_DATE('01 -Jun-2011','dd-MM-yyyy')   来自双重日;

它将返回两个日期之间的日差....

答案 3 :(得分:0)

select to_date(2011, 'yyyy'), to_date(2012, 'yyyy'), to_date(2013, 'yyyy')
from dual;

TO_DATE(2011,'Y TO_DATE(2012,'Y TO_DATE(2013,'Y
--------------- --------------- ---------------
01-MAY-11       01-MAY-12       01-MAY-13

答案 4 :(得分:0)

 select to_char(date_field,'yyyy'), count(*)
 from your_table
 where date_field between to_date('01-Jun-2011', 'DD-MON-YYYY')
 and to_date('31-Dec-2013 23:59:59', 'DD-MON-YYYY hh24:mi:ss')
 group by to_char(date_field,'yyyy')
 order by to_char(date_field,'yyyy');