oracle中列转行

convert column to row in oracle

我有一个问题,有什么方法可以将列转换为行。 例如,我有一个这样的 table:

CREATE TABLE mytable(u_id, month, offer, revenue) as
SELECT 1, 'January', 'Offer_1', 45  FROM dual
UNION ALL
SELECT 1, 'February','Offer_2', 40  FROM dual
UNION ALL
SELECT 1, 'March'   ,'Offer_1', 35  FROM dual
UNION ALL
SELECT 2, 'January' ,'Offer_2', 40  FROM dual
UNION ALL
SELECT 2, 'February','Offer_3', 40  FROM dual
UNION ALL
SELECT 2, 'March'   ,'Offer_1', 50  FROM dual;

我的预期 table 是

(每个用户应该一行)

u_id january_offer january_revenue february_offer february_revenue march_offer march_revenue
1 Offer_1 45 Offer_2 40 Offer_1 35
2 Offer_2 40 Offer_3 40 Offer_1 50

我试过了:

SELECT t1.u_id,
       t1.january_offer,
       t1.january_revenue,
       t2.february_offer,
       t2.february_revenue,
       t3.march_offer,
       t3.march_revenue
  FROM (SELECT u_id, offer AS january_offer, 
                     revenue AS january_revenue
          FROM mytable
         WHERE month = 'January') t1
  LEFT JOIN (SELECT u_id,
                    offer   AS february_offer,
                    revenue AS february_revenue
               FROM mytable t1
              WHERE month = 'February') t2
    ON t1.u_id = t2.u_id
  LEFT JOIN (SELECT u_id, offer AS march_offer, 
                          revenue AS march_revenue
               FROM mytable t1
              WHERE month = 'March') t3
    ON t2.u_id = t3.u_id

但是,真的,我有很多数据和一个很长的 SQL 脚本,这种方法加重了我的脚本。

您可以将条件聚合与 CASE..WHEN 表达式一起使用,例如

SELECT u_id,
       MAX(CASE WHEN month = 'January' THEN offer END) AS january_offer,
       MAX(CASE WHEN month = 'January' THEN revenue END) AS january_revenue,
       MAX(CASE WHEN month = 'February' THEN offer END) AS february_offer,
       MAX(CASE WHEN month = 'February' THEN revenue END) AS february_revenue,
       MAX(CASE WHEN month = 'March' THEN offer END) AS march_offer,
       MAX(CASE WHEN month = 'March' THEN revenue END) AS march_revenue
  FROM t
 GROUP BY u_id 

或使用DECODE()函数,例如

SELECT u_id,
       MAX(DECODE (month , 'January', offer)) AS january_offer,
       MAX(DECODE (month , 'January', revenue)) AS january_revenue,
       MAX(DECODE (month , 'February', offer)) AS february_offer,
       MAX(DECODE (month , 'February', revenue)) AS february_revenue,
       MAX(DECODE (month , 'March', offer)) AS march_offer,
       MAX(DECODE (month , 'March', revenue)) AS march_revenue
  FROM t
 GROUP BY u_id

Demo

另一种可能更有效的模式是使用 case 表达式:

SELECT t1.u_id
     , CASE month WHEN 'January' then offer END AS january_offer
     , CASE month WHEN 'January' then revenue END AS january_revenue
     , CASE month WHEN 'February' then offer END AS february_offer
     , ...

从那里您可以使用聚合函数来消除空行:

SELECT u_id
     , MAX(january_offer) AS january_offer
     , MAX(january_revenue) AS january_revenue
     , MAX(february_offer) AS february_offer
     , ...
FROM (  
    SELECT t1.u_id
         , CASE month WHEN 'January' then offer END AS january_offer
         , CASE month WHEN 'February' then offer END AS february_offer
         , ...
) AS t
GROUP BY u_id

一般来说,这种操作在应用程序的表示层比在数据库层处理得更好。

这就是 PIVOT 的设计目的:

SELECT *
FROM   package
PIVOT (
  MAX(offer) AS offer, MAX(revenue) AS revenue
  FOR month IN ( 'January' AS january, 'February' AS feburary, 'March' AS march )
)

其中,对于示例数据:

CREATE TABLE package (U_id, month, offer,revenue) AS
SELECT 1, 'January',  'offer_1', 45 FROM DUAL UNION ALL
SELECT 1, 'February', 'offer_2', 40 FROM DUAL UNION ALL
SELECT 1, 'March',    'offer_1', 35 FROM DUAL UNION ALL
SELECT 2, 'January',  'offer_2', 40 FROM DUAL UNION ALL
SELECT 2, 'February', 'offer_3', 40 FROM DUAL UNION ALL
SELECT 2, 'March',    'offer_1', 50 FROM DUAL;

输出:

U_ID JANUARY_OFFER JANUARY_REVENUE FEBURARY_OFFER FEBURARY_REVENUE MARCH_OFFER MARCH_REVENUE
1 offer_1 45 offer_2 40 offer_1 35
2 offer_2 40 offer_3 40 offer_1 50

db<>fiddle here