Comment déclarer une variable dans une requête PostgreSQL


242

Comment déclarer une variable à utiliser dans une requête PostgreSQL 8.3?

Dans MS SQL Server, je peux le faire:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

Comment faire la même chose dans PostgreSQL? Selon la documentation, les variables sont déclarées simplement comme "type de nom;", mais cela me donne une erreur de syntaxe:

myvar INTEGER;

Quelqu'un pourrait-il me donner un exemple de la syntaxe correcte?


2
Cela peut être fait uniquement avec PostgreSQL. Voir la réponse à cette question connexe: stackoverflow.com/questions/766657/…
Sean the Bean

2
Cette réponse connexe a de meilleures réponses: stackoverflow.com/questions/13316773/…
Erwin Brandstetter

Réponses:


113

Il n'y a pas une telle fonctionnalité dans PostgreSQL. Vous ne pouvez le faire qu'en pl / PgSQL (ou autre pl / *), mais pas en SQL simple.

Une exception est la WITH ()requête qui peut fonctionner comme une variable, ou même comme tupledes variables. Il vous permet de renvoyer un tableau de valeurs temporaires.

WITH master_user AS (
    SELECT
      login,
      registration_date
    FROM users
    WHERE ...
)

SELECT *
FROM users
WHERE master_login = (SELECT login
                      FROM master_user)
      AND (SELECT registration_date
           FROM master_user) > ...;

J'ai essayé cette méthode de CTE utilisés comme vriables. Mais j'ai rapidement rencontré un problème où différentes requêtes de modification de données dans les CTE ne sont pas garanties de voir les effets les unes des autres. J'ai dû utiliser plusieurs CTE car j'avais besoin d'utiliser cette variable dans plusieurs requêtes.
Zia Ul Rehman Mughal

228

J'ai atteint le même objectif en utilisant une WITHclause , elle est loin d'être aussi élégante mais peut faire la même chose. Mais pour cet exemple, c'est vraiment exagéré. Je ne le recommande pas non plus particulièrement.

