오라클 PIVOT 및 UNPIVOT SQL 문 요약

다중 행을 문자열로 변환

이것은 비교적 간단하여 || 또는 concat 함수를 사용하여 구현할 수 있습니다.

1 select concat(user_id, user_name) combined_str from user_profile  
2   
3 select user_id||user_name combined_str from user_profile

문자열을 다중 열로 변환

실제로는 문자열 분할 문제이며, substr, instr, regexp_substr 함수 방식을 사용할 수 있습니다.

문자열을 다중 행으로 변환

union all 함수 등을 사용합니다.

listagg 함수

1 select listagg(employee_name,' , ')  within group (order by employee_name) from employee_table;

구문: listagg(열 이름, 구분자) within group (order by 열 이름)

wm_concat 함수

먼저 이 신기한 함수 wm_concat(열 이름)을 살펴보겠습니다. 이 함수는 열 값을 쉼표로 구분하여 한 줄로 표시할 수 있습니다. 다음으로 예제를 통해 이 신기한 함수가 어떻게 적용되는지 확인해 보겠습니다. 테스트 데이터를 준비합니다.

1 create table product_data(item_id number,item_name varchar2(20));  
2   
3 insert into product_data values(1,'Keyboard');  
4 insert into product_data values(1,'Mouse');  
5 insert into product_data values(1,'Monitor');  
6 insert into product_data values(2,'Webcam');  
7 insert into product_data values(2,'Headset');

효과 1: 행을 열로 변환, 기본적으로 쉼표로 구분

1 select wm_concat(item_name) item_names from product_data;

효과 2: 결과의 쉼표를 "|"로 교체

1 select replace(wm_concat(item_name),',','|') from product_data;

효과 3: ID별로 그룹화하여 item_name 병합

1 select item_id,wm_concat(item_name) item_names from product_data group by item_id;

SQL 문은 다음 SQL 문과 동일합니다

 1 -------- 적용 범위: 8i,9i,10g 및 이후 버전  ( MAX + DECODE )  
 2 select item_id, max(decode(rn, 1, item_name, null)) || max(decode(rn, 2, ','||item_name, null)) || max(decode(rn, 3, ','||item_name, null)) combined_str  
 3     from (select item_id, item_name ,row_number() over(partition by item_id order by item_name) as rn from product_data) t group by item_id order by 1;   
 4        
 5 -------- 적용 범위: 8i,9i,10g 및 이후 버전 ( ROW_NUMBER + LEAD )  
 6 select item_id, combined_str from (select item_id,row_number() over(partition by item_id order by item_name) as rn,item_name || lead(',' || item_name, 1)  
 7     over(partition by item_id order by item_name) ||  lead(',' || item_name, 2) over(partition by item_id order by item_name) || lead(',' || item_name, 3)   
 8     over(partition by item_id order by item_name) as combined_str from product_data) where rn = 1 order by 1;  
 9     
10 -------- 적용 범위: 10g 및 이후 버전 ( MODEL )  
11 select item_id, substr(combined_str, 2) combined_str from product_data model return updated rows partition by(item_id) dimension by(row_number()  
12     over(partition by item_id order by item_name) as rn) measures (cast(item_name as varchar2(20)) as combined_str) rules upsert iterate(3)  
13     until(presentv(combined_str[iteration_number + 2], 1, 0)=0) (combined_str[0] = combined_str[0] || ',' || combined_str[iteration_number + 1]) order by 1;       
14            
15 -------- 적용 범위: 8i,9i,10g 및 이후 버전 ( MAX + DECODE )  
16 select t.item_id item_id, max(substr(sys_connect_by_path(t.item_name, ','), 2)) combined_str from (select item_id, item_name, row_number()   
17     over(partition by item_id order by item_name) rn from product_data) t start with rn = 1 connect by rn = prior rn + 1 and item_id = prior item_id  
18     group by t.item_id;

간편 확장 사용법:

사례: 뷰를 작성해야 하는데, "create or replace view as select 필드1,...필드50 from tablename"과 유사하며, 기본 테이블에 50개 이상의 필드가 있습니다. 수동으로 작성하면 너무 번거롭습니다. 더 간단한 방법이 있을까요? 물론 있습니다. wm_concat을 사용하여 이 요구 사항을 어떻게 간단하게 만드는지 보여드리겠습니다. APP_USER 테이블에 (id, username, password, age) 4개의 필드가 있다고 가정해 보겠습니다. 쿼리 결과는 다음과 같습니다

1  /** 여기서 테이블 이름은 기본적으로 대소문자를 구분합니다 */  
2 select 'create or replace view as select '|| wm_concat(column_name) || ' from APP_USER' sql_statement   
3        from user_tab_columns where table_name='APP_USER';

시스템 테이블을 이용한 조회

1 select * from user_tab_columns

Oracle 11g 행과 열 전환 PIVOT 및 UNPIVOT 설명

Oracle 11g에서 Oracle은 pivot(행을 열로)과 unpivot(열을 행으로)이라는 2개의 쿼리를 추가했습니다.

참고: http://blog.csdn.net/tianlesoftware/article/details/7060306, http://www.oracle.com/technetwork/cn/articles/11g-pivot-101924-zhs.html

pivot 행을 열로 변환

테스트 데이터 (ID, 카테고리 이름, 판매 수량), 사례: 과일 유형별로 각 유형의 판매 수량이 표시되는 한 줄의 데이터를 쿼리합니다.

