我有一个函数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"
正如您所看到的,这比我最初发布的要复杂一些。一般的想法是在添加每条记录时创建多对多的关系。
重申一下,在第一行之后运行{{1}}失败;但是,手动运行内容按预期工作。
答案 0 :(得分: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的函数返回类型不匹配 DETAIL:实际返回类型是整数。
第二个问题是fn_SetFoo
可能只被调用一次,这是我从问题的“过程在第一个记录之后停止”中理解的。尽管调用在一个查询的选择列表中,其中一些连接表可能产生N
行,但是没有理由在这里可以看到SQL引擎将其称为N
次。更好的是它只调用一次,并将相同的结果影响到作为查询输出形成的每一行。
看起来有点像sql
plpgsql
。为了确保函数被调用N次,在过程代码中循环N次,这将保证有效。