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?

EMP
la source
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.Wincewicz
la source
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;
fei0x
la source
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.

Dario Barrionuevo
la source
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

un cheval sans nom
la source
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';
Shahriar Aghajani
la source
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;
Evan Carroll
la source
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;
bleuâtre
la source
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.

Jorge Luis
la source
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;
Martin Zinovsky
la source
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)
Michael.Medvedskiy
la source
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);
gavenkoa
la source
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.

Le codeur
la source