在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 之前的版本,您可以使用 MAX 和 CASE.
A common misconception,在性能方面比 MAX
和 DECODE
的旧方法要好 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
我在 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 之前的版本,您可以使用 MAX 和 CASE.
A common misconception,在性能方面比 MAX
和 DECODE
的旧方法要好 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