我在postgresql的规则中子查询的执行顺序中遇到了一个奇怪的行为(或者这是postgresql中的一个错误?)。请考虑以下SQL:
BEGIN;
CREATE OR REPLACE FUNCTION debug(anyelement) RETURNS bool AS $$
pg_raise('notice', 'debug(): ' . json_encode($args[0]));
RETURN TRUE;
$$ LANGUAGE PLPHP IMMUTABLE STRICT;
CREATE TABLE foo_table (c1 text);
CREATE OR REPLACE RULE foo_update_rule AS ON UPDATE TO foo_table DO INSTEAD
(
WITH foobar_update AS
(
SELECT unnest('{a,b}'::text[]) AS _value, debug('update_inner'::text)
)
SELECT *, debug('update_outer_1'::text), debug('update_outer_2 -> '::text || _value::text) FROM foobar_update;
SELECT
( ROW(FALSE,FALSE) IN ( SELECT
debug('update2_outer_1'::text), debug('update2_outer_2 -> '::text || _value::text)
FROM ( SELECT unnest('{a,b}'::text[]) AS _value, debug('update_inner'::text) ) AS foobar_update2 ))
);
-----------------------------------------------
WITH foobar_select AS
(
SELECT unnest('{a,b}'::text[]) AS _value, debug('select_inner'::text)
)
SELECT *, debug('select_outer_1'::text), debug('select_outer_2 -> '::text || _value::text), debug('select_outer_3'::text) FROM foobar_select;
UPDATE foo_table SET c1 = NULL where c1 = 'aaa';
ROLLBACK;
执行上面的代码会生成以下输出:
NOTICE: plphp: debug(): "select_inner"
NOTICE: plphp: debug(): "select_outer_1"
NOTICE: plphp: debug(): "select_outer_3"
NOTICE: plphp: debug(): "select_outer_2 -> a"
NOTICE: plphp: debug(): "select_outer_2 -> b"
NOTICE: plphp: debug(): "update_inner"
NOTICE: plphp: debug(): "update_outer_1"
NOTICE: plphp: debug(): "update2_outer_1"
NOTICE: plphp: debug(): "update_inner"
从输出中,它显示问题是子查询(也称为“内部”)在foo_update_rule中的2个SELECT查询中的引用(也称为“外部”)查询之后执行。因此,在评估外部查询时,尚未定义_value列(在子查询中定义),导致调试('update_outer_2 - >':: text || _value :: text)无提示失败(并且不打印通知)。
奇怪的是,ON INSERT规则中的相同SQL将正常工作(打印出'outer_2 - > ...'两个通知)。但由于某种原因,SQL在ON UPDATE规则中不起作用。
如何修复上述查询以便打印以下2个通知?
NOTICE: plphp: debug(): "update_outer_2 -> a"
NOTICE: plphp: debug(): "update_outer_2 -> b"
NOTICE: plphp: debug(): "update2_outer_2 -> a"
NOTICE: plphp: debug(): "update2_outer_2 -> b"
答案 0 :(得分:4)
PostgreSQL,或者就此而言,SQL本身并不能保证查询的不同部分执行的顺序。它只定义了最终结果。事实上,如果数据库要支持,查询的不同部分可以执行intemixed - 或完全并行化。
现在,RULEs让事情变得更糟,因为它们通常不像用户期望的那样工作。 RULEs在解析器级别工作,而不是在执行时工作。所以你的不同部分可能会运行不止一次 - 只是因为它们会突然出现在解析树中不止一次。
在大多数情况下,你想要的是触发而不是规则。
但是,底线是您的应用程序不应该依赖查询中的特定子查询(或连接或其他)以特定顺序执行。