Pergunta

No MS SQL Server, eu crio meus scripts para usar variáveis ​​personalizáveis:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

então vou alterar o valor de @somevariable em tempo de execução, dependendo do valor que desejo na situação específica.Como está no topo do script, é fácil de ver e lembrar.

Como faço o mesmo com o cliente PostgreSQL psql?

Foi útil?

Solução

Variáveis ​​Postgres são criadas através do comando \set, por exemplo ...

\set myvariable value

...e pode então ser substituído, por exemplo, como ...

SELECT * FROM :myvariable.table1;

...ou ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

editar:A partir do psql 9.1, as variáveis ​​podem ser expandidas entre aspas como em:

\set myvariable value 

SELECT * FROM table1 WHERE column1 = :'myvariable';

Em versões mais antigas do cliente psql:

...Se você quiser usar a variável como valor em uma consulta de string condicional, como ...

SELECT * FROM table1 WHERE column1 = ':myvariable';

...então você precisa incluir as aspas na própria variável, pois o procedimento acima não funcionará.Em vez disso, defina sua variável como tal ...

\set myvariable 'value'

Porém, se assim como eu, você se deparou com uma situação em que queria fazer uma string a partir de uma variável existente, descobri que o truque é este...

\set quoted_myvariable '\'' :myvariable '\''

Agora você tem uma variável entre aspas e sem aspas da mesma string!E você pode fazer algo assim....

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;

Outras dicas

Uma palavra final sobre variáveis ​​PSQL:

  1. Eles não se expandem se você os colocar entre aspas simples na instrução SQL.Portanto, isso não funciona:

    SELECT * FROM foo WHERE bar = ':myvariable'
    
  2. Para expandir para uma string literal em uma instrução SQL, você deve incluir as aspas no conjunto de variáveis.No entanto, o valor da variável já precisa estar entre aspas, o que significa que você precisa de um segundo conjunto de aspas, e o conjunto interno deve ser escapado.Assim você precisa:

    \set myvariable '\'somestring\''  
    SELECT * FROM foo WHERE bar = :myvariable
    

    EDITAR:começando com PostgreSQL 9.1, você pode escrever:

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
    

Você pode tentar usar um COM cláusula.

WITH vars AS (SELECT 42 AS answer, 3.14 AS appr_pi)
SELECT t.*, vars.answer, t.radius*vars.appr_pi
FROM table AS t, vars;

Especificamente para psql, você pode passar psql variáveis ​​da linha de comando também;você pode passá-los com -v.Aqui está um exemplo de uso:

$ psql -v filepath=/path/to/my/directory/mydatafile.data regress
regress=> SELECT :'filepath';
               ?column?                
---------------------------------------
 /path/to/my/directory/mydatafile.data
(1 row)

Observe que os dois pontos não estão entre aspas e o próprio nome da variável está entre aspas.Sintaxe estranha, eu sei.Isso só funciona no psql;não funcionará em (digamos) PgAdmin-III.

Essa substituição acontece durante o processamento de entrada no psql, então você não pode (digamos) definir uma função que use :'filepath' e espere o valor de :'filepath' para mudar de sessão para sessão.Será substituído uma vez, quando a função for definida, e depois disso será uma constante.É útil para scripts, mas não para uso em tempo de execução.

FWIW, o verdadeiro problema foi que incluí um ponto e vírgula no final do meu comando \set:

\set proprietário_password 'a senha';

O ponto e vírgula foi interpretado como um caractere real na variável:

eco: proprietário_password thepassword;

Então, quando tentei usá-lo:

CREATE ROLE myrole LOGIN UNENCRYPTED SENHA:owner_password NOINHERIT CREATEDB CREATEROLE VALID ATÉ 'infinito';

...Eu tenho esse:

CREATE ROLE myrole LOGIN UNENCRYPTED SENHA thepassword;NOINHERIT CREATEDB CREATEROLE VÁLIDO ATÉ 'infinito';

Isso não apenas falhou ao definir as aspas em torno do literal, mas também dividiu o comando em 2 partes (a segunda das quais era inválida porque começava com "NOINHERIT").

A moral desta história:As "variáveis" do PostgreSQL são, na verdade, macros usadas na expansão de texto, não valores verdadeiros.Tenho certeza de que isso será útil, mas é complicado no começo.

Você precisa usar uma das linguagens procedurais, como PL/pgSQL, e não a linguagem SQL proc.No PL/pgSQL você pode usar vars diretamente nas instruções SQL.Para aspas simples, você pode usar a função literal de cotação.

postgres (desde a versão 9.0) permite blocos anônimos em qualquer uma das linguagens de script do lado do servidor suportadas

DO '
DECLARE somevariable int = -1;
BEGIN
INSERT INTO foo VALUES ( somevariable );
END
' ;

http://www.postgresql.org/docs/current/static/sql-do.html

Como tudo está dentro de uma string, as variáveis ​​​​de string externas substituídas precisarão ser escapadas e citadas duas vezes.Usar a cotação do dólar não oferecerá proteção total contra injeção de SQL.

Outra abordagem é (ab)usar o mecanismo PostgreSQL GUC para criar variáveis.Ver esta resposta anterior para detalhes e exemplos.

