LoginSignup
2
3

More than 5 years have passed since last update.

postgresqlのレコード型変数のNULLチェック

Posted at

postgresql 9.6で確認。

postgresqlの関数で、レコード型の変数を用意して、
そのレコード型変数自体を、IS NULLと、IS NOT NULLで判断したとき、
どうなるか調べてみた。

結果、以下のような判断となった。
・IS NULLとした場合、レコードが持つ列すべてがNULLだったら真、それ以外だったら偽(1つでも値がNULLでなければ偽)
・IS NOT NULLとした場合、レコードが持つ列すべてが非NULLだったら真、それ以外だったら偽(1つでも値がNULLだったら偽)

また、NULLのチェックをするときに、レコード型の変数自体がNULLだった場合、エラーが発生する。
レコード型が持っている列が確定していないと、チェックができない、ということらしい。

SELECT INTOで代入しようとしたとき、SELECTの結果が1行もない場合は、
レコード型変数の構造自体は決定するが、列すべてはNULL値となっている。
なので、レコード型変数自体にNULLを代入したときのようなエラーは発生しない。

CREATE OR REPLACE FUNCTION public.test_record()
  RETURNS integer AS
$BODY$
DECLARE
    rec RECORD;
BEGIN
    -- 何も代入していないレコード型変数に対して、IS NULLを見ると、以下のエラーが発生する。
    -- ERROR:  record "rec" is not assigned yet
    -- DETAIL:  The tuple structure of a not-yet-assigned record is indeterminate.
/*
    IF rec IS NULL THEN
        RAISE INFO 'rec is null';
    ELSE
        RAISE INFO 'rec is not null';
    END IF;
*/

    -- 2つのカラムを持つレコードを代入。うち、1つのカラムはNULLとした。
    SELECT INTO rec
         'test' as name
        ,NULL as val
    WHERE
        true
    ;

    RAISE INFO 'rec.name = %, rec.val = %', rec.name, rec.val;

    -- この結果は偽となる。
    -- rec.nameが非NULLなので。
    IF rec IS NULL THEN
        RAISE INFO 'rec IS NULL -> true';
    ELSE
        RAISE INFO 'rec IS NULL -> false';
    END IF;

    -- この結果は偽となる。(自分は、この結果で少しハマった。レコードあるのに、NOT NULLが偽?、、という具合に)
    -- rec.valがNULLなので。
    IF rec IS NOT NULL THEN
        RAISE INFO 'rec IS NOT NULL -> true';
    ELSE
        RAISE INFO 'rec IS NOT NULL -> false';
    END IF;

    -- 先ほど代入したカラムのうち、NULLではなかったカラムにNULLを代入。
    rec.name := NULL;

    RAISE INFO 'rec.name = %, rec.val = %', rec.name, rec.val;

    -- この結果は真となる。
    -- すべての列がNULLなので。
    IF rec IS NULL THEN
        RAISE INFO 'rec IS NULL -> true';
    ELSE
        RAISE INFO 'rec IS NULL -> false';
    END IF;

    -- この結果は偽となる。
    -- すべての列がNULLなので。
    IF rec IS NOT NULL THEN
        RAISE INFO 'rec IS NOT NULL -> true';
    ELSE
        RAISE INFO 'rec IS NOT NULL -> false';
    END IF;

    -- 両方のカラムに値を代入
    rec.name := 'test';
    rec.val := '1';

    RAISE INFO 'rec.name = %, rec.val = %', rec.name, rec.val;

    -- この結果は偽となる。
    -- すべての列が非NULLなので。
    IF rec IS NULL THEN
        RAISE INFO 'rec IS NULL -> true';
    ELSE
        RAISE INFO 'rec IS NULL -> false';
    END IF;

    -- この結果は真となる。
    -- すべての列が非NULLなので。
    IF rec IS NOT NULL THEN
        RAISE INFO 'rec IS NOT NULL -> true';
    ELSE
        RAISE INFO 'rec IS NOT NULL -> false';
    END IF;

    -- レコード型変数自体に、NULLを代入。
    rec := NULL;

    -- レコード型変数に対してNULLを代入してからIS NULLを見ると、以下のエラーが発生する。
    -- ERROR:  record "rec" is not assigned yet
    -- DETAIL:  The tuple structure of a not-yet-assigned record is indeterminate.
/*
    IF rec IS NULL THEN
        RAISE INFO 'rec is null';
    ELSE
        RAISE INFO 'rec is not null';
    END IF;
*/

    -- 2つのカラムを持つ問い合わせで、結果が0行だった、という結果を代入する。
    SELECT INTO rec
         'test' as name
        ,NULL as val
    WHERE
        false
    ;

    RAISE INFO 'rec.name = %, rec.val = %', rec.name, rec.val;

    -- この結果は真となる。
    -- すべての列がNULLなので。
    IF rec IS NULL THEN
        RAISE INFO 'rec IS NULL -> true';
    ELSE
        RAISE INFO 'rec IS NULL -> false';
    END IF;

    -- この結果は偽となる。
    -- すべての列がNULLなので。
    IF rec IS NOT NULL THEN
        RAISE INFO 'rec IS NOT NULL -> true';
    ELSE
        RAISE INFO 'rec IS NOT NULL -> false';
    END IF;

    RETURN 0;
END
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

ログの出力結果

< 2017-09-22 12:31:53.503 JST > INFO:  rec.name = test, rec.val = <NULL>
< 2017-09-22 12:31:53.503 JST > INFO:  rec IS NULL -> false
< 2017-09-22 12:31:53.503 JST > INFO:  rec IS NOT NULL -> false
< 2017-09-22 12:31:53.503 JST > INFO:  rec.name = <NULL>, rec.val = <NULL>
< 2017-09-22 12:31:53.504 JST > INFO:  rec IS NULL -> true
< 2017-09-22 12:31:53.504 JST > INFO:  rec IS NOT NULL -> false
< 2017-09-22 12:31:53.504 JST > INFO:  rec.name = test, rec.val = 1
< 2017-09-22 12:31:53.504 JST > INFO:  rec IS NULL -> false
< 2017-09-22 12:31:53.504 JST > INFO:  rec IS NOT NULL -> true
< 2017-09-22 12:31:53.505 JST > INFO:  rec.name = <NULL>, rec.val = <NULL>
< 2017-09-22 12:31:53.505 JST > INFO:  rec IS NULL -> true
< 2017-09-22 12:31:53.505 JST > INFO:  rec IS NOT NULL -> false
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3