将多列转置为行

Transpose multiple columns into rows

甲骨文 12c/19c

我继承了以下格式的 table 中的一些数据(请不要打败我为什么有人会以这种格式存储数据等 - 我明白了但必须工作用我所拥有的)。业务需要数月的列转换为行的数据(目标格式如下所示)。

我可以使用下面列出的大联合查询获取我想要的数据,但想看看是否有人有任何 better/efficient 方法来编写它。我也在考虑创建一个夜间作业来将这些数据转换为新格式(或创建一个 mview)。

来源:

ORDER_ID
ORDER_YEAR
QTY_JAN
QTY_FEB
QTY_MAR
QTY_APR
QTY_MAY
QTY_JUN
QTY_JUL
QTY_AUG
QTY_SEP
QTY_OCT
QTY_NOV
QTY_DEC

目标:

ORDER_ID
ORDER_YEAR
ORDER_MONTH
QTY

查询:

select order_id, order_year, '01' order_month, sum(qty_jan) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '02' order_month, sum(qty_feb) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '03' order_month, sum(qty_mar) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '04' order_month, sum(qty_apr) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '05' order_month, sum(qty_may) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '06' order_month, sum(qty_jun) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '07' order_month, sum(qty_jul) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '08' order_month, sum(qty_aug) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '09' order_month, sum(qty_sep) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '10' order_month, sum(qty_oct) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '11' order_month, sum(qty_nov) qty
from order_quantity
group by order_id, order_year
union
select order_id, order_year, '12' order_month, sum(qty_dec) qty
from order_quantity
group by order_id, order_year
;

提前致谢!

最简单的方法是使用 UNPIVOT 运算符,从 Oracle 11.1 开始可用。

我在输出中包含了数量为 NULL 的月份(如果不需要它们,很容易修改)。我还为每个月创建了 两个 列 - 一个用于月份名称,另一个用于月份在一年中的位置。该名称可以在 SELECT 子句中使用(它将显示在输出中),而 "position" 仅在 ORDER BY 子句中使用。

我在 WITH 子句中创建了用于测试的样本数据。当然,这不是现实生活中解决方案的一部分。在主查询中使用实际的 table 和列名。我只包括前三个月 - 您应该很容易更改以包括所有 12 个月。

with
  order_quantity (order_id, order_year, qty_jan, qty_feb, qty_mar) as (
    select 1001, 2018, 300, 450, 200 from dual union all
    select 1001, 2019, 400, 250, null from dual union all
    select 1001, 2020, 300, null, 200 from dual union all
    select 1002, 2019, 540, 230, null from dual union all
    select 1002, 2020, null, 300, 800 from dual
  )
select order_id, order_year, order_month, qty
from   order_quantity
unpivot include nulls (
    qty for (order_month, pos) in ( qty_jan as ('January' , 1)
                                  , qty_feb as ('February', 2)
                                  , qty_mar as ('March'   , 3)
                                  )
                      )
order by order_id, order_year, pos
;

输出:

  ORDER_ID ORDER_YEAR ORDER_MONTH        QTY
---------- ---------- ----------- ----------
      1001       2018 January            300
      1001       2018 February           450
      1001       2018 March              200
      1001       2019 January            400
      1001       2019 February           250
      1001       2019 March                 
      1001       2020 January            300
      1001       2020 February              
      1001       2020 March              200
      1002       2019 January            540
      1002       2019 February           230
      1002       2019 March                 
      1002       2020 January               
      1002       2020 February           300
      1002       2020 March              800

您可以使用层级查询如下:

SELECT ORDER_ID, ORDER_YEAR,
       DECODE(LVL,1,'January',2,'February', ...., 12,'December') AS ORDER_MONTH,
       DECODE(LVL,1,QTY_JAN,2,QTY_FEB, ...., 12,QTY_DEC) AS QTY
  FROM YOUR_TABLE
  CROSS JOIN (SELECT LEVEL AS LVL FROM DUAL CONNECT BY LEVEL <= 12);