Você declara o GUC em postgresql.conf, então altere seu valor em tempo de execução com SET comandos e obtenha seu valor com current_setting(...).

Não recomendo isso para uso geral, mas pode ser útil em casos restritos, como o mencionado na pergunta vinculada, em que o autor da postagem queria uma maneira de fornecer o nome de usuário no nível do aplicativo para gatilhos e funções.

Eu resolvi isso com uma tabela temporária.

CREATE TEMP TABLE temp_session_variables (
    "sessionSalt" TEXT
);
INSERT INTO temp_session_variables ("sessionSalt") VALUES (current_timestamp || RANDOM()::TEXT);

Dessa forma, eu tinha uma "variável" que poderia usar em múltiplas consultas, que é exclusiva para a sessão.Eu precisava que ele gerasse "nomes de usuário" exclusivos e ainda não tivesse colisões ao importar usuários com o mesmo nome de usuário.

Achei esta pergunta e as respostas extremamente úteis, mas também confusas.Tive muitos problemas para fazer com que as variáveis ​​citadas funcionassem, então aqui está como fiz funcionar:

\set deployment_user username    -- username
\set deployment_pass '\'string_password\''
ALTER USER :deployment_user WITH PASSWORD :deployment_pass;

Desta forma você pode definir a variável em uma instrução.Ao usá-lo, aspas simples serão incorporadas à variável.

OBSERVAÇÃO!Quando coloquei um comentário após a variável citada, ele foi sugado como parte da variável quando tentei alguns dos métodos em outras respostas.Isso realmente estava me atrapalhando por um tempo.Com este método, os comentários parecem ser tratados conforme o esperado.

Eu realmente sinto falta desse recurso.A única maneira de conseguir algo semelhante é usar funções.

Eu usei de duas maneiras:

  • funções perl que usam a variável $_SHARED
  • armazene suas variáveis ​​na tabela

Versão Perl:

   CREATE FUNCTION var(name text, val text) RETURNS void AS $$
        $_SHARED{$_[0]} = $_[1];
   $$ LANGUAGE plperl;
   CREATE FUNCTION var(name text) RETURNS text AS $$
        return $_SHARED{$_[0]};
   $$ LANGUAGE plperl;

Versão da tabela:

CREATE TABLE var (
  sess bigint NOT NULL,
  key varchar NOT NULL,
  val varchar,
  CONSTRAINT var_pkey PRIMARY KEY (sess, key)
);
CREATE FUNCTION var(key varchar, val anyelement) RETURNS void AS $$
  DELETE FROM var WHERE sess = pg_backend_pid() AND key = $1;
  INSERT INTO var (sess, key, val) VALUES (sessid(), $1, $2::varchar);
$$ LANGUAGE 'sql';

CREATE FUNCTION var(varname varchar) RETURNS varchar AS $$
  SELECT val FROM var WHERE sess = pg_backend_pid() AND key = $1;
$$ LANGUAGE 'sql';

Notas:

  • plperlu é mais rápido que perl
  • pg_backend_pid não é a melhor identificação de sessão, considere usar pid combinado com backend_start de pg_stat_activity
  • esta versão da tabela também é ruim porque você precisa limpar isso ocasionalmente (e não excluir as variáveis ​​​​de sessão atualmente em funcionamento)

Variáveis ​​em psql é uma droga.Se você quiser declarar um número inteiro, deverá inseri-lo, executar um retorno de carro e terminar a instrução com ponto e vírgula.Observar:

Digamos que eu queira declarar uma variável inteira my_var e insira-o em uma tabela test:

Tabela de exemplo test:

thedatabase=# \d test;
                         Table "public.test"
 Column |  Type   |                     Modifiers                     
--------+---------+---------------------------------------------------
 id     | integer | not null default nextval('test_id_seq'::regclass)
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)

Claramente, nada nesta tabela ainda:

thedatabase=# select * from test;
 id 
----
(0 rows)

Declaramos uma variável. Observe como o ponto e vírgula está na próxima linha!

thedatabase=# \set my_var 999
thedatabase=# ;

Agora podemos inserir.Temos que usar esse estranho":''"procurando sintaxe:

thedatabase=# insert into test(id) values (:'my_var');
INSERT 0 1

Funcionou!

thedatabase=# select * from test;
 id  
-----
 999
(1 row)

Explicação:

Então...o que acontece se não tivermos ponto e vírgula na próxima linha?A variável?Dar uma olhada:

Nós declaramos my_var sem a nova linha.

thedatabase=# \set my_var 999;

Vamos selecionar my_var.

thedatabase=# select :'my_var';
 ?column? 
----------
 999;
(1 row)

WTF é isso?Não é um inteiro, é um corda 999;!

thedatabase=# select 999;
 ?column? 
----------
      999
(1 row)

Eu postei uma nova solução para isso em outro tópico.

Ele utiliza uma tabela para armazenar variáveis ​​e pode ser atualizado a qualquer momento.Uma função getter estática e imutável é criada dinamicamente (por outra função), acionada pela atualização da sua tabela.Você obtém um ótimo armazenamento de mesa, além das velocidades extremamente rápidas de um getter imutável.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top