【问题标题】:Tricky view for OracleOracle 的棘手视图
【发布时间】:2011-05-09 01:02:09
【问题描述】:

我有一个带有列的“价格”表:

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

(一年中所有月份的价格和数量)

我需要创建一个包含列的视图“monthlyprices”:

year, month, price, quantity 

使用上表中的数据。 我该怎么做?

谢谢!

【问题讨论】:

  • 先把一张卷起来的报纸贴在“设计”那张桌子的人身上……
  • @Tony:很高兴他们没有这样做2010janprc, 2010janqty ... 2011janprc, 2011janqty...。 :)
  • 有没有人有 11g 可以使用unpivot 看看?

标签: sql oracle view union


【解决方案1】:

如果UNPIVOT 可用,您绝对应该使用它。对于早期版本的 Oracle,您可以将表与包含月份名称(生成或预构建)的表交叉连接,然后使用 decode 或 case 语句来选择正确的月份、价格和数量。这是它的外观。

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;

【讨论】:

    【解决方案2】:

    这里是如何使用一个 UNPIVOT 语句而不是 UNION 来做到这一点。

    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
    ;
    

    【讨论】:

    【解决方案3】:

    联合方法对我来说有点痛苦。您可以这样做,将您的真实表名替换为 so_4164416 并选择您想要表示月份的方式 - 可能不是全名(我怀疑无论如何都有更好的方法来生成月份名称!):

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

    【讨论】:

    • 我认为“痛苦”是主观的。在我自己(非常主观的)意见中,this 看起来比UNIONs 更痛苦。 :)
    • 因此是“对我来说”。一般来说,我认为工会会更难维持,尽管这在这里可能不是问题。当然,这也是主观的。
    • 我关心的不是维护,而是性能。我猜 Alex 的解决方案比 uber-union 性能更高,但我猜这取决于数据。
    • @nineside:只是出于好奇,是什么让您认为它的性能更高?它在tmp_month_num 伪表和so_4164416 之间进行交叉连接,从而访问相同的记录12 次(就像UNION 查询一样)。
    • @Adam Paynter:我想我假设,在最坏的情况下,它仍然只会导致对一个可能很大的表进行单表扫描,然后是一些内存欺骗。 uber-union 需要对同一个表进行 12 次表扫描,不是吗?
    【解决方案4】:

    您也许可以使用 Oracle 11g 的 UNPIVOT 操作,但我担心我没有 11g 实例可供测试。

    【讨论】:

    • 除非我弄错了,UNPIVOT 只能产生一列(而不是两列),对吗?他如何能够通过UNPIVOT 产生价格数量?
    【解决方案5】:

    一次使用 11 个 UNION 构建您想要的表,

    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
    ;
    

    这假设每年不超过一个记录。如果每年有多个记录,请使用 UNION ALL 保留重复行。

    【讨论】:

      【解决方案6】:

      这非常简单,只需编写 12 个子查询并UNION将它们的结果放在一起:

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

      您可以使用UNION ALL,因为您知道不会有任何重复。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多