Domanda

I am creating a query to give number of days between two days based on year. Actually I have below type of date range

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

My Result should be:

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

I've tried below query

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

I got exact output

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

My question is if i use connect by then query execution takes long time as i have millions of records in table and hence i don't want to use connect by clause connect by clause is creating virtual rows against the particular record.

Any help or suggestion would be greatly appreciated.

È stato utile?

Soluzione 2

you can use the below function to reduce the number of virtual rows by considering only the years in between.You can check the SQLFIDDLE to check the performance.

  1. First consider only the number of days between start date and the year end of that year or End date if it is in same year

  2. Then consider the years in between from next year of start date to the year before the end date year

  3. Finally consider the number of days from start of end date year to end date

    Hence instead of iterating for all the days between start date and end date we need to iterate only the years

    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;
    

Altri suggerimenti

From your vague expected results I think you want the number of records between those dates, not the number of days; but it's rather unclear. Since you refer to a table in the question I assume you want something related to the table data, not simply days between two dates which wouldn't depend on a table at all. (I have no idea what the connect by clause reference means though). This should give you that, if it is what you want:

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

The where clause is as you'd expect between two dates; I've assumed there might be times in your date field (i.e. not all at midnight) and that you want to count all records on the last date in your range. Then it's grouping and counting based on the year for each record.

SQL Fiddle.

If you want the number of days that have records within the range, then you can just vary the count slightly:

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

SQL Fiddle.

In Oracle you can perform Addition and Substraction to dates like this...

SELECT TO_DATE('31-Dec-2013','dd-MM-yyyy') - TO_DATE('01-Jun-2011','dd-MM-yyyy') DAYS FROM DUAL;

it will return day difference between two dates....

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
 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');
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top