スポンサーリンク

【PostgreSQL】Function / Procedure【書き方】【まとめ】

スポンサーリンク

FunctionとProcedureの違い

Function:返り値有り

Procedure:返り値無し

だけです.構文はLanguageに何を指定するかで変わります.

LanguageのSQLとPL/pgSQLの違い

SQL

通常のSQL構文です.制御構文はCASE程度しか使用できないため,制御上は簡便な処理しか記述できません.

languageとしてSQLを指定する利用用途としては,非常に長く煩雑なjoin操作を保存しておき,アプリケーション側でのクエリ組み立てを容易にすること等が想定されます.

重要な利点としては通常のSQLで記載されているので,他DBへ容易に移行できる互換性の高さが挙げられます.

PL/pgSQL

PostgreSQL独自の制御構文をSQLに加えて使用することができ,条件分岐・繰り返し等の制御構文を用いて,複雑な処理を記述可能です.

公式サイトでも利点として挙げられていますが,ClientとDBが同一デバイスに存在しない場合,PL/pgSQLを利用することでネットワーク負荷・応答性を向上することができます.

理由としては,結果を導出するにあたって,DBに対して複数回クエリを投げる必要がある場合,その回数分のネットワークラウンドトリップのレイテンシが発生しますが,Functionを用いた場合,一度の呼び出しで完結するためです.

Chapter 43. PL/pgSQL — SQL Procedural Language
Chapter43.PL/pgSQL — SQL Procedural Language Table of Contents 43.1. Overview 43.1.1. Advantages of Using PL/pgSQL 43.1.2. Supported Argument and Result …

欠点としては,PostgreSQL特有の構文であるため,他DBへの移植性が悪いということです.

とはいってもOracleのPL / SQLをならって記載されているため,多少の手直しで簡単に動作するようにはなります.

ちなみにPLはprocudure languageの略称で手続き型の処理を記述するための言語を指します.

Function – SQL

上記通り,複雑な処理をSQLで記載することはあまりないので,簡単に記載します

足し算をするFunction

様々な書き方が存在し,下記は全て同一のFunctionです.

create or replace function add(a integer, b integer) returns integer
as $$
    select (a+b);
$$ language sql;

create or replace function add(a integer, b integer) returns integer
as 'select (a+b)' language sql;

create or replace function add(a integer, b integer) returns integer
language sql
return a + b;

create or replace function add(integer, integer) returns integer
as $$
select ($1+$2);
$$ language sql;

create or replace function add(integer, integer) returns integer
language sql
return ($1+$2);

create or replace function xx()

既に同一の関数が存在する場合は置き換え(オーバーロード),なければ通常通り作成します.

なお,PostgreSQLでは引数の型が違えば異なる関数として認識されます.

