【Oracle PL/SQL】縦持ちのデータを横持ちに変換する表関数
ただし、帳票などにデータを展開したい場合、これを横に持った形に整形して出したい時がたまにあります。
例えば、こんな感じの発注先の発注が受けられる日と配送できる日のカレンダーを格納したテーブルがあったとします。
発注日 | 発注可能(0:不可、1:可能) | 配送可能(0:不可、1:可能) |
2019/07/01 | 1 | 1 |
2019/07/02 | 1 | 0 |
2019/07/03 | 1 | 1 |
2019/07/04 | 1 | 0 |
2019/07/05 | 1 | 1 |
2019/07/06 | 0 | 1 |
2019/07/07 | 0 | 1 |
発注日 | 2019/07/01 | 2019/07/02 | 2019/07/03 | 2019/07/04 | 2019/07/05 | 2019/07/06 | 2019/07/07 |
発注可能 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
配送可能 | 1 | 0 | 1 | 0 | 1 | 1 | 1 |
これをOracleのPL/SQL表関数で実現してみるのが今回の趣旨です。
実行環境はブラウザでクラウド環境のOracle Databeseが実行できるOracle Live SQLを使います。
データの準備
まずはテーブルとデータを用意します。-- 発注カレンダー CREATE TABLE order_calendar ( order_date VARCHAR2(10) NOT NULL, is_orderable NUMBER(1) NOT NULL, is_deliverable NUMBER(1) NOT NULL, CONSTRAINT pk_order_calendar PRIMARY KEY(order_date) ); -- 1ヵ月分のデータ INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/01', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/02', 1, 0); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/03', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/04', 1, 0); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/05', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/06', 0, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/07', 0, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/08', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/09', 1, 0); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/10', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/11', 1, 0); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/12', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/13', 0, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/14', 0, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/15', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/16', 1, 0); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/17', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/18', 1, 0); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/19', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/20', 0, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/21', 0, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/22', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/23', 1, 0); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/24', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/25', 1, 0); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/26', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/27', 0, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/28', 0, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/29', 1, 1); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/30', 1, 0); INSERT INTO order_calendar( order_date, is_orderable, is_deliverable)VALUES( '2019/07/31', 1, 1);
表関数の定義
ストアドパッケージを使います。まずはパッケージ仕様部。
CREATE OR REPLACE PACKAGE pkg_order_calendar IS -- レコード TYPE rec_cal IS RECORD( col1 VARCHAR2(10) ,col2 VARCHAR2(10) ,col3 VARCHAR2(10) ,col4 VARCHAR2(10) ,col5 VARCHAR2(10) ,col6 VARCHAR2(10) ,col7 VARCHAR2(10) ); -- ネスト表 TYPE tbl_cal IS TABLE OF rec_cal; -- 表関数 FUNCTION get_cal( base_date IN order_calendar.order_date%TYPE -- 基準日 ) RETURN tbl_cal PIPELINED; END pkg_order_calendar;次にパッケージ本体です。
CREATE OR REPLACE PACKAGE BODY pkg_order_calendar IS -- 表関数 FUNCTION get_cal( base_date IN order_calendar.order_date%TYPE -- 基準日 ) RETURN tbl_cal PIPELINED IS v_rec_cal rec_cal; v_to_date order_calendar.order_date%TYPE; v_col_count PLS_INTEGER; v_set_col VARCHAR2(10); BEGIN v_to_date := TO_CHAR(TO_DATE(base_date ,'yyyy/mm/dd') + 6, 'yyyy/mm/dd'); FOR i IN 1..3 LOOP v_rec_cal := NULL; -- レコードクリア v_col_count := 0; FOR v_rec_order_calendar IN( SELECT * FROM order_calendar WHERE order_date BETWEEN base_date AND v_to_date ORDER BY order_date ) LOOP CASE i WHEN 1 THEN v_set_col := v_rec_order_calendar.order_date; WHEN 2 THEN v_set_col := v_rec_order_calendar.is_orderable; WHEN 3 THEN v_set_col := v_rec_order_calendar.is_deliverable; END CASE; v_col_count := v_col_count + 1; CASE v_col_count WHEN 1 THEN v_rec_cal.col1 := v_set_col; WHEN 2 THEN v_rec_cal.col2 := v_set_col; WHEN 3 THEN v_rec_cal.col3 := v_set_col; WHEN 4 THEN v_rec_cal.col4 := v_set_col; WHEN 5 THEN v_rec_cal.col5 := v_set_col; WHEN 6 THEN v_rec_cal.col6 := v_set_col; WHEN 7 THEN v_rec_cal.col7 := v_set_col; END CASE; END LOOP; PIPE ROW(v_rec_cal); -- 1レコード返す END LOOP; END get_cal; END pkg_order_calendar;実行してみます
select * from pkg_order_calendar.get_cal('2019/07/10');縦持ちが横持ちに変換できました。
書いてみると相当無駄なコードですね。
列数×行数分クエリ発行してますし…。
まあ、例え効率が悪いとわかっててもやらなきゃいけないときって往々にしてあるのでこれも一つの実装例だと思って頂ければ…。
Web屋には嫌われがちなOracleですが、使ってての安心感と強力なストアドはやっぱり魅力的です。
複雑で高額なライセンス価格さえ何とかなればなあ…。
ディスカッション
コメント一覧
まだ、コメントがありません