WITH myconstants (var1, var2) as (
   values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
   OR something_else = var2;

2
Cela fonctionne très bien pour la plupart des cas où vous souhaitez des variables. Cependant, si vous vouliez utiliser une variable pour LIMIT (qui ne peut pas contenir de variables), alors vous voudriez l'utiliser \setcomme suggéré dans la réponse de Shahriar Aghajani.
cimmanon du

1
C'est idéal lorsque j'ai un script de migration où je veux importer des données relationnelles. Évidemment, je ne connais pas l'identifiant de séquence donné aux données relationnelles.
Relequestual

3
Je viens d'essayer cette approche, et j'ai trouvé peut-être un meilleur moyen: JOIN myconstants ON trueet puis il n'y a pas besoin de faire la sous-sélection.
Vektor

7
Cela ne fonctionne que dans une seule requête, vous ne pouvez pas partager un WITHCTE entre les requêtes d'une transaction.
Daenyth

2
Vieille question, mais voici ici une variante: WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;. CROSS JOIN, étant un avec une expression de table à une seule ligne, duplique virtuellement les données de toutes les lignes de la vraie table et simplifie l'expression.
Manngo

83

Vous pouvez également essayer ceci dans PLPGSQL:

DO $$
DECLARE myvar integer;
BEGIN
    SELECT 5 INTO myvar;

    DROP TABLE IF EXISTS tmp_table;
    CREATE TABLE tmp_table AS
    SELECT * FROM yourtable WHERE   id = myvar;
END $$;

SELECT * FROM tmp_table;

Ce qui précède nécessite Postgres 9.0 ou version ultérieure.


1
L'instruction DO a été ajoutée dans PostgreSQL 9.0 et ne fonctionne pas dans 8.3.
Johny

14
Utilisez CREATE TEMPORARY TABLE ou CREATE TEMP TABLE, et non CREATE TABLE. Mais sinon ça va.
Stefan Steiger

60

Paramètres de configuration dynamique

vous pouvez "abuser" des paramètres de configuration dynamique pour cela:

-- choose some prefix that is unlikely to be used by postgres
set session my.vars.id = '1';

select *
from person 
where id = current_setting('my.vars.id')::int;

Les paramètres de configuration sont toujours des valeurs varchar, vous devez donc les caster dans le type de données correct lorsque vous les utilisez. Cela fonctionne avec n'importe quel client SQL alors \setquepsql

Ce qui précède nécessite Postgres 9.2 ou version ultérieure.

Pour les versions précédentes, la variable devait être déclarée postgresql.confavant d'être utilisée, ce qui limitait quelque peu son utilisation. En fait pas complètement la variable, mais la "classe" de configuration qui est essentiellement le préfixe. Mais une fois le préfixe défini, toute variable peut être utilisée sans changerpostgresql.conf


3
@BrijanElwadhi: oui, c'est transactionnel.
a_horse_with_no_name

2
En guise de remarque: certains mots sont réservés, par exemple changer set session my.vars.id = '1';pour set session my.user.id = '1';donnerERROR: syntax error at or near "user"
dominik

2
@BrijanElwadhi: Pour faire spécifique de transaction variable que vous devez utiliser: SET LOCAL .... La sessionvariable restera en vigueur tant que votre connexion l'est. Le localest limité à la transaction.
Eugen Konkov

@dominik Vous pouvez contourner cette limitation avec des guillemets, par exemple. set session "my.user.id" = '1';L' current_setting('my.user.id')appel fonctionne comme prévu.
Miles Elam

58

Cela dépend de votre client.

Cependant, si vous utilisez le client psql , vous pouvez utiliser les éléments suivants:

my_db=> \set myvar 5
my_db=> SELECT :myvar  + 1 AS my_var_plus_1;
 my_var_plus_1 
---------------
             6

Si vous utilisez des variables de texte, vous devez citer.

\set myvar 'sometextvalue'
select * from sometable where name = :'myvar';

1
\setdoit être en minuscules
deluan

db = # \ set profile_id 102 db = #: profile_id; ERREUR: erreur de syntaxe à ou près de "102" LIGNE 1: 102; ^
AlxVallejo

1
@AlxVallejo vous devez l'utiliser dans la console d' instructions et psql . db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
everis

21

Utilisation d'une table temporaire en dehors de pl / PgSQL

En dehors de l'utilisation de pl / pgsql ou d'un autre langage pl / * comme suggéré, c'est la seule autre possibilité à laquelle je pourrais penser.

begin;
select 5::int as var into temp table myvar;
select *
  from somewhere s, myvar v
 where s.something = v.var;
commit;

13

Je veux proposer une amélioration à la réponse de @ DarioBarrionuevo , pour la rendre plus simple en utilisant des tables temporaires.

DO $$
    DECLARE myvar integer = 5;
BEGIN
    CREATE TEMP TABLE tmp_table ON COMMIT DROP AS
        -- put here your query with variables:
        SELECT * 
        FROM yourtable
        WHERE id = myvar;
END $$;

SELECT * FROM tmp_table;

belle solution pour résoudre le bloc DO ne peut pas retourner l'ensemble de données!
CodeFarmer

Sur PostgreSQL 11.0, une telle requête renvoie 1(vraisemblablement le nombre de lignes) plutôt que le contenu de tmp_table.
Ed Noepel

9

Cette solution est basée sur celle proposée par fei0x mais elle présente l'avantage de ne pas avoir besoin de joindre la liste de valeurs des constantes dans la requête et les constantes peuvent être facilement listées au début de la requête. Il fonctionne également dans les requêtes récursives.

Fondamentalement, chaque constante est une table à valeur unique déclarée dans une clause WITH qui peut ensuite être appelée n'importe où dans la partie restante de la requête.

  • Exemple de base avec deux constantes:
WITH
    constant_1_str AS (VALUES ('Hello World')),
    constant_2_int AS (VALUES (100))
SELECT *
FROM some_table
WHERE table_column = (table constant_1_str)
LIMIT (table constant_2_int)

Vous pouvez également utiliser SELECT * FROM constant_nameau lieu de celui TABLE constant_namequi pourrait ne pas être valide pour d'autres langages de requête différents de postgresql.


6

Voici un exemple utilisant des instructions PREPARE . Vous ne pouvez toujours pas utiliser ?, mais vous pouvez utiliser la $nnotation:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;

Fonctionne assez bien! Merci.
Rui Carvalho

4

Certes, il n'y a pas de façon claire et non ambiguë de déclarer une variable à valeur unique, ce que vous pouvez faire est

with myVar as (select "any value really")

puis, pour accéder à la valeur stockée dans cette construction, vous devez

(select * from myVar)

par exemple

with var as (select 123)    
... where id = (select * from var)

3

Vous pouvez recourir à des fonctionnalités spéciales de l'outil. Comme pour la propre syntaxe propriétaire de DBeaver:

@set name = 'me'
SELECT :name;
SELECT ${name};

DELETE FROM book b
WHERE b.author_id IN (SELECT a.id FROM author AS a WHERE a.name = :name);

C'est plus proche de l'utilisable: je vais voir si DBeaver prend en charge les listes et les boucles: j'ai besoin d'appliquer le même sql à plusieurs schémas et la liste serait des schémas auxquels les appliquer.
javadba

1

Dans DBeaver, vous pouvez utiliser les paramètres dans les requêtes comme vous le pouvez à partir du code, donc cela fonctionnera:

SELECT *
FROM somewhere
WHERE something = :myvar

Lorsque vous exécutez la requête, DBeaver vous demandera la valeur de: myvar et exécutera la requête.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.