Oracle show all dates within a range and join to another table - sql

I am trying to run a report to show a list of all dates within the past 90 days and then join that back to another table that has a date in one column and supplemental data in another. Here is how I am getting all the dates within a range:
select trunc(sysdate-90) + rownum -1 from all_objects where rownum <=90
The problem is joining this to another table on date. If I run:
select trunc(sysdate-90) + rownum -1, t.col2 from all_objects
left join (select date, col2 from table) t on trunc(sysdate-90) + rownum -1 = t.date
where rownum <=90
Then it only displays the first record from t.col2 for all values within the date range. How do I properly join these two tables?
Thanks

A better way to get the previous 90 days is to use the dual / connect by trick:
select trunc(sysdate-level) as the_date from dual connect by level <= 90
Now, you should be able to do something like:
select the_date, t.col2
from (select trunc(sysdate-level) as the_date from dual connect by level <= 90) date_tbl
left join t on date_tbl.the_date = t.date

Don't use all_objects, this is a really bad idea in terms of performance and your DBA will want to string you up. I have no idea where this idea of using all_objects came from originally, but I wish it would die!
In order to get a list of dates, you can implement a much simpler and fast bit of SQL:
SELECT TRUNC( SYSDATE ) - ROWNUM a_date
FROM DUAL
CONNECT BY ROWNUM <= (the number of days you want)
If you want an offset, you can add that
SELECT TRUNC( SYSDATE ) - ROWNUM + (the offset) a_date
FROM DUAL
CONNECT BY ROWNUM <= (the number of days you want)
If you want a different order you can wrap if in another select and then order:
SELECT a_date
FROM (
SELECT TRUNC( SYSDATE ) - ROWNUM + (the offset)
FROM DUAL
CONNECT BY ROWNUM <= (the number of days you want)
)
ORDER BY a_date ASC
You can then embed this into your existing statement, bearing in mind that you need it in a sub-select. The reason for that is so that your ROWNUM values don't get mixed up:
SELECT t.date, t.col_2
FROM ( SELECT TRUNC( SYSDATE ) - ROWNUM a_date
FROM DUAL
CONNECT BY ROWNUM <= 90 ) date_list
LEFT JOIN table t ON date_list.a_date = t.date
ORDER BY a_date DESC

Related

Oracle SQL: fill in dates between dates

