1. pl/pgsqlでfunction定義をしてみる

pl/pgsqlでfunctionをつくってダミーデータ挿入を省力化したかったので色々調べてみたまとめ

1.1. こんな動機

ダミーデータをINSERTがめんどくさい

エクセルやマクロでしこしこ作るのもなんかなぁ

→ pgsqlでfunction作ってあげると必要に応じて呼び出すだけでよいので それをライブラリ化したらワンライナーみたいに便利になりそう…!

1.2. 試行錯誤の流れ

1.2.1. ベースとなるINSERT文を生成する

一先ず作成。

-- insert
INSERT INTO log (log_id, task_id, log_decl, log_real)
VALUES (
    (SELECT CASE WHEN 'L' ||  MAX(TO_NUMBER(SUBSTRING(log_id,2,255),'999999999'))+1 ISNULL THEN 'L1'
            ELSE  'L' ||  MAX(TO_NUMBER(SUBSTRING(log_id,2,255),'999999999'))+1 END
            FROM log),
    'T2',
    3,
    3
  );

ほんでもってこれについて、
任意の回数、任意のtask_id, log_decl, log_realを指定して複数回INSERTしたい。 この時、このSQL文は1クエリにつき、1つの’LXXX’というルールのlog_idを払いだしてくれる。

ここで、
’LXXX’~’LXXX+100’なクエリを作りたいと考えたら、 このクエリがその時の最大値+1の番号を払いだそうとするため 1クエリ1コミットで100回分回さないといけない。

つまりあるテーブルの状態から100行同時生成するのではなく ループで100回実施する形にしなければならない

そこで、こいつを関数化する。pl/pgsqlはこういうニーズに対応するのが得意らしい。

1.3. 完成品


CREATE OR REPLACE FUNCTION func_addlogs(arg_records int, arg_taskid VARCHAR(255) ,arg_decl int , arg_real int) RETURNS bool
AS $$
    DECLARE
        num1 int;
    BEGIN
        num1:=arg_records;
        FOR a IN 1..num1 LOOP
            EXECUTE
                'INSERT INTO log (log_id, task_id, log_decl, log_real)
                VALUES (
                    (SELECT CASE WHEN ''L'' ||  MAX(TO_NUMBER(SUBSTRING(log_id,2,255),''999999999''))+1 ISNULL THEN ''L1''
                            ELSE  ''L'' ||  MAX(TO_NUMBER(SUBSTRING(log_id,2,255),''999999999''))+1 END
                            FROM log),''' ||
                    arg_taskid ||
                    ''',' ||
                    arg_decl ||
                    ',' ||
                    arg_real ||
                 ');'
            ;
        END LOOP;
        RETURN true;
    END;
$$ LANGUAGE plpgsql;

1.3.1. 知りし事などいろいろ

1.3.1.1. CREATE OR REPLACE FUNCTION (引数) RETURN 戻り値

CREATE OR REPLACE FUNCTION で新たな関数の定義か更新が可能。 javaの様にシグネチャで判別されるので、関数名が同じでも引数の型、戻り値の型の異なる関数は別もの扱いらしい

そこら辺を変えたい場合はDROPして再度宣言しなおす必要がある。

RETURN 戻り値 でこの関数実行時の戻り値規定。
今回は通ったかどうか知りたかったのでとりあえずbool返させた。

1.3.1.2. AS $$ ~ $$

関数を定義する文字列定数。をここに置く。 $$ ~ $$はシングルクオーテーションを全体的にエスケープするため。 この中では普通に意識せず「’」と打ってよくなる。

1.3.1.3. DECLARE

変数定義をするところ。 今回はループ回数を制御するためのカウンタ変数の上限値の役割として
num1 int;
を用意した。

1.3.1.4. BEGIN

処理を記述するところ

今回の目的は
用意したクエリに制御構造を与えて便利に使いまわす
というようにも言い換えられると思う

ここは、その制御を与える場面。

1.3.1.4.1. num1:=arg_records

変数定義。与えられた引数はarg_recordsの様に呼び出せる。

定義時には:=を使う、すうがくみたい。(小並感)

1.3.1.4.2. FOR a IN 1..num1 LOOP END LOOP

いわゆるFOR文。1~num1までループさせる。

1.3.1.5. EXECUTE

ここに記述したsqlを実行する ここでは文字列として定義されるので、与えた引数を適用させるために 一度切って || で文字列結合する。

1.3.1.6. RETURN true

返り値があればここに

1.3.1.7. END

おわり

1.3.1.8. LANGUAGE plpgsql

この関数を実装している言語をここに記述する

1.3.1.9. DROP

この関数がいらなくなった時や定義しなおしたくなったときに使った

    --function DROP
    DROP FUNCTION func_addlogs(int,VARCHAR(255),int,int);

DROP FUNCTION 関数名(引数…)

1.3.1.10. 実行する

こんな感じで実行

    SELECT func_addlogs(100,'T2',2,2);

1.4. 感想とか課題

初めて触れたけどもデータベースに対して検証したいときとか
今回みたいなダミーデータの挿入とかを行いたい時に
さらっとかけるといいなぁと感じた