【问题标题】:Avoiding infinite recursion when calculating value of database view column depending on another view column根据另一个视图列计算数据库视图列的值时避免无限递归
【发布时间】:2017-03-28 02:42:41
【问题描述】:

我有以下使用 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种不同的观点。

只有一个视图可以解决问题吗?

【问题讨论】:

    标签: database postgresql recursion sql-view dynamic-columns


    【解决方案1】:

    为什么不呢?

    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)
    

    【讨论】:

      猜你喜欢
      • 2020-04-12
      • 1970-01-01
      • 2018-07-30
      • 1970-01-01
      • 2020-01-03
      • 1970-01-01
      • 2013-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多