根据另一个视图列计算数据库视图列的值时避免无限递归

Avoiding infinite recursion when calculating value of database view column depending on another view column

我有以下使用PostgreSQL视图的案例。我必须根据另一个动态计算的视图列的值动态计算视图列的值。这是代码的简化版本:

BEGIN;

CREATE TABLE test
(
  id serial PRIMARY KEY,
  value integer NOT NULL
);

INSERT INTO test VALUES
  (1, 13),
  (2, 42);

CREATE FUNCTION inc(value integer)
  RETURNS integer AS
$BODY$
  BEGIN
    RETURN value + 1;
  END;
$BODY$
  LANGUAGE plpgsql;

CREATE FUNCTION double(id integer)
  RETURNS integer AS
$BODY$
  DECLARE
    local_value integer;
  BEGIN
    SELECT value_1 INTO local_value
    FROM test_view WHERE double.id = test_view.id;

    RETURN 2 * local_value;
  END;
$BODY$
  LANGUAGE plpgsql;

CREATE VIEW test_view AS
  SELECT *,
         inc(test.value) AS value_1,
         double(test.id) AS value_2
  FROM test;

COMMIT;

但是由于第二个函数中的以下语句,这段代码陷入了无限递归。

SELECT value_1 INTO local_value
FROM test_view WHERE double.id = test_view.id;

准确错误如下:

ERROR:  stack depth limit exceeded
HINT:  Increase the configuration parameter "max_stack_depth
(currently  2048kB), after ensuring the platform's stack depth limit is adequate.
CONTEXT:  PL/pgSQL function inc(integer) line 3 at RETURN
SQL statement "SELECT value_1 FROM test_view WHERE double.id = test_view.id"
 PL/pgSQL function double(integer) line 5 at SQL statement

使用第二个视图可以很容易地解决这个问题。例如:

CREATE FUNCTION double(value integer)
  RETURNS integer AS
$BODY$
  BEGIN
    RETURN 2 * value;
  END;
$BODY$
  LANGUAGE plpgsql;

CREATE VIEW test_view_1 AS
  SELECT *,
         inc(test.value) AS value_1
  FROM test;

CREATE VIEW test_view_2 AS
  SELECT *,
         double(test_view_1.value_1) AS value_2
  FROM test_view_1;

但我不喜欢这种方法,因为它需要创建第二个视图。在我有 n 不同值的情况下,它不会缩放,每个值取决于前一个值。那我肯定有n个不同的看法。

是否可以只用一个视图解决问题?

为什么不呢?

CREATE FUNCTION inc(value integer)
  RETURNS integer AS
  $BODY$
  BEGIN
    RETURN value + 1;
  END;
$BODY$
LANGUAGE plpgsql;

CREATE FUNCTION double(value integer)
  RETURNS integer AS
  $BODY$
  BEGIN
    RETURN 2 * value;
  END;
$BODY$
LANGUAGE plpgsql;

CREATE VIEW test_view AS
  SELECT *,double(value_1) AS value_2 FROM 
(SELECT *,
    inc(test.value) AS value_1
  FROM test) x;

select * from test_view;

我是不是漏掉了 OP 里的东西?

您可以在第

步完成
CREATE OR REPLACE  FUNCTION incdouble(value integer)
  RETURNS RECORD AS
  $BODY$
  DECLARE
     linc integer;
     ret  RECORD;  
  BEGIN
    linc := value + 1;
    SELECT linc,linc*2 INTO ret;
    RETURN ret;
  END;
$BODY$
LANGUAGE plpgsql ;
select * from test t,incdouble(t.value) as (i integer ,d integer)