0

情况:

我有一个fn_SetFoo()将记录插入表的函数TableFoo

我还有一个触发功能,在每次插入TableFoo. 它TableFooID从新插入的行中获取新的主键并将其插入到第二个表中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_NormalizeFoo() 处理一些数据,然后为它处理的每条记录调用 fn_SetFoo()。

但是,显示的代码fn_NormaliseFoo是用 SQL 语言声明的,因此它不是程序性的,因此它不能执行所声明的操作。它可以只运行一个查询并返回其结果(如果没有结果,则什么都不返回)。这样的代码必须与调用查询的内联兼容。

所以第一个问题是它fn_NormaliseFoo被声明为返回void,但它与它是一个 SELECT 的事实不兼容。sql通常,该语言的解释器甚至不应该接受函数创建。例子:

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

这失败了:

错误:声明返回 void 的函数中的返回类型不匹配
详细信息:实际返回类型是整数。

第二个问题fn_SetFoo是可能只被调用一次,这是我从问题的“第一条记录后停止的过程”中理解的。尽管调用位于查询的选择列表中,其中一些连接表可能会生成N行,但这里没有理由让 SQL 引擎调用它N。更好的是它只调用一次并影响作为查询输出形成的每一行的相同结果。

sql尽管plpgsql. _ 为了确保一个函数被调用 N 次,在过程代码中循环 N 次,这将保证工作。

于 2013-01-15T20:36:00.310 回答