Como declarar uma variável em uma consulta PostgreSQL


240

Como declaro uma variável para uso em uma consulta do PostgreSQL 8.3?

No MS SQL Server, eu posso fazer isso:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

Como faço o mesmo no PostgreSQL? De acordo com a documentação, as variáveis ​​são declaradas simplesmente como "name type;", mas isso gera um erro de sintaxe:

myvar INTEGER;

Alguém poderia me dar um exemplo da sintaxe correta?


2
Isso pode ser feito apenas no PostgreSQL. Veja a resposta para esta pergunta relacionada: stackoverflow.com/questions/766657/…
Sean the Bean

2
Esta resposta relacionada tem melhores respostas: stackoverflow.com/questions/13316773/...
Erwin Brandstetter

Respostas:


113

Não existe esse recurso no PostgreSQL. Você pode fazer isso apenas em pl / PgSQL (ou outro pl / *), mas não em SQL simples.

Uma exceção é a WITH ()consulta que pode funcionar como uma variável ou mesmo tuplede variáveis. Permite retornar uma tabela de valores temporários.

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) > ...;

Eu tentei esse método de CTEs sendo usados ​​como variáveis. Mas, rapidamente, encontrei um problema em que diferentes consultas de modificação de dados nos CTEs não têm garantia de ver os efeitos uns dos outros. Eu tive que usar vários CTEs, pois eu precisava usar essa variável em várias consultas.
Zia Ul Rehman Mughal

227

Eu alcancei o mesmo objetivo usando uma WITHcláusula , não é nem de longe tão elegante, mas pode fazer a mesma coisa. Embora para este exemplo seja realmente um exagero. Eu também particularmente não recomendo isso.

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

2
Isso funciona muito bem na maioria dos casos em que você deseja variáveis. No entanto, se você quiser usar uma variável para LIMIT (que não pode conter variáveis), use \setcomo sugerido na resposta de Shahriar Aghajani.
Cimmanon

1
Isso é ideal para quando eu tenho um script de migração em que desejo importar alguns dados relacionais. Obviamente, não saberei o ID da sequência que os dados relacionais são fornecidos.
Relequestual

3
Eu apenas tentei essa abordagem e achei uma maneira talvez melhor: JOIN myconstants ON truee então não há necessidade de fazer a sub-seleção.
58615

7
Isso funciona apenas em uma única consulta. Você não pode compartilhar um WITHCTE entre as consultas de uma transação.
Daenyth

2
Velha questão, mas aqui, aqui está uma variação: WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;. O CROSS JOIN, sendo um com uma expressão de tabela de linha única, praticamente duplica os dados de todas as linhas da tabela real e simplifica a expressão.
Manngo

82

Você também pode tentar isso no 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;

O acima requer Postgres 9.0 ou posterior.


1
A instrução DO foi adicionada no PostgreSQL 9.0 e não funciona na versão 8.3.
19415 Johny

14
Use CREATE TEMPORARY TABLE ou CREATE TEMP TABLE, não CREATE TABLE. Mas por outro lado tudo bem.
Stefan Steiger

60

Definições de configuração dinâmica

você pode "abusar" das configurações dinâmicas da configuração para isso:

-- 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;

As definições de configuração são sempre valores varchar, portanto, você precisa convertê-las no tipo de dados correto ao usá-las. Isso funciona com qualquer cliente SQL, enquanto \setsó funciona empsql

O acima requer Postgres 9.2 ou posterior.

Para versões anteriores, a variável tinha que ser declarada postgresql.confantes de ser usada, limitando um pouco a sua usabilidade. Na verdade, não a variável completamente, mas a "classe" de configuração, que é essencialmente o prefixo. Porém, uma vez definido o prefixo, qualquer variável pode ser usada sem alterarpostgresql.conf


3
@BrijanElwadhi: sim, isso é transacional.
A_horse_with_no_name 20/02

2
Como uma observação: algumas palavras são reservados, por exemplo alterando set session my.vars.id = '1';a set session my.user.id = '1';produziráERROR: syntax error at or near "user"
dominik

2
@BrijanElwadhi: Para fazer específico transação variável que você deve usar: SET LOCAL .... A sessionvariável entrará em vigor enquanto você estiver conectado. O localescopo é a transação.
Eugen Konkov

@dominik Você pode contornar essa limitação com aspas, por exemplo, set session "my.user.id" = '1';a current_setting('my.user.id')chamada funciona conforme o esperado.
Miles Elam

58

Depende do seu cliente.

No entanto, se você estiver usando o cliente psql , poderá usar o seguinte:

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

Se você estiver usando variáveis ​​de texto, precisará citar.

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

1
\setdeve ser minúsculo
deluan 8/08/17

db = # \ definir profile_id 102 db = #: profile_id; ERRO: erro de sintaxe na linha "102" ou próximo a "102" 1: 102; ^
AlxVallejo 30/01/19

1
@AlxVallejo, você precisa usá-lo na instrução e no console psql . db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
everis

21

Usando uma tabela temporária fora do pl / PgSQL

Fora o uso de pl / pgsql ou outra linguagem pl / *, conforme sugerido, esta é a única outra possibilidade em que eu poderia pensar.

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

13

Quero propor uma melhoria na resposta de @ DarioBarrionuevo , para simplificar o aproveitamento de tabelas temporárias.

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;

Uma boa solução para resolver o bloco DO não pode retornar o conjunto de dados!
CodeFarmer

No PostgreSQL 11.0, essa consulta retorna 1(presumivelmente a contagem de linhas) em vez do conteúdo de tmp_table.
Ed Noepel

9

Essa solução é baseada na proposta por fei0x, mas possui as vantagens de que não há necessidade de associar a lista de valores de constantes na consulta e as constantes podem ser facilmente listadas no início da consulta. Também funciona em consultas recursivas.

Basicamente, toda constante é uma tabela de valor único declarada em uma cláusula WITH que pode ser chamada em qualquer lugar da parte restante da consulta.

  • Exemplo básico com duas 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)

Como alternativa, você pode usar em SELECT * FROM constant_namevez do TABLE constant_nameque pode não ser válido para outras linguagens de consulta diferentes do postgresql.


6

Aqui está um exemplo usando instruções PREPARE . Você ainda não pode usar ?, mas pode usar a $nnotação:

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

Funciona muito bem! Obrigado.
Rui Carvalho

4

É verdade que não há uma maneira clara e inequívoca de declarar uma variável de valor único, o que você pode fazer é

with myVar as (select "any value really")

então, para obter acesso ao valor armazenado nesta construção, faça

(select * from myVar)

por exemplo

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

3

Você pode recorrer a recursos especiais da ferramenta. Como na sintaxe proprietária do 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);

Isso é mais fácil de usar: vou examinar se o DBeaver suporta listas e loop: preciso aplicar o mesmo sql a vários esquemas e a lista seria dos esquemas para aplicá-los.
javadba 26/03

1

No DBeaver, você pode usar parâmetros em consultas, assim como no código, portanto, isso funcionará:

SELECT *
FROM somewhere
WHERE something = :myvar

Quando você executa a consulta, o DBeaver solicita o valor para: myvar e executa a consulta.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.