2016-11-14 1 views
0

J'ai le cas suivant d'utiliser la vue PostgreSQL. Je dois calculer dynamiquement la valeur de la colonne de vue en fonction de la valeur d'une autre colonne de vue calculée dynamiquement. Voici la version simplifiée du code:Éviter la récursion infinie lors du calcul de la valeur de la colonne de vue de base de données en fonction d'une autre colonne de vue

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; 

Mais ce code tombe dans une récursion infinie en raison de la déclaration suivante dans la deuxième fonction.

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

L'erreur exacte est la suivante:

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 

Le problème peut être facilement résolu en utilisant la deuxième vue. Par exemple:

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; 

Mais je n'aime pas cette approche car elle nécessite la création d'une deuxième vue. Il n'échelle pas dans le cas où j'ai n différentes valeurs dépendant chacune de la précédente. Ensuite, je dois avoir n différentes vues.

Est-il possible de résoudre le problème avec une seule vue?

Répondre

1

Pourquoi pas?

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; 

Quelque chose m'avait échappé?

Vous pouvez le faire dans à l'étape

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)