2010-11-12 19 views
5

Tengo una tabla "precios" con columnas:vista Tricky para Oracle

year, janprc, janqty, febprc, febqty ... 

(precios y quantyties para todos los meses en un año)

lo que necesito es crear un punto de vista "monthlyprices "con las columnas:

year, month, price, quantity 

usando los datos de la tabla anterior. ¿cómo podría hacer eso?

Gracias!

+9

En primer lugar se aplica un periódico enrollado a la persona que "diseñado" esa mesa ... –

+5

@Tony: Alégrate de que no hicieron '2010janprc, 2010janqty ... 2011janprc , 2011janqty ... '. :) –

+2

¿Alguien tiene 11g dando vueltas para ver el uso de 'unpivot'? – ninesided

Respuesta

1

es casi tan simple como escribir 12 sub-consultas y sus resultados UNION ing juntos:

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 ... 

Puede utilizar UNION ALL porque usted sabe que no habrá ningún duplicado.

1

Uso 11 sindicatos para construir la tabla que desea un mes a la vez,

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 
; 

alt text

Esto asume que no hay más de un registro por año. Si hay más de un registro por año, use UNION ALL para conservar las filas duplicadas.

1

Es posible que pueda hacer uso de la operación UNPIVOT de Oracle 11g, aunque no tengo una instancia de 11 g dando vueltas para probar en contra, aunque me temo.

+0

A menos que me equivoque, 'UNPIVOT' solo puede generar una columna (no dos), ¿correcto? ¿Cómo podría generar el precio ** y ** cantidad a través de 'UNPIVOT'? –

0

Si UNPIVOT está disponible, definitivamente debe usar eso. Para versiones anteriores de Oracle, puede cruzar su mesa con una tabla de los nombres de los meses (generados o preconstruidos) y luego usar descodificaciones o declaraciones de casos para seleccionar el mes, el precio y la cantidad correctos. Así es como se vería esto.

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

El enfoque de unión me parece un poco doloroso. Puede hacerlo así, reemplazando su nombre de tabla real para so_4164416 y eligiendo la forma en que desea representar los meses; tal vez nombres completos (y sospecho que hay una forma mejor de generar los nombres de mes de todos modos):

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'; 
+0

Supongo que "doloroso" es subjetivo. En mi propia opinión (altamente subjetiva), * esto * parece más doloroso que el 'UNION's. :) –

+0

Por lo tanto 'a mí' . En general, creo que la unión sería más difícil de mantener, aunque probablemente no sea un problema aquí. Y también subjetivo, por supuesto. –

+0

no es el mantenimiento que me preocupa, sino el rendimiento. Supongo que la solución de Alex es más eficiente que la uber-union, pero supongo que depende de los datos. – ninesided

9

Aquí se muestra cómo hacerlo con una declaración UNPIVOT y sin UNIONES.

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 
; 

alt text

+0

+1: ¡Totalmente maravilloso! –

+2

Apoyos a @ninesided para inspirarme a luchar contra los diagramas de sintaxis de UNPIVOT. Valió el esfuerzo. –

+0

@Janek: ¿Dónde está el diagrama de sintaxis? Lo estaba buscando ... –