在oracle中将行拆分为列

Splitting rows to columns in oracle

我在 table 中有数据,看起来像:

我想拆分它的数据并通过 Oracle 中的 sql 查询(不使用数据透视表)使它看起来像下面这样:

怎么办??有没有其他不使用枢轴的方法?

既然你提到没有使用PIVOT函数,你可以试试 在组内使用 SQL 将行移动到一行,并使用 listagg 在一列中显示多个列值。

在Oracle 11g中,我们可以使用listagg内置函数:

select
    deptno,
    listagg (ename, ',') 
    WITHIN GROUP 
    (ORDER BY ename) enames
FROM 
 emp
GROUP BY 
 deptno

应该会得到以下结果:

DEPTNO ENAMES                                            
------ --------------------------------------------------
    10 CLARK,KING,MILLER                                 
    20 ADAMS,FORD,JONES,SCOTT,SMITH                
    30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD     

您可以在此处找到此问题的所有解决方案: http://www.dba-oracle.com/t_converting_rows_columns.htm

您需要在此处使用数据透视查询来获取您想要的输出:

SELECT Name,
       MIN(CASE WHEN ID_Type = 'PAN'      THEN ID_No ELSE NULL END) AS PAN,
       MIN(CASE WHEN ID_Type = 'DL'       THEN ID_No ELSE NULL END) AS DL,
       MIN(CASE WHEN ID_Type = 'Passport' THEN ID_No ELSE NULL END) AS Passport
FROM yourTable
GROUP BY Name

如果您是 运行 版本 11g 或更高版本,您也可以尝试使用 Oracle 的内置 PIVOT() 函数。

您可以使用 CTE 分解数据,然后将它们重新组合起来以获得您想要的结果:

WITH NAMES AS (SELECT DISTINCT NAME
                 FROM YOURTABLE),
     PAN AS (SELECT NAME, ID_NO AS PAN
               FROM YOURTABLE
               WHERE ID_TYPE = 'PAN'),
     DL AS (SELECT NAME, ID_NO AS DL
              FROM YOURTABLE
              WHERE ID_TYPE = 'DL'),
     PASSPORT AS (SELECT NAME, ID_NO AS "Passport"
                    FROM YOURTABLE
                    WHERE ID_TYPE = 'Passport')
SELECT n.NAME, p.PAN, d.DL, t."Passport"
  FROM NAMES n
  LEFT OUTER JOIN PAN p
    ON p.NAME = n.NAME
  LEFT OUTER JOIN DL d
    ON d.NAME = p.NAME
  LEFT OUTER JOIN PASSPORT t
    ON t.NAME = p.NAME'

YOURTABLE 替换为您感兴趣的 table 的实际名称。

祝你好运。

对于 Oracle 11g 及更高版本,您可以使用 PIVOT.

对于 11g 之前的版本,您可以使用 MAXCASE.

A common misconception,在性能方面比 MAXDECODE 的旧方法要好 PIVOT。但是,在引擎盖下 PIVOT 是相同的 MAX + CASE。您可以在 12c 中查看它,其中 Oracle 添加了 EXPAND_SQL_TEXT 过程到 DBMS_UTILITY 包。

例如,

SQL> variable c clob
SQL> begin
  2      dbms_utility.expand_sql_text(Q'[with choice_tbl as (
  3                      select 'Jones' person,1 choice_nbr,'Yellow' color from dual union all
  4                      select 'Jones',2,'Green' from dual union all
  5                      select 'Jones',3,'Blue' from dual union all
  6                      select 'Smith',1,'Orange' from dual
  7                     )
  8  select  *
  9    from  choice_tbl
 10    pivot(
 11          max(color)
 12          for choice_nbr in (1 choice_nbr1,2 choice_nbr2,3 choice_nbr3)
 13         )]',:c);
 14  end;
 15  /

PL/SQL procedure successfully completed.

现在让我们看看 Oracle 内部实际做了什么:

SQL> set long 100000
SQL> print c

C
--------------------------------------------------------------------------------
SELECT  "A1"."PERSON" "PERSON",
        "A1"."CHOICE_NBR1" "CHOICE_NBR1",
        "A1"."CHOICE_NBR2" "CHOICE_NBR2",
        "A1"."CHOICE_NBR3" "CHOICE_NBR3"
  FROM  (
         SELECT  "A2"."PERSON" "PERSON",
                 MAX(CASE  WHEN ("A2"."CHOICE_NBR"=1) THEN "A2"."COLOR" END ) "CHOICE_NBR1",
                 MAX(CASE  WHEN ("A2"."CHOICE_NBR"=2) THEN "A2"."COLOR" END ) "CHOICE_NBR2",
                 MAX(CASE  WHEN ("A2"."CHOICE_NBR"=3) THEN "A2"."COLOR" END ) "CHOICE_NBR3"
          FROM  (
                 (SELECT 'Jones' "PERSON",1 "CHOICE_NBR",'Yellow' "COLOR" FROM "SYS"."DUAL" "A7") UNION ALL
                 (SELECT 'Jones' "'JONES'",2 "2",'Green' "'GREEN'" FROM "SYS"."DUAL" "A6") UNION ALL
                 (SELECT 'Jones' "'JONES'",3 "3",'Blue' "'BLUE'" FROM "SYS"."DUAL" "A5") UNION ALL
                 (SELECT 'Smith' "'SMITH'",1 "1",'Orange' "'ORANGE'" FROM "SYS"."DUAL" "A4")
                ) "A2"
          GROUP BY "A2"."PERSON"
       ) "A1"
SQL>

Oracle 在内部将 PIVOT 解释为 MAX + CASE

您可以通过了解数据透视查询的作用来创建非数据透视查询:

select *
from yourTable
pivot
(
  max (id_no)
  for (id_type) in ('PAN' as pan, 'DL' as dl, 'Passport' as passport)
)

主元所做的是 GROUP BY PIVOT 子句中未指定的所有列(实际上,只是 name 列),选择新列基于 FOR 子句之前的聚合的子查询方式,用于 IN 子句中指定的每个值,并丢弃 PIVOT 子句中指定的那些列。

当我说 "subquery fashion" 时,我指的是一种实现 PIVOT 结果的方法。实际上,我不知道这在幕后是如何运作的。这种子查询方式将是这样的:

select <aggregation>
from <yourTable>
where 1=1 
  and <FORclauseColumns> = <INclauseValue>
  and <subqueryTableColumns> = <PIVOTgroupedColumns>

现在您确定了如何在没有 PIVOT 子句的情况下创建查询:

select 
  name,
  (select max(id_no) from yourTable where name = t.name and id_type = 'PAN') as pan,
  (select max(id_no) from yourTable where name = t.name and id_type = 'DL') as dl,
  (select max(id_no) from yourTable where name = t.name and id_type = 'Passport') as passport
from yourTable t
group by name