説明: この記事は、PL/SQL 開発の初心者向けのガイドブックです。タグ
: パイプライン関数、パイプテーブル関数、テーブル関数、パイプ、パイプ ヒント: この記事に何か問題がある場合、またはもっと良い書き方がある場合は、変更と最適化のためにメッセージを残すかプライベートメッセージを送ってください
★用語の説明
テーブル・ファンクションを使用すると、SQLでPL/SQLファンクションを呼び出すことができます。
パイプライン・ファンクションは、パイプライン・テーブル・ファンクションとも呼ばれ、これもテーブル・ファンクションです。その動作特性は、実行中にPL/SQLセット・データを「即時に」返すことです。機能の操作。
★ 案例
SQL> INSERT INTO ティッカーテーブル (ティッカー, 価格_タイプ, 価格, 価格_日付)
SELECT ティッカー, 価格_タイプ, 価格, 価格_日付
FROM TABLE(stockpivot_pkg.pipe_stocks(CURSOR(SELECT * FROM Stocktable)));
★ワークフロー
*********************************
* · · · · *
* SQLパイプライン関数 · PL/ SQL ·
**********************************
_
★関連記事
『Oracle PL/SQLプログラミング 第6版』
★シナリオ説明
外部テーブル(stocktable)からデータを抽出し、データベーステーブル(tickertable)に挿入します。
★スクリプト説明
※環境準備:stockpivot_setup.sql
※実行方法:stockpivot_run.sql
※テストスクリプト:stockpivot_test.sql
※テスト結果:stockpivot_test.txt
※クリーンアップスクリプト:stockpivot_teardown.sql
★テスト内容
通常のPL/SQL
最適化PL/SQLは
パイプライン関数のみを使用 PL/SQLは
バルブコレクト最適化パイプライン関数を使用 PL/SQLは
バルブコレクト+パラレル最適化パイプライン関数を使用 PL/SQL
★主なオブジェクト
※load_stocks_legacy
型:ストアドプロシージャ
関数:従来のDMLを
一つ一つ実行するのに使用 時間のかかる:57.43秒
REDO:273,003K
※load_stocks_forall
型:ストアドプロシージャ
関数:Bukck+forallのパフォーマンステストに使用 時間のかかる
: 5.07秒
REDO:37,918K
※load_stocks
型:ストアドプロシージャ
関数:通常のパイプライン関数(pipe_stocks)の呼び出しに使用
所要時間:16.47秒
REDO:37,559K
※load_stocks_array
型:ストアドプロシージャ
関数:パイプライン関数(pipe_stocks_array)の呼び出しに使用) 配列を使用する)
消費時間: 6.26 秒
REDO: 37,550K
※load_stocks_Parallel
タイプ: ストアド プロシージャ
関数: 並列 + 配列を使用するパイプライン関数 (pipe_stocks_Parallel) の呼び出しに使用
消費時間: 3.41 秒
REDO: 25K
★ 関連情報
※ テーブル
CREATE TABLE Stocktable (外部表)
CREATE TABLEtickertable (ヒープ表)
※ SQL データ型
CREATE TYPE Stockpivot_ot AS OBJECT (オブジェクト型)
CREATE TYPE Stockpivot_ntt AS TABLE OF Stockpivot_ot (コレクション)
※ PL/SQL データ型
TYPEstocktable_rct IS REF CURSOR RETURNstocktable%ROWTYPE; (厳密に型指定された参照カーソル)
TYPE Stocktable_aat IS TABLE OFstocktable%ROWTYPE INDEX BY PLS_INTEGER; (コレクション)
SUBTYPE Stocktable_rt IS Stocktable%ROWTYPE; (カーソル)
TYPEtickertable_aat IS TABLE OF tinyertable %ROWTYPE INDEX BY PLS_INTEGER ; (コレクション)
SUBTYPEtickertable_rt IStickertable%ROWTYPE; (カーソル)
★実験データ(stockpivot_setup.sql)
CREATE OR REPLACE DIRECTORY dir AS '/home/oracle';
grant read,write on directory dir to scott;
grant dba to scott;
DECLARE
f UTL_FILE.FILE_TYPE := UTL_FILE.FOPEN('DIR', 'stocktable.dat', 'w');
BEGIN
FOR r IN (WITH opening_prices AS (
SELECT 'STK' || TO_CHAR(ROWNUM) AS ticker
, ROUND(DBMS_RANDOM.VALUE(0, 2000), 4) AS open_price
, SYSDATE-ABS(DBMS_RANDOM.VALUE(0,30)) AS trade_date
FROM dual
CONNECT BY ROWNUM <= &num_rows
)
SELECT ticker
, open_price
, ROUND(open_price * ABS(DBMS_RANDOM.VALUE(0.1,2)),4) AS close_price
, trade_date
FROM opening_prices)
LOOP
UTL_FILE.PUT_LINE(f, r.ticker || ',' || r.open_price || ',' ||
r.close_price || ',' || TO_CHAR(r.trade_date, 'DD/MM/YYYY'));
END LOOP;
UTL_FILE.FCLOSE(f);
END;
/
CREATE TABLE stocktable
( ticker VARCHAR2(10)
, open_price NUMBER
, close_price NUMBER
, trade_date DATE
)
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY dir
ACCESS PARAMETERS
(
RECORDS DELIMITED by NEWLINE
NOBADFILE
NOLOGFILE
NODISCARDFILE
FIELDS TERMINATED BY ','
( ticker
, open_price
, close_price
, trade_date CHAR(20) DATE_FORMAT DATE MASK "DD/MM/YYYY"
)
)
LOCATION ('stocktable.dat')
)
REJECT LIMIT UNLIMITED;
exec DBMS_STATS.GATHER_TABLE_STATS(USER, 'STOCKTABLE', estimate_percent=>NULL);
CREATE TABLE tickertable
( ticker VARCHAR2(10)
, price_type VARCHAR2(1)
, price NUMBER
, price_date DATE
);
CREATE TYPE stockpivot_ot AS OBJECT
( ticker VARCHAR2(10)
, price_type VARCHAR2(1)
, price NUMBER
, price_date DATE
);
/
CREATE TYPE stockpivot_ntt AS TABLE OF stockpivot_ot;
/
CREATE PACKAGE stockpivot_pkg AS
c_default_limit CONSTANT PLS_INTEGER := 100;
TYPE stocktable_rct IS REF CURSOR
RETURN stocktable%ROWTYPE;
TYPE stocktable_aat IS TABLE OF stocktable%ROWTYPE
INDEX BY PLS_INTEGER;
SUBTYPE stocktable_rt IS stocktable%ROWTYPE;
TYPE tickertable_aat IS TABLE OF tickertable%ROWTYPE
INDEX BY PLS_INTEGER;
SUBTYPE tickertable_rt IS tickertable%ROWTYPE;
PROCEDURE load_stocks_legacy;
PROCEDURE load_stocks_forall(
p_limit_size IN PLS_INTEGER DEFAULT stockpivot_pkg.c_default_limit
);
FUNCTION pipe_stocks(
p_source_data IN stockpivot_pkg.stocktable_rct
) RETURN stockpivot_ntt PIPELINED;
FUNCTION pipe_stocks_array(
p_source_data IN stockpivot_pkg.stocktable_rct,
p_limit_size IN PLS_INTEGER DEFAULT stockpivot_pkg.c_default_limit
) RETURN stockpivot_ntt PIPELINED;
FUNCTION pipe_stocks_parallel(
p_source_data IN stockpivot_pkg.stocktable_rct,
p_limit_size IN PLS_INTEGER DEFAULT stockpivot_pkg.c_default_limit
) RETURN stockpivot_ntt
PIPELINED
PARALLEL_ENABLE (PARTITION p_source_data BY ANY);
PROCEDURE load_stocks;
PROCEDURE load_stocks_array;
PROCEDURE load_stocks_parallel;
END stockpivot_pkg;
/
CREATE PACKAGE BODY stockpivot_pkg AS
-----------------------------------------------------------------------
PROCEDURE load_stocks_legacy IS
CURSOR c_source_data IS
SELECT ticker, open_price, close_price, trade_date
FROM stocktable;
r_source_data stockpivot_pkg.stocktable_rt;
r_target_data stockpivot_pkg.tickertable_rt;
BEGIN
OPEN c_source_data;
LOOP
FETCH c_source_data INTO r_source_data;
EXIT WHEN c_source_data%NOTFOUND;
/* Opening price... */
r_target_data.ticker := r_source_data.ticker;
r_target_data.price_type := 'O';
r_target_data.price := r_source_data.open_price;
r_target_data.price_date := r_source_data.trade_date;
INSERT INTO tickertable VALUES r_target_data;
/* Closing price... */
r_target_data.price_type := 'C';
r_target_data.price := r_source_data.close_price;
INSERT INTO tickertable VALUES r_target_data;
END LOOP;
DBMS_OUTPUT.PUT_LINE(
c_source_data%ROWCOUNT * 2 || ' rows inserted.' );
CLOSE c_source_data;
END load_stocks_legacy;
-----------------------------------------------------------------------
FUNCTION pipe_stocks(
p_source_data IN stockpivot_pkg.stocktable_rct
) RETURN stockpivot_ntt PIPELINED IS
r_target_data stockpivot_ot := stockpivot_ot(NULL, NULL, NULL, NULL);
r_source_data stockpivot_pkg.stocktable_rt;
BEGIN
LOOP
FETCH p_source_data INTO r_source_data;
EXIT WHEN p_source_data%NOTFOUND;
/* First row... */
r_target_data.ticker := r_source_data.ticker;
r_target_data.price_type := 'O';
r_target_data.price := r_source_data.open_price;
r_target_data.price_date := r_source_data.trade_date;
PIPE ROW (r_target_data);
/* Second row... */
r_target_data.price_type := 'C';
r_target_data.price := r_source_data.close_price;
PIPE ROW (r_target_data);
END LOOP;
CLOSE p_source_data;
RETURN;
END pipe_stocks;
-----------------------------------------------------------------------
FUNCTION pipe_stocks_array(
p_source_data IN stockpivot_pkg.stocktable_rct,
p_limit_size IN PLS_INTEGER DEFAULT stockpivot_pkg.c_default_limit
) RETURN stockpivot_ntt PIPELINED IS
r_target_data stockpivot_ot := stockpivot_ot(NULL, NULL, NULL, NULL);
aa_source_data stockpivot_pkg.stocktable_aat;
BEGIN
LOOP
FETCH p_source_data BULK COLLECT INTO aa_source_data LIMIT p_limit_size;
EXIT WHEN aa_source_data.COUNT = 0;
/* Process the batch of (p_limit_size) records... */
FOR i IN 1 .. aa_source_data.COUNT LOOP
/* First row... */
r_target_data.ticker := aa_source_data(i).ticker;
r_target_data.price_type := 'O';
r_target_data.price := aa_source_data(i).open_price;
r_target_data.price_date := aa_source_data(i).trade_date;
PIPE ROW (r_target_data);
/* Second row... */
r_target_data.price_type := 'C';
r_target_data.price := aa_source_data(i).close_price;
PIPE ROW (r_target_data);
END LOOP;
END LOOP;
CLOSE p_source_data;
RETURN;
END pipe_stocks_array;
-----------------------------------------------------------------------
FUNCTION pipe_stocks_parallel(
p_source_data IN stockpivot_pkg.stocktable_rct,
p_limit_size IN PLS_INTEGER DEFAULT stockpivot_pkg.c_default_limit
) RETURN stockpivot_ntt
PIPELINED
PARALLEL_ENABLE (PARTITION p_source_data BY ANY) IS
r_target_data stockpivot_ot := stockpivot_ot(NULL, NULL, NULL, NULL);
aa_source_data stockpivot_pkg.stocktable_aat;
BEGIN
LOOP
FETCH p_source_data BULK COLLECT INTO aa_source_data LIMIT p_limit_size;
EXIT WHEN aa_source_data.COUNT = 0;
/* Process the batch of (p_limit_size) records... */
FOR i IN 1 .. aa_source_data.COUNT LOOP
/* First row... */
r_target_data.ticker := aa_source_data(i).ticker;
r_target_data.price_type := 'O';
r_target_data.price := aa_source_data(i).open_price;
r_target_data.price_date := aa_source_data(i).trade_date;
PIPE ROW (r_target_data);
/* Second row... */
r_target_data.price_type := 'C';
r_target_data.price := aa_source_data(i).close_price;
PIPE ROW (r_target_data);
END LOOP;
END LOOP;
CLOSE p_source_data;
RETURN;
END pipe_stocks_parallel;
-----------------------------------------------------------------------
PROCEDURE load_stocks IS
BEGIN
INSERT INTO tickertable (ticker, price_type, price, price_date)
SELECT ticker, price_type, price, price_date
FROM TABLE(
stockpivot_pkg.pipe_stocks(
CURSOR(SELECT * FROM stocktable)));
DBMS_OUTPUT.PUT_LINE( SQL%ROWCOUNT || ' rows inserted.' );
END load_stocks;
-----------------------------------------------------------------------
PROCEDURE load_stocks_array IS
BEGIN
INSERT INTO tickertable (ticker, price_type, price, price_date)
SELECT ticker, price_type, price, price_date
FROM TABLE(
stockpivot_pkg.pipe_stocks_array(
CURSOR(SELECT * FROM stocktable)));
DBMS_OUTPUT.PUT_LINE( SQL%ROWCOUNT || ' rows inserted.' );
END load_stocks_array;
-----------------------------------------------------------------------
PROCEDURE load_stocks_parallel IS
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION ENABLE PARALLEL DML';
INSERT /*+ PARALLEL(t, 4) */ INTO tickertable t
(ticker, price_type, price, price_date)
SELECT ticker, price_type, price, price_date
FROM TABLE(
stockpivot_pkg.pipe_stocks_parallel(
CURSOR(SELECT /*+ PARALLEL(s, 4) */ * FROM stocktable s)));
DBMS_OUTPUT.PUT_LINE( SQL%ROWCOUNT || ' rows inserted.' );
END load_stocks_parallel;
-----------------------------------------------------------------------
PROCEDURE load_stocks_forall(
p_limit_size IN PLS_INTEGER DEFAULT stockpivot_pkg.c_default_limit
) IS
CURSOR c_source_data IS
SELECT ticker, open_price, close_price, trade_date
FROM stocktable;
aa_source_data stockpivot_pkg.stocktable_aat;
aa_target_data stockpivot_pkg.tickertable_aat;
v_indx PLS_INTEGER;
v_rowcount PLS_INTEGER := 0;
BEGIN
OPEN c_source_data;
LOOP
FETCH c_source_data BULK COLLECT INTO aa_source_data LIMIT p_limit_size;
EXIT WHEN aa_source_data.COUNT = 0;
aa_target_data.DELETE;
FOR i IN 1 .. aa_source_data.COUNT LOOP
/* Opening price... */
v_indx := aa_target_data.COUNT + 1;
aa_target_data(v_indx).ticker := aa_source_data(i).ticker;
aa_target_data(v_indx).price_type := 'O';
aa_target_data(v_indx).price := aa_source_data(i).open_price;
aa_target_data(v_indx).price_date := aa_source_data(i).trade_date;
/* Closing price... */
v_indx := aa_target_data.COUNT + 1;
aa_target_data(v_indx).ticker := aa_source_data(i).ticker;
aa_target_data(v_indx).price_type := 'C';
aa_target_data(v_indx).price := aa_source_data(i).close_price;
aa_target_data(v_indx).price_date := aa_source_data(i).trade_date;
END LOOP;
FORALL i IN INDICES OF aa_target_data
INSERT INTO tickertable
VALUES aa_target_data(i);
v_rowcount := v_rowcount + SQL%ROWCOUNT;
END LOOP;
DBMS_OUTPUT.PUT_LINE( v_rowcount || ' rows inserted.' );
CLOSE c_source_data;
END load_stocks_forall;
END stockpivot_pkg;
/
※記事がうまく書かれていると思われる場合は、記事の最後に著者に親指を立てることを忘れないでください〜
以上