1 create table sales_data(id int,category_name varchar(20),quantity int);  ---- 테이블 생성  
2 insert into sales_data values(1, 'Apple', 1000);  
3 insert into sales_data values(2, 'Apple', 2000);  
4 insert into sales_data values(3, 'Apple', 4000);  
5 insert into sales_data values(4, 'Orange', 5000);  
6 insert into sales_data values(5, 'Orange', 3000);  
7 insert into sales_data values(6, 'Grape', 3500);  
8 insert into sales_data values(7, 'Mango', 4200);  
9 insert into sales_data values(8, 'Mango', 5500);

그룹화 쿼리 (물론 이것은 한 줄의 데이터를 조회하는 요구 사항에 부합하지 않습니다)

1 select category_name, sum(quantity) total_quantity from sales_data group by category_name

행을 열로 변환하는 쿼리

1 select * from (select category_name, quantity from sales_data) pivot (sum(quantity) for category_name in ('Apple' Apple, 'Orange', 'Grape', 'Mango'));

참고: pivot(집계 함수 for 열 이름 in(유형)), 여기서 in('')에서 별칭을 지정할 수 있으며, in에는 서브쿼리도 지정할 수 있습니다. 예: select distinct code from customers

물론 pivot 함수를 사용하지 않아도 동일한 결과를 얻을 수 있지만, 코드가 길고 이해하기 쉽습니다

1 ------ 여러 서브쿼리  
2 select * from (select sum(quantity) Apple from sales_data where category_name='Apple'),(select sum(quantity) Orange from sales_data where category_name='Orange'),  
3        (select sum(quantity) Grape from sales_data where category_name='Grape'),(select sum(quantity) Mango from sales_data where category_name='Mango');  
4          
5 ------ decode 함수 활용  
6 select sum(decode(category_name,'Apple',quantity)) Apple, sum(decode(category_name,'Orange',quantity)) Orange,   
7        sum(decode(category_name,'Grape',quantity)) Grape, sum(decode(category_name,'Mango',quantity)) Mango from sales_data

unpivot 열을 행으로 변환

명칭 그대로 여러 열을 하나의 열로 변환하는 것입니다.

사례: 현재 과일 테이블에는 4분기의 판매 수량이 기록되어 있으며, 이제 각 과일의 각 분기 판매 상황을 여러 행 데이터로 표시해야 합니다.

테이블 생성 및 데이터 삽입

1 create table fruit_sales(id int,fruit_name varchar(20), Q1 int, Q2 int, Q3 int, Q4 int);  
2   
3 insert into fruit_sales values(1,'Apple',1000,2000,3300,5000);  
4 insert into fruit_sales values(2,'Orange',3000,3000,3200,1500);  
5 insert into fruit_sales values(3,'Banana',2500,3500,2200,2500);  
6 insert into fruit_sales values(4,'Grape',1500,2500,1200,3500);  
7 select * from fruit_sales

열을 행으로 변환하는 쿼리

1 select id , fruit_name, quarter, sales_amount from fruit_sales unpivot (sales_amount for quarter in (q1, q2, q3, q4) )

참고: unpivot에는 집계 함수가 없으며, sales_amount, quarter 필드도 임시 변수입니다

마찬가지로 unpivot을 사용하지 않아도 동일한 효과를 얻을 수 있지만 SQL 문장이 길고 실행 속도 효율도 전자보다 낮습니다

1 select id, fruit_name ,'Q1' quarter, (select q1 from fruit_sales where id=f.id) sales_amount from fruit_sales f  
2 union  
3 select id, fruit_name ,'Q2' quarter, (select q2 from fruit_sales where id=f.id) sales_amount from fruit_sales f  
4 union  
5 select id, fruit_name ,'Q3' quarter, (select q3 from fruit_sales where id=f.id) sales_amount from fruit_sales f  
6 union  
7 select id, fruit_name ,'Q4' quarter, (select q4 from fruit_sales where id=f.id) sales_amount from fruit_sales f

XML 유형

위 pivot 열을 행으로 변환 예제에서 이미 조회해야 할 유형이 무엇인지 알고 있으며, in() 방식으로 포함했습니다. 만약 어떤 값이 있는지 모른다면 어떻게 쿼리를 구축할 수 있을까요?

pivot 작업의 다른 하위 절 XML은 이 문제를 해결하는 데 사용될 수 있습니다. 이 하위 절은 XML 형식으로 pivot 작업의 출력을 생성하도록 허용하며, 이 출력에서 ANY라는 특별한 하위 절을 지정할 수 있습니다

다음과 같은 예시:

1 select * from (  
2    select category_name, quantity as "Purchase Frequency"  
3    from sales_data t  
4 )                                
5 pivot xml (  
6    sum(quantity) for category_name in (any)  
7 )

보시다시피 CATEGORY_NAME_XML XMLTYPE 열에는 루트 요소가 있습니다. 각 값은 이름-값 요소 쌍으로 표시됩니다. 임의의 XML 분석기에서 출력을 사용하여 더 유용한 출력을 생성할 수 있습니다.

결론

Pivot은 SQL 언어에 매우 중요하고 실용적인 기능을 추가합니다. pivot 함수를 사용하여 복잡한 decode 함수가 포함된 이해하기 어렵고 직관적이지 않은 코드를 작성할 필요 없이 관계형 테이블에 대한 교차 테이블 보고서를 생성할 수 있습니다. 마찬가지로 unpivot 작업을 사용하여 교차 테이블 보고서를 변환하여 일반 관계형 테이블 형식으로 저장할 수 있습니다. Pivot은 일반 텍스트 또는 XML 형식의 출력을 생성할 수 있습니다. XML 형식의 출력인 경우 pivot 작업이 검색해야 하는 값 도메인을 지정할 필요가 없습니다.

원본 출처: http://www.ibloger.net/article/260.html

태그: 오라클 SQL PIVOT UNPIVOT LISTAGG WM_CONCAT

6월 23일 16:14에 게시됨