CREATE FUNCTION
CREATE FUNCTION CREATE FUNCTION — define a new function Synopsis CREATE FUNCTION name ( [ [ …

引数名

引数名は省略することが可能です.省略した場合は,定義の順番に応じて$1, $2…のようにアクセスことができます.

returns 返り値の型

返り値の型を指定します.

返り値

sql body内で返り値を決定します.あるいは最後尾にreturn句を使用することでそこに記載することも可能です.

単純な処理であればreturn句を用いた方がシンプルになります.ただし,このように様々な書き方が可能な場合にはプロジェクトでコーディング規約に明記し,統一した記法を採用した方がいいでしょう.

Function – PL/pgSQL

足し算をするFunction

PL / pgSQLのFunctionでは,bodyをbegin ~ end; で囲みます.

また,SQLで使用可能であった行末でのreturn句を使用することができず,必ずbody内部でreturnを指定します.

また,関数の引数にoutを指定することでも返り値を指定することができ,この場合はreturns句は指定できません.end;に達した点でその値に代入された値が返り値となります.

-- OK
create or replace function add(a integer, b integer) returns integer
as $$
    begin
        return (a+b);
    end;
$$ language plpgsql;

-- OK
create or replace function add(integer, integer) returns integer
as $$
    begin
        return ($1+$2);
    end;
$$ language plpgsql;

-- NG !!!!!!!
create or replace function add(integer, integer) returns integer
language plpgsql
return ($1+$2);

-- OK
create or replace function add(a integer, b integer, out sum integer)
as $$
begin
    sum := a+b;
end;
$$ language plpgsql;

PL / pgSQLではこの他にも様々な機能を利用することが可能です.

変数宣言(declare)

単一値を受ける

例えばsomeTableに保存されているあるカラムcolumn1のmax値を利用した計算を行いたい場合は以下のように指定できます.

create or replace function add(a integer, b integer) returns integer
as $$
    declare
        c integer := (select max(column1) from someTable);
    begin
        return (a+b+c);
    end;
$$ language plpgsql;

複数値を受ける(record / rowtype)

複数値を受けたい場合はrecordを指定します.なおrecordは厳密には形式は決まっておらず,任意の数のカラムを受けることが可能な単純なプレースホルダーです.受ける値がテーブル定義と同義であればrowtypeを指定した受けた方が安全です.

値の代入は結果がFunctionの返り値であれば初期化時に代入できますが、select文(サブクエリ)を発行する必要がある場合にはselect intoをbody内で使用します.

-- recordを用いた例
create or replace function add(a integer, b integer) returns integer
as $$
declare
    row record;
  row2 record := some_func() -- some_funcが複数値を返す関数の場合、初期化時に代入可能
    c integer;
    d integer;
begin
    select * into row from someTable limit 1;
    c = row.column1;
    d = row.column2;
    return (a+b+c+d);
end;
$$ language plpgsql;

受ける方が決まっている場合にはrowTypeを使用する.

-- rowtypeを用いた例
create or replace function add(a integer, b integer) returns integer
as $$
declare
    row someTable%rowtype;
    c integer;
    d integer;
begin
    select * into row from someTable limit 1;
    c = row.column1;
    d = row.column2;
    return (a+b+c+d);
end;
$$ language plpgsql;

なお,サブクエリは複数カラムを返すことができませんので,宣言時にselectを用いて初期化することはできません.以下のような定義にすると

ERROR: subquery must return only one column

といったエラーが発生します.

-- NG例
create or replace function add10(a integer, b integer) returns integer
as $$
declare
    row record := (select * from someTable limit 1); -- NG!!!!!!
    c integer = row.column1;
    d integer = row.column2;
begin
    return (a+b+c+d);
end;
$$ language plpgsql;

条件(if – else)

if – elseは以下のような構文です.a, bの値を閾値で補正している例です.
良く忘れるのはend if; なので注意が必要です.

create or replace function add(a integer, b integer) returns integer
as $$
begin
    if a < 0
        then a = 0;
    end if;

    if b < 0
        then b = 0;
    elseif b > 100
        then b = 100;
    else
        b = b;
    end if;

    return (a+b);
end;
$$ language plpgsql;

繰り返し(for)

標準的には以下のように使用します.掛け算を足し算で表現した例です.

create or replace function multiply(a integer, b integer) returns integer
as $$
    declare
        sum integer := 0;
begin
    for i in 1..b loop
        sum = sum + a;
    end loop;

    return sum;
end;
$$ language plpgsql;

一般的なContinue(次のイテレーションに進む)やExit(for分終了)を挟むこともできます.

create or replace function multiply(a integer, b integer) returns integer
as $$
declare
    sum integer := 0;
begin
    for i in 1..b loop
        if i = 3 then continue; end if;
        if i = 10 then exit; end if;
        sum = sum + a;
    end loop;

    return sum;
end;
$$ language plpgsql;

代入(:= or =)

代入は実は:=でも=でもどちらでいいです.if文でbooleanが求められる場合には等値のオペレーターとして認識されます.誤解を招かないように:=で統一するコーディング規則としたほうがいい可能性はあります.

複数値(行)を返す

複数値を返す方法は複数あります。

単一行の場合は、return {expression};を使用します。

複数行が必要な場合setofを追加し、return next {expression} や return query {query}。

返り値のtableを定義する

複数値を返したい場合は以下のようにtableを定義します.新しくテーブルを定義した場合は複数行を返す場合でもsetofは必要ありません。

create or replace function makeRow(a integer, b integer)
returns table(
    value1 integer,
    value2 integer
             )
as $$
begin
    value1 = a;
    value2 = b;
    return next;
end;
$$ language plpgsql;

recordで返す

複数値を受ける章でも説明しましたが、recordは型が決まっていない任意のテーブル定義として利用することが可能です。 複数行返したい場合はsetofを追加します。

-- 1
create or replace function makeRow(a integer, b integer)
    returns record
as $$
begin
    return (a,b);
end;
$$ language plpgsql;

-- 2. out parameterを使用した場合
create or replace function makeRow(a integer, b integer, out value1 integer, out value2 integer)
returns record
as $$
begin
    value1 = a;
    value2 = b;
    return;
end;
$$ language plpgsql;

1番目の場合、使用側ではテーブル定義を手動で導入する必要があります。

-- OK 定義あり
select * from makeRow(1,2) as (s integer, ss integer);
-- NG 定義なし
select * from makeRow(1,2);

定義なしの場合、以下のようなエラーが発生します。

ERROR: a column definition list is required for functions returning “record”

既存のテーブル定義を利用する

また、既存のテーブル定義を利用することも可能です。複数行返却する場合にはsetofを追加する必要があります。

create or replace function makeRow(a integer, b integer)
returns someTable
as $$
begin
    return (a, b);
end;
$$ language plpgsql;

return next / return query

ともに終端演算子ではありません。結果に対して実行した行を追加します。この文に差し掛かってもFunctionは継続されます。Functionが終了するのはreturn;、あるいは最後に辿り着いた時です。

return next

return nextは2通りの使用方法があります。

return next;は返り値としてテーブルを定義した場合にのみ使用可能です。それまでにアサインされた値をテーブル行として返します。アサインできていない列にはnullが入ります。

create or replace function makeRow(a integer, b integer)
    returns table(
                     value1 integer,
                     value2 integer
                 )
as $$
begin
    value1 = a;
    return next;

    value1 = b;
    value2 = a;
    return next;
end;
$$ language plpgsql;

select * from makeRow(1,2);

return nextは通常expressionを伴って使用されます。以下のような使用例が一般的です。

create or replace function makeRow(a integer, b integer)
    returns setof someTable
as $$
    declare r record;
begin
    return next (b,a);
    return next (a,b);
    for r in (select * from someTable) loop
        return next r;
    end loop;
end;
$$ language plpgsql;

select * from makeRow(1,2);

someTableは単純に以下のような適当なテーブルです。

return query

クエリ結果をまとめて追加したい場合は、return queryを使用します。

create or replace function makeRow(a integer, b integer)
    returns setof someTable
as $$
begin
    return query select * from someTable;
end;
$$ language plpgsql;

ログを残す(raise)

Function / Procedure作成中にはコンソールでログを残したくなるはずです。raiseを使用することで実現できます。

create or replace function makeRow(a integer, b integer)
    returns someTable
as $$
begin
    raise info 'input parameter a=%,b=%', a, b;
    return (a,b);
end;
$$ language plpgsql;

levelは以下から選べます。

DEBUG LOG INFO NOTICE WARNING EXCEPTION
デフォルト

なお、デフォルトのEXCEPTIONのままだとこの時点でEXCEPTIONがThrowされて実行が中断されてしまいますので、適切なレベルを設定する必要があります。

なお、%はプレースホルダーで、記載順で置き換えられていきます。

select * from makeRow(1,2);

-- console画面
input parameter a=1,b=2
タイトルとURLをコピーしました