2017-07-04 3 views
2

J'ai essayé de simuler mon problème dans l'exemple de code ci-dessous. Dans le code ci-dessous, je fais un delete from test2 dans une procédure. Cela fonctionne très bien:Postgres plpgsql avec requêtes CTE modifiant les données PERFORM

Cependant, dans mon cas, ce delete fait partie d'un CTE assez complexe avec plusieurs mises à jour et insertions (il n'y a pas de sélection donc j'ajoute un select 1 factice comme requête principale). Faisons simulent cela comme ceci:

with my_cte as(delete from test2) select 1 

Maintenant, comme nous le savons, nous devons utiliser le mot-clé perform pour exécuter ceci:

perform (with my_cte as(delete from test2) select 1); 

Je reçois l'erreur suivante:

ERROR: WITH clause containing a data-modifying statement must be at the top level 

Est-ce une limitation de plpgsql?

(S'il vous plaît noter que ceci est juste un exemple pour expliquer mon problème. Je sais que les requêtes ne font pas vraiment de sens.)

create table test 
(
    key int primary key 
); 

create table test2 
(
    key int primary key 
); 

create function test() returns trigger as 
$$ 
begin 
    raise notice 'hello there'; 
    -- this does work 
    delete from test2; 
    -- this doesn't work 
    perform (with my_cte as(delete from test2) select 1); 
    return new; 
end; 
$$ 
language plpgsql; 

create trigger test after insert on test for each row execute procedure test(); 

insert into test(key) select 1; 
+0

https://www.postgresql.org/message-id/[email protected] .com avez-vous lu le fil ci-dessus? Je dirais que c'est une limitation. Il serait plus facile si vous partagiez le vrai problème - peut-être il y a une solution plus simple alors effectuant CTW avec manipulation de données –

+0

Raison pourquoi j'utilise CTEs: Je pourrais en effet avoir plusieurs requêtes INSERT, DELETE, UPDATE séparées, mais il est (beaucoup) plus performant pour transmettre les résultats entre les CTE avec retour ... à la fin de la requête. L'alternative est d'aller chercher les données et de les stocker dans les structures de données locales dans les procédures de plpgsq, pour les utiliser dans les requêtes suivantes. –

+0

Donc, à mon avis, ce genre de requêtes ne sont pas vraiment «inutiles», comme indiqué dans votre lien mailing. Merci pour ce lien en passant! –

Répondre

2

Vous pouvez utiliser CTE pour combiner plusieurs requêtes de retour DELETE, INSERT, UPDATE. Et vous ne devez effectuer pour cela, par exemple:

t=# begin; do $$ begin with d as (delete from s133 returning *) insert into s133 select * from d; raise info '%',(select count(1) from s133); 
end; $$; commit; 
BEGIN 
Time: 0.135 ms 
INFO: 4 
DO 
Time: 0.469 ms 
COMMIT 
Time: 0.887 ms 
t=# select count(1) from s133; 
count 
------- 
    4 
(1 row) 

ici je supprime quatre lignes et dans les CTE reinsére

+0

Clever.Au lieu d'avoir plusieurs CTE 'INSERT/UPDATE/DELETE' et un 'select 1' factice, je pourrais déplacer la dernière requête CTE vers la requête principale. –

1

Comme vous avez découvert, vous ne pouvez ni imbriquer une telle clause WITH dans une sous-sélection, ne pouvez-vous faire

WITH cte AS (...) 
PERFORM 1; 

une solution serait d'utiliser SELECT ... INTO dummy au lieu de PERFORM et d'ignorer le résultat.

Mais je ne vois pas pourquoi vous ne pouvez pas coder les DELETE s, UPDATE s et INSERT s dans votre fonction avec plusieurs instructions SQL plutôt que de les regrouper en CTEs.

Si vous tentez de vous protéger contre la modification simultanée de données, utilisez une transaction REPEATABLE READ afin que toutes vos instructions fonctionnent sur le même instantané de la base de données.

+0

Raison pour laquelle j'utilise des CTE: je pourrais en effet avoir plusieurs requêtes séparées 'INSERT', 'DELETE',' UPDATE', mais il est (beaucoup) plus performant de passer des résultats entre CTEs avec 'return ...' à la fin de la requête. L'alternative est d'aller chercher les données et de les stocker dans les structures de données locales dans les procédures de plpgsq, pour les utiliser dans les requêtes suivantes. –

+0

Ok, alors utilisez 'SELECT' au lieu de' PERFORM' et vous êtes défini. –