Tricky view for Oracle
I have a table "prices" with columns :
year, janprc, janqty, febprc, febqty开发者_JS百科 ...
(prices and quantyties for all the months in a year)
what I need is to create a view "monthlyprices" with the columns :
year, month, price, quantity
using data from the table above. how could i do that ?
Thanks!
Here is how to do it with one UNPIVOT statement and no UNIONs.
with t as (
select 2008 year, 1 janprc, 500 janqty, 1 febprc, 600 febqty from dual
union
select 2009, 50, 1000, 20, 3000 from dual
union
select 2010, 60, 1000, 25, 3000 from dual
)
SELECT *
FROM t
UNPIVOT (
(price, quantity) FOR month IN
(
(janprc, janqty) AS 'jan',
(febprc, febqty) AS 'feb'
)
)
order by
year, month
;
It's pretty much as simple as writing 12 sub-queries and UNION
ing their results together:
CREATE VIEW MONTHLYPRICES AS
SELECT
year AS year,
'January' AS month,
janprc AS price,
janqty AS quantity
FROM
PRICES
UNION ALL
SELECT
year AS year,
'February' AS month,
febprc AS price,
febqty AS quantity
FROM
PRICES
UNION ALL
SELECT
year AS year,
'March' AS month,
marprc AS price,
marqty AS quantity
FROM
PRICES
UNION ALL
... and so on ...
You can use UNION ALL
because you know that there will not be any duplicates.
Use 11 UNIONs to build the table you want one month at a time,
with t as (
select 2008 year, 1 janprc, 1 janqty, 1 febprc, 1 febqty from dual
union
select 2009, 50, 10, 20, 30 from dual
union
select 2010, 60, 10, 25, 30 from dual
)
select year, 'jan' month, janprc price, janqty quantity from t
union
select year, 'feb', febprc, febqty from t
;
This assumes there is no more than one record per year. If there is more than one record per year use UNION ALL to preserve duplicate rows.
You might be able to make use of Oracle 11g's UNPIVOT operation, I don't have an 11g instance kicking around to test against though I'm afraid.
The union approach looks a bit painful to me. You can do it like this, replacing your real table name for so_4164416 and choosing the way you want to represent the months - maybe not full names (and I suspect there's a better way to generate the month names anyway!):
create or replace view monthlyprices as
with tmp_month_num as
(select rownum as month_num from dual connect by level <= 12)
select so.year,
trim(to_char(to_date('01/' || tmn.month_num || '/2010','DD/MM/YYYY'),
'Month')) month,
case tmn.month_num
when 01 then so.janprc
when 02 then so.febprc
when 03 then so.marprc
when 04 then so.aprprc
when 05 then so.mayprc
when 06 then so.junprc
when 07 then so.julprc
when 08 then so.augprc
when 09 then so.sepprc
when 10 then so.octprc
when 11 then so.novprc
when 12 then so.decprc end as price,
case tmn.month_num
when 01 then so.janqty
when 02 then so.febqty
when 03 then so.marqty
when 04 then so.aprqty
when 05 then so.mayqty
when 06 then so.junqty
when 07 then so.julqty
when 08 then so.augqty
when 09 then so.sepqty
when 10 then so.octqty
when 11 then so.novqty
when 12 then so.decqty end as quantity
from so_4164416 so, tmp_month_num tmn
order by so.year, tmn.month_num;
select * from monthlyprices where year = 2009 and month = 'January';
If UNPIVOT
is available, you should definitely use that. For earlier versions of Oracle you could cross join your table with a table of the month names (generated or prebuilt) and then use decode or case statements to select the correct month, price, and quantity. Here is how this would look.
create table prices (Year Varchar2(4), JanPrc Number(3), JanQty Number(3),
FebPrc Number(5,2), FebQty Number(3), MarPrc Number(3), MarQty Number(3));
insert into prices values ('2008',1,500,1,600,1,700);
insert into prices values ('2009',50,100,20,300,30,800);
insert into prices values ('2010',60,5,70,10,80,15);
SELECT Year, Month, DECODE(MonthNumber,1,JanPrc,2,FebPrc,MarPrc) Price,
DECODE(MonthNumber,1,JanQty,2,FebQty,MarQty) Quantity
FROM Prices
CROSS JOIN (
SELECT rownum MonthNumber,
to_char(to_date(to_char(rownum,'FM00') || '2000','MMYYYY'),
'FMMonth') Month
FROM dual CONNECT BY rownum <= 3
)
ORDER BY Year, MonthNumber;
精彩评论