I have the following basic script, this shows me some current capacity loading in my production schedule.
select rl.duedate, rl.reservation_no resnr, qty
from gps_reservation_load rl
where rl.reservation_no in ('179459','179460','179461')
and rl.work_center_no in ('ALIN','AVD5','AVD9')
But, I want to show the DUEDATE as a date range from the sysdate to end of the year, e.g.
I have the following that gives me that range but, how can I combine the scripts to give me the result above?
select trunc(sysdate + rownum) dt
from DUAL connect by rownum < (to_date('01-JAN-2016', 'dd-mon-yyyy') - trunc(sysdate))
You just need to use outer join.
SELECT duedate_generated,reservation_no, qty, dt
from
(SELECT
trunc(sysdate + rownum) AS duedate_generated
FROM DUAL
connect by rownum < (to_date('01-JAN-2016', 'dd-mon-yyyy') - trunc(sydsate)
) d
OUTER JOIN
(select
duedate, reservation_no resnr, qty
from gps_reservation_load
where
reservation_no in ('179459','179460','179461')
and work_center_no in ('ALIN','AVD5','AVD9')
) r1
ON (d.duedate_generated = r1. due date)

get date range between dates

I have following table tbl in database and I have dynamic joining date 1-1-2012 and I want this date is between (Fall and spring) or (spring and summer) or (summer and fall).I want query in which i passed only joining date which return semestertime and joining date in Oracle.
Semestertime joiningDate
Fall 10-13-2011
Spring 2-1-2012
Summer 6-11-2012
Fall 10-1-2015
If I understand your question correctly:
SELECT *
FROM your_table
WHERE joiningDate between to_date (your_lower_limit_date_here, 'mm-dd-yyyy')
AND to_date (your_upper_limit_date_here, 'mm-dd-yyyy`);
What about something like that:
select 'BEFORE' term,
t."Semestertime", to_char(t."joiningDate", 'MM-DD-YYYY')
from (
select tbl.*, rownum rn from tbl where tbl."joiningDate" < to_date('1-1-2012','MM-DD-YYYY')
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- your reference date
order by tbl."joiningDate" desc) t
where rn = 1
union all
select 'AFTER' term,
t."Semestertime", to_char(t."joiningDate", 'MM-DD-YYYY')
from (
select tbl.*, rownum rn from tbl where tbl."joiningDate" > to_date('1-1-2012','MM-DD-YYYY')
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- your reference date
order by tbl."joiningDate" asc) t
where rn = 1
This will return the "term" before and after a given date. You will probably have to adapt such query to your specific needs. But that might be a good starting point.
For example, given your business rules, you might consider using <= instead of <. You you might require to have the result displayer a column instead of rows. Bu all of this shouldn't be too had to change.
As an alternate solution using CTE and sub-queries:
with testdata as (select to_date('1-1-2012','MM-DD-YYYY') refdate from dual)
select v.what, tbl.* from tbl join
(
select 'BEFORE' what, max(t1."joiningDate") d
from tbl t1
where t1."joiningDate" < to_date('1-1-2012','MM-DD-YYYY')
union all
select 'AFTER' what, min(t1."joiningDate") d
from tbl t1
where t1."joiningDate" > to_date('1-1-2012','MM-DD-YYYY')
) v
on tbl."joiningDate" = v.d
See http://sqlfiddle.com/#!4/c7fa5/15 for a live demo comparing those solutions.

Counting business days between two dates for each row in a table

I'm trying to implement this askTom solution to count the business days between two date fields in a table.
select count(*)
from ( select rownum rnum
from all_objects
where rownum <= to_date('&1') - to_date('&2')+1 )
where to_char( to_date('&2')+rnum-1, 'DY' ) not in ( 'SAT', 'SUN' )
I don't know how I can pass values to toms code or how to do a work around so that each time the select executes with a different set of dates and that way obtaining a similar output :
rowid | bussiness_days
I guess this could be implemented with a PL/SQL block but I'd prefer to keep it down to a query if possible. Could it be possible to pass values using &1 parameters from a select above toms one?
This is not like the original askTom, but if you're using 11gR2 you can use a Recursive CTE:
with rcte(a,b, i, is_wd) as (
select from_dt , to_dt , id, case when (to_char(from_dt, 'DY') in ('SAT','SUN')) then 0 else 1 end from t
union all
select decode(a, null, a,a+1), b, i, case when (to_char(a, 'DY') in ('SAT','SUN')) then 0 else 1 end
from rcte
where a+1 <= b
)
select i id, sum(is_wd)
from rcte
group by i
where t is a table containing "from_dates" and "to_dates"
Here is a sqlfiddle demo
Try this one:
SELECT COUNT(*)
FROM ( SELECT ROWNUM rnum
FROM all_objects
WHERE ROWNUM <= TO_DATE('2014-02-07','yyyy-mm-dd') - TO_DATE('2014-02-01','yyyy-mm-dd')+1 )
WHERE TO_CHAR( TO_DATE('2014-02-01','yyyy-mm-dd')+rnum-1, 'DY' ) NOT IN ( 'SAT', 'SUN' )
However, it has at least two issues:
It presumes the session NLS_DATE_LANGUAGE is set to English
The query gives wrong result if number of rows in ALL_OBJECTS is smaller than the amount of days between your ranges.
The version is less error-prone and faster:
WITH t AS
(SELECT TO_DATE('2014-02-01','yyyy-mm-dd')+LEVEL-1 the_date
FROM dual
CONNECT BY TO_DATE('2014-02-01','yyyy-mm-dd')+LEVEL-1 <= TO_DATE('2014-02-07','yyyy-mm-dd'))
SELECT COUNT(*)
FROM t
WHERE TO_CHAR(the_date, 'DY', 'NLS_DATE_LANGUAGE = AMERICAN') NOT IN ( 'SAT', 'SUN' )
This is another situation where a calendar table comes in handy. A calendar table has a row for every date.
If Saturday and Sunday are not business days, it's quite likely that holidays are not either. A field in the calendar table to indicate holidays makes it a lot easier to exclude them from your business days calculation.

Oracle SQL insert multiple rows into a table that has a range of dates

I found the following answer to a related question.
with calendar as(
select :startdate + rownum - 1 as day
from dual
connect by rownum < :enddate - startdate
)
select rownum as "S.No", to_date(day,'dd_mm_yyyy') as "Cal_Dt", to_char(day,'day') as "DayName"
from calendar
This SQL generates a number of dates and related info for a range of dates. Can some body tell me how to wrap this into an SQL statement so that the out of the select (all the rows) can be inserted into another table.
The Pseudocolumn LEVEL will also work
insert into tgt_tbl(id,dt,dy)
select distinct level,:startdate+level-1,to_char(:startdate+level-1,'Day') from dual
connect by level<=(:enddate-:startdate)+1
If the table you want to insert data into already exists, use INSERT INTO:
with calendar as (
select :startdate + rownum - 1 as day
from dual
connect by rownum < :enddate - startdate )
insert into anothertable (no, dt, dayname)
select rownum as "S.No", to_date(day,'dd_mm_yyyy') as "Cal_Dt", to_char(day,'day') as "DayName"
from calendar
Or use SELECT INTO syntax to create new table:
with calendar as (
select :startdate + rownum - 1 as day
from dual
connect by rownum < :enddate - startdate )
select rownum as "S.No", to_date(day,'dd_mm_yyyy') as "Cal_Dt", to_char(day,'day') as "DayName" into newtable
from calendar
EDIT -- Actually the comments are completely correct -- wasn't thinking about Oracle :-) Here is the proper way to create a table from a SQL statement in Oracle:
with calendar as (
select :startdate + rownum - 1 as day
from dual
connect by rownum < :enddate - startdate )
CREATE TABLE newtable
AS
(select rownum as "S.No", to_date(day,'dd_mm_yyyy') as "Cal_Dt", to_char(day,'day') as "DayName"
from calendar)
CREATE TABLE DATES_TABLE
(
Date_Field DATE
);
INSERT INTO DATES_TABLE
(
Date_Field
)
SELECT ROWNUM - 1 + TO_DATE('01-Jun-2004','dd-mon-yyyy') Date_Field
FROM all_objects
WHERE ROWNUM < TO_DATE('30-Jun-2004','dd-mon-yyyy') - TO_DATE('01-Jun-2004','dd-mon-yyyy') + 2;
Try changing those date values to get the values of dates in your required range

Find missing dates using SQL

I have some dates in table over of a two years like this as a example date
01-jan-2012
02-jan-2012
04-jan-2012
05-jan-2012
06-jan-2012
07-jan-2012
09-jan-2012
11-jan-2012
.
.
.
01-DEC-2012
I think you have noticed that there a missing date of 03-jan-2012 and 08-jan-2012 and the same criteria with all dates.My Question is that is there any way in oracle to find the missing dates.Plz Help !
This will get you all missing days for one year (SQL Fiddle).
all_dates generates a list of all dates of 2012 (adjust as required), and the LEFT JOIN checking for IS NULL eliminates those dates that exist in your source-table.
WITH all_dates AS (
SELECT TO_DATE('01-jan-2012') + ROWNUM - 1 AS d
FROM dual
CONNECT BY ROWNUM <= ADD_MONTHS(TO_DATE('01-jan-2012'), 12 ) - TO_DATE('01-jan-2012')
)
SELECT all_dates.d
FROM all_dates
LEFT JOIN t ON ( t.d = all_dates.d )
WHERE t.d IS NULL
ORDER BY all_dates.d
;
Make sure to use a bind variable instead of hard-coding the date three times.
You can generate sequence of date range you needed, then use LEFT JOIN to find missed dates.
Almost the same answer as #Peter, but a very slightly different version.
select all_dates.date_ missing_dates from
(select to_date('01-Jan-14') + level - 1 date_
from dual connect by level <= 365) all_dates
left join
((select to_date('01-Jan-14') + level - 1 date_
from dual connect by level <= 365)
minus
(select to_date(round (dbms_random.value (1, 28))
|| '-'
|| round (dbms_random.value (01, 12))
|| '-'
|| round (dbms_random.value (2014, 2014)),
'DD-MM-YYYY') + level - 1 random_date_1
from dual
connect by level <= 52)) transaction_data
on all_dates.date_ = transaction_data.date_
where transaction_data.date_ is null;