0

状況:

fn_SetFoo()table にレコードを挿入する関数がありますTableFoo

に挿入するたびに実行されるトリガー関数もありますTableFooTableFooID新しく挿入された行から新しい主キーを取得し、それを 2 番目のテーブルTableFooBarに (外部キー制約を使用して) 挿入します。

実行するトリガーを作成しましたAFTER INSERT ON TableFoo FOR EACH ROW EXECUTE PROCEDURE fn_SetFooBar();

fn_SetFoo()直接呼び出すと、すべてが期待どおりに機能します。

ただし、fn_NormalizeFoo()一部のデータを処理し、処理fn_SetFoo()した各レコードを呼び出す別の関数があります。

呼び出すとfn_NormalizeFoo()、最初のレコードのみが処理され、関数が停止します。


質問:

の内容が直接実行されfn_NormalizeFoo()たときにプロセス全体が実行されたときに呼び出されたときに、最初のレコードの後に​​プロセスが停止するのはなぜですか?fn_NormalizeFoo()


いくつかのコード:

--------------------------------------------------------
--                Insert Into TableFoo                --
--------------------------------------------------------
CREATE OR REPLACE FUNCTION "example"."fn_SetFoo" (
    IN "Foo1" INTEGER,
    IN "Foo2" INTEGER,
    IN "Foo3" INTEGER
) RETURNS "void" AS 
$$
BEGIN
    INSERT INTO 
        "example"."TableFoo"(
            "Foo1",
            "Foo2",
            "Foo3"
        )
    VALUES
        (
            $1, 
            $2, 
            $3
        );
    RETURN;

    EXCEPTION WHEN "unique_violation" THEN
        -- DO NOTHING
END;
$$
LANGUAGE plpgsql;



--------------------------------------------------------
--              Insert Into TableFooBar               --
--------------------------------------------------------
CREATE OR REPLACE FUNCTION "example"."fn_SetFooBar" (
    IN "FooPK" INTEGER,
    IN "BarPK" INTEGER
) RETURNS "void" AS 
$$
BEGIN
    INSERT INTO 
        "example"."TableFooBar"(
            "FooPK",
            "BarPK"
        )
    VALUES
        (
            $1, 
            $2
        );
    RETURN;

    EXCEPTION WHEN "unique_violation" THEN
        -- DO NOTHING
END;
$$
LANGUAGE plpgsql;



--------------------------------------------------------
--                  Trigger Function                  --
--------------------------------------------------------
CREATE OR REPLACE FUNCTION "example"."tr_SetFooBar"() RETURNS TRIGGER AS
$$
BEGIN
    PERFORM
        "example"."fn_SetFooBar"(
            "TableFoo"."FooPK",
            "TableBar"."BarPK"
        )
    FROM
        "example"."TableFoo" JOIN
        "example"."TableBar" ON [SOMETHING TRUE]
    WHERE
        NEW.SOMECOLUMN = SOMETHING AND 
        [MORE STUFF IS TRUE];

    RETURN NULL;
END;
$$ 
LANGUAGE plpgsql;



--------------------------------------------------------
--                      Trigger                       --
--------------------------------------------------------
CREATE TRIGGER "SetFooBar" AFTER INSERT ON "example"."Foo" FOR EACH ROW EXECUTE PROCEDURE "example"."tr_SetFooBar"();



--------------------------------------------------------
--                   Normalize Foo                    --
--------------------------------------------------------
CREATE OR REPLACE FUNCTION "example"."fn_NormaliseFoo" (
    IN "Param1" VARCHAR,
    IN "Param2" VARCHAR,
    IN "Param3" VARCHAR
) RETURNS "void" AS 
$$
SELECT
    "example"."fn_SetFoo" (
        "Foo1",
        "Foo2",
        "Foo3"
    )
FROM
    [TABLES]
WHERE
    [STUFF IS TRUE]
$$
LANGUAGE SQL;

ご覧のとおり、これは最初に投稿したものよりも少し複雑です。一般的な考え方は、各レコードが追加されるたびに多対多の関係を作成することです。

繰り返しますが、実行"example"."fn_NormaliseFoo"は最初の行の後で失敗します。ただし、コンテンツを手動で実行すると、期待どおりに機能します。

4

1 に答える 1

0

fn_NormalizeFoo()次のように説明されます:

一部のデータを処理し、処理したレコードごとにfn_SetFoo()を呼び出す個別の関数fn_NormalizeFoo()。

ただし、に示されているコードfn_NormaliseFooはSQL言語で宣言されているため、手続き型ではないため、要求されていることを実行できません。1つのクエリを実行して、その結果を返すことができます(結果がない場合は何も返しません)。このようなコードは、呼び出し元のクエリへのインライン化と互換性がある必要があります。

したがって、最初の問題は、fn_NormaliseFoo戻り値として宣言されてvoidいるが、それがSELECTであるという事実と互換性がないことです。sql通常、関数の作成は、言語のインタプリタによって受け入れられるべきではありません。例:

CREATE FUNCTION f(int) returns void as 'select $1;' language sql;

これは次の場合に失敗します。

エラー:voidを返すように宣言された関数の戻り型の不一致
詳細:実際の戻り型は整数です。

2番目の問題は、一度だけ呼び出される可能性が高いことです。これは、質問の「最初のレコードの後でプロセスが停止する」fn_SetFooから私が理解していることです。おそらく行を生成するいくつかの結合されたテーブルを含むクエリの選択リストに呼び出しがあるにもかかわらず、SQLエンジンがそれを何度も呼び出す理由はここにありません。一度だけ呼び出すと、クエリの出力として形成されるすべての行に同じ結果が影響するため、より適切です。NN

sqlにもかかわらず、使用しているように見えますplpgsql。関数がN回呼び出されるようにするには、手続き型コードでN回ループします。これにより、確実に機能するようになります。

于 2013-01-15T20:36:00.310 に答える