見出し画像

SQLの縦持ち(unpivot)と横持ち(pivot)関数

表記件、Teradataで横持ちテーブルを縦持ちにする際の関数、反対に縦持ちテーブルを横持ちにする際の関数を使ってみました。まずは以下のような横持ちテーブルを用意します。

作成SQLは以下の通りです。

 /*サンプルデータのテーブル*/ 
 -- drop table jumbo.pivo; 
create table jumbo.pivo ( 
顧客番号 integer, 
地域 varchar(2) character set unicode, 
商品a integer, 
商品b integer, 
商品c integer 
) primary index(顧客番号) 
; 

insert into jumbo.pivo values ('1','関東','8','8','3'); 
insert into jumbo.pivo values ('2','関東','9','1','1'); 
insert into jumbo.pivo values ('3','関西','6','2','6'); 
insert into jumbo.pivo values ('4','関西','7','0','8'); 
insert into jumbo.pivo values ('5','関東','8','0','8'); 
insert into jumbo.pivo values ('6','関西','8','6','9'); 
insert into jumbo.pivo values ('7','関西','3','0','1'); 
insert into jumbo.pivo values ('8','関西','0','9','8'); 
insert into jumbo.pivo values ('9','関東','5','3','9'); 
insert into jumbo.pivo values ('10','九州','5','6','4'); 

縦持ち(unpivot)

ご覧の通り横持ちされたデータで、商品の3列を縦持ちしたいとしましょう。3つ程度の列であればunionしても良いですが、数が多くなると大変です。まずはシステムテーブルから対象テーブルの列情報を取得します。

 /*unpivotSQL用の文字列取得*/ 
select
columnname||', ' as aa 
from dbc.columnsv
where databasename='jumbo'
and tablename='pivo'
; 

aaという列で取得したリストから、「縦に溶解したい列のみ」を、以下のSQLに貼り付けます。要はこの列が縦持ちの対象ですよということをここで指定します。また[売上数量]は、実際の数値データが縦持ちされて入る列であり、[商品]が元々は列名だった商品区分が入る列です。ご自身で利用される際は読み替えください。

 /*unpivot(縦持ち)処理*/ 			
 -- drop table jumbo.unpivo; 			
create table jumbo.unpivo as ( 			
	select * from jumbo.pivo unpivot ( 		
		売上数量 for 商品 in ( 	
			 /*aaの結果をここに、最後のカンマはとる*/ 
			商品a, 
			商品b, 
			商品c 
		) 	
	) a1 		
) with data 			
; 	

結果は以下のように縦持ちされます。保持する必要がなければcreate table とwith dataの行は不要です。


横持ち(pivot)

続いて、縦持ちされたデータを横持ちに戻します。以下がそのSQLです。

 /*pivot(横持ち)処理*/ 		
select 		
顧客番号, 		
地域, 		
 /*bbの結果をここに、最後のカンマはとる*/ 		
商品a_x as 商品a, 		
商品b_x as 商品b, 		
商品c_x as 商品c 		
from jumbo.unpivo pivot ( 		
	max(売上数量) as x for (	
	商品 	
	) in ( 	
		 /*ccの結果をここに、最後のカンマはとる*/ 
		 '商品a' as 商品a, 
		 '商品b' as 商品b, 
		 '商品c' as 商品c 
	)	
) a1 		
; 	

[max(売上数量) as x]と記載がある部分は集約関数を指定します。またカンマ区切りで集約関数を複数指定できます。そしてfor句の中にある[商品]は、横に展開したい列を指定します。in句の中には、[商品]の中の値に応じてどのように横展開するかを記述します。この結果が一番外側のSQL(a1の外側)に出力されるのですが、ここで気にしなければならない点が2つです。
1点目は、元テーブルにある売上数量と商品以外の入力列でgroup byされるということです。ここでは顧客番号と地域でgroup byされます。このデータでは発生してないですが、仮に顧客番号に対して複数の地域が存在していた場合、顧客番号と地域の組み合わせごとにデータは集約され、顧客番号から見た際には泣き別れになります。
そして2点目は、in句の中で指定した列名に、アンダースコア(_)、そしてmax(売上数量)のエイリアスとして指定したxがついて列名として出力されるということです。仮に集約関数を2つ指定していたら、_xと_yの列が3列ずつ作成されることになります。複数の集約関数を使う際には便利ですが、集約関数が1つの場合には_xが余計です。全部に対して列名を変更するのも、3つならいいですがたくさんあると手間です。

そのため、横持ちSQLを書く前に、まず元テーブルに対して以下のようなSQLを投げます。要は値のバリエーションに対して、ひと手間加えたリストを取得し、それを上述のSQLに貼り付けてあげています。このSQLで得られたbb列、cc列を上述のSQLに貼り付けることにより、割と手間をかけずにpivotのSQLを作成できます。

 /*pivotSQL用の文字列取得*/ 
select 
商品, 
商品||'_x as '||商品||', ' as bb,  
 ' '''||商品||''' as '||商品||', ' as cc 
from jumbo.unpivo 
group by 1,2,3 
order by 1 
; 

実行結果は冒頭の表と同じになるため、割愛します。より詳細な使い方はマニュアルを参照ください。

  • unpivot関数: 縦持ちにする

  • pivot関数: 横持ちにする

以上です。

#sql #teradata #pivot #unpivot

///

いいなと思ったら応援しよう!