Reputation: 21
I am having difficulty creating a Oracle query to report the historical status of orders on certain days (the first of each month over a series of months). I have searched and searched and have found no one asking a similar question. It seems like a straighforward problem so hopefully someone can help! Here is my example:
ORDERS table:
ORDER_NUMBER STATUS DATE
50001000 Created 01-15-2010
50001000 Released 02-20-2010
50001000 Completed 02-25-2010
50001000 Closed 03-10-2010
50001035 Created 01-20-2010
50001035 Released 01-25-2010
50001035 Completed 04-05-2010
50001035 Closed 05-30-2010
So the output I am needing is the status of each order at the beginning of each month. Something like this:
DATE ORDER_NUMBER STATUS
12-01-2009
01-01-2010
02-01-2010 50001000 Created
02-01-2010 50001035 Released
03-01-2010 50001000 Completed
03-01-2010 50001035 Released
04-01-2010 50001000 Closed
04-01-2010 50001035 Released
05-01-2010 50001000 Closed
05-01-2010 50001035 Completed
06-01-2010 50001000 Closed
06-01-2010 50001035 Closed
07-01-2010 50001000 Closed
07-01-2010 50001035 Closed
..etc
Are there some native keywords that can make this work without lots of joins and subqueries?
Thanks,
Garrett
Upvotes: 2
Views: 160
Reputation: 35068
It took a while, but I think this would be what you're looking for:
select to_char(mf.month_first, 'MON-YYYY'),
o.order_name,
o.status
from (select add_months(to_date('01-DEC-2009'), level-1) month_first
from dual
connect by level <= 12) mf
left outer join orders o
on trunc(o.status_date, 'MM') <= mf.month_first
where not exists(
select 1
from orders
where ((trunc(status_date, 'MM') = trunc(o.status_date, 'MM')
and status_date < o.status_date)
or (trunc(status_date, 'MM') != trunc(o.status_date, 'MM')
and status_date >= o.status_date))
and trunc(status_date, 'MM') <= mf.month_first
and order_name = o.order_name)
order by mf.month_first, o.order_name
The above query ensures that if there are two status changes in the same month, the first one is displayed, which is why the comparison between status_date
and o.status_date
happens twice, once for when you're in the same month, once for different months...
The connect by level
subselect allows you to vary the date range by specifying a first date of the month (01-DEC-2009
in this case) and a duration/length of the report (12
months).
I hope this is what you were after, though I have to say if you've got lots of orders, this will most likely create lots of rows (especially if an order is completed in March, it'll show up as Closed
until the end of the report.
Here's a SQLFiddle to see it working.
Upvotes: 1
Reputation: 5792
Not sure that I understood your sample data but here's the query that may help you - compare your date to the first day of each month:
SELECT * FROM your_table
WHERE your_date IN
(
-- This query will give you the first day of each month --
Select Add_Months(Trunc(Sysdate,'YEAR'),Level-1) first_day_of_month
From dual
Connect By Level <= 12 -- number of months in a year --
)
/
Upvotes: 0