Как вы используете переменные скрипта в psql?

133

В MS SQL Server я создаю свои сценарии для использования настраиваемых переменных:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

Затем я изменю значение @somevariableво время выполнения в зависимости от значения, которое мне нужно в конкретной ситуации. Поскольку он находится в верхней части сценария, его легко увидеть и запомнить.

Как мне сделать то же самое с клиентом PostgreSQL psql?

Крейг Уокер
источник
5
FWIW, оператор \ set, похоже, связан с инструментом командной строки psql, а не с пакетным языком pgsql. Я могу ошибаться.
Daniel Yankowsky
1
На какой версии Postgres вы работаете?
Kuberchaun

Ответы:

181

Переменные Postgres создаются с помощью команды \ set, например ...

\set myvariable value

... и затем может быть заменен, например, как ...

SELECT * FROM :myvariable.table1;

... или ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

edit: Начиная с psql 9.1, переменные можно раскрывать в кавычках, как в:

\set myvariable value 

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

В старых версиях клиента psql:

... Если вы хотите использовать переменную в качестве значения в условном строковом запросе, например ...

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

... тогда вам нужно включить кавычки в саму переменную, так как приведенное выше не будет работать. Вместо этого определите свою переменную как таковую ...

\set myvariable 'value'

Однако, если вы, как и я, столкнетесь с ситуацией, когда захотите создать строку из существующей переменной, я обнаружил, что уловка будет в следующем ...

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

Теперь у вас есть переменная в кавычках и без кавычек одной и той же строки! А можно сделать что-то вроде этого ....

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
crowmagnumb
источник
67
\setпредназначен только для psqlинструмента, вы не можете использовать его в хранимых процедурах!
sorin
6
@SorinSbarnea, ОП спросил о сценарии , а не о процедуре
Даниэль Серодио
35
В этом ответе psqlмета-команды смешиваются с командами \setPostgreSQL запутанным образом.
Эрвин Брандштеттер
20
Начиная с postgresql 9.1, в psql теперь вы можете использовать: 'переменную', чтобы она правильно указывалась в качестве значения для вас, или: "переменная", чтобы использовать ее как идентификатор.
HitScan,
9
\set myvariable 'value'вовсе не включать в себя любую цитату внутри переменной, вопреки тому , что говорит этот ответ. В случае сомнений используйте \echo :myvariableв psql, чтобы отобразить значение независимо от любого запроса.
Даниэль Верите
61

И последнее слово о переменных PSQL:

  1. Они не расширяются, если вы заключите их в одинарные кавычки в операторе SQL. Таким образом, это не работает:

    SELECT * FROM foo WHERE bar = ':myvariable'
  2. Чтобы преобразовать в строковый литерал в операторе SQL, вы должны включить кавычки в набор переменных. Однако значение переменной уже должно быть заключено в кавычки, что означает, что вам нужен второй набор кавычек, а внутренний набор должен быть экранирован. Таким образом, вам необходимо:

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

    РЕДАКТИРОВАТЬ : начиная с PostgreSQL 9.1, вы можете вместо этого написать:

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
Крейг Уокер
источник
12
:'myvariable'
Болеем
Именно то, что я искал!
KeniSteward
47

Вы можете попробовать использовать предложение WITH .

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;
skaurus
источник
Этот способ наиболее удобен, когда вы несколько раз используете одни и те же вычисленные значения в своем запросе.
skaurus 08
2
Вопреки отчету Брайса, мне кажется, это нормально работает. CREATE TABLE test (name VARCHAR, age INT); INSERT INTO test (name, age) VALUES ('Jack', 21), ('Jill', 20); WITH vars AS (SELECT N'Jack' AS name, 21 AS age) SELECT test.* FROM test, vars WHERE test.name = vars.name and test.age = vars.age; Как и ожидалось, Джек и его возраст.
Джошуа
2
Для множества применений, особенно в контексте фреймворка веб-приложений, такого как Python Flask, это лучшее решение для повторного использования сложных вычисляемых значений в одном запросе.
Уилл
1
Может ли кто-нибудь подсказать, как это может работать во вставке?
Stoopkid
@Stoopkidcreate table t(x integer); insert into t(x) with sub as (select 999 as num) select num from sub; select * from t;
JL_SO
33

В частности psql, вы также можете передавать psqlпеременные из командной строки; вы можете передать их с помощью -v. Вот пример использования:

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

Обратите внимание, что двоеточие не заключено в кавычки, тогда указывается само имя переменной. Странный синтаксис, я знаю. Это работает только в psql; это не будет работать (скажем) в PgAdmin-III.

Эта подстановка происходит во время обработки ввода в psql, поэтому вы не можете (скажем) определить функцию, которая использует, :'filepath'и ожидать, что значение :'filepath'будет изменяться от сеанса к сеансу. Он будет заменен один раз, когда функция определена, и после этого станет константой. Это полезно для написания сценариев, но не для использования во время выполнения.

Крейг Рингер
источник
Переменные psql, например: 'filepath', вы указали: «Обратите внимание, что двоеточие не заключено в кавычки, тогда имя переменной цитируется самостоятельно». Спасибо! Вы! Я уже нанес кучу вмятин в форме лба на моем столе, пытаясь справиться с этой задачей, и вы только что спасли меня еще на тонну. Именно то, что мне нужно для написания скриптов.
Джейсон
13

FWIW, настоящая проблема заключалась в том, что я поставил точку с запятой в конце моей команды \ set:

\ установить пароль_владельца 'пароль';

Точка с запятой интерпретировалась как фактический символ в переменной:

\ echo: owner_password thepassword;

Итак, когда я попытался его использовать:

СОЗДАТЬ РОЛЬ myrole ВОЙТИ НЕШифрованный пароль: owner_password NOINHERIT CREATEDB CREATEROLE VALID ДО 'бесконечности';

... Получил вот что:

СОЗДАТЬ РОЛЬ myrole ВХОД НЕШифрованный ПАРОЛЬ thepassword; NOINHERIT CREATEDB CREATEROLE ДЕЙСТВИТЕЛЬНО ДО "бесконечности";

При этом не только не удалось установить кавычки вокруг литерала, но и разбить команду на 2 части (вторая из которых была недействительной, поскольку начиналась с «NOINHERIT»).

Мораль этой истории: «переменные» PostgreSQL - это на самом деле макросы, используемые при расширении текста, а не истинные значения. Я уверен, что это пригодится, но поначалу это сложно.

Крейг Уокер
источник
12

Вам необходимо использовать один из процедурных языков, например PL / pgSQL, а не язык процедур SQL. В PL / pgSQL вы можете использовать переменные прямо в операторах SQL. Для одинарных кавычек вы можете использовать функцию кавычки.


источник
5
Это невозможно сделать в самом postgres, но это можно сделать в клиентском приложении PSQL.
Филлуминати
1
plpgsql может (теперь) использоваться в postgres (начиная с версии 9.0)) postgresql.org/docs/9.0/static/sql-do.html
Jasen
11

postgres (начиная с версии 9.0) разрешает анонимные блоки на любом из поддерживаемых языков сценариев на стороне сервера

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

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

Поскольку все находится внутри строки, заменяемые внешние строковые переменные должны быть экранированы и дважды заключены в кавычки. Использование долларовых кавычек вместо этого не даст полной защиты от SQL-инъекций.

Jasen
источник
5

Другой подход - (ab) использовать механизм PostgreSQL GUC для создания переменных. См. Предыдущий ответ для получения подробностей и примеров.

Вы объявляете GUC в postgresql.conf, затем изменяете его значение во время выполнения с помощью SETкоманд и получаете его значение с помощьюcurrent_setting(...) .

Я не рекомендую это для общего использования, но это может быть полезно в узких случаях, таких как тот, который упоминается в связанном вопросе, когда плакат хотел предоставить способ предоставить имя пользователя на уровне приложения для триггеров и функций.

Крейг Рингер
источник
4

Я решил это с помощью временной таблицы.

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

Таким образом, у меня была «переменная», которую я мог использовать для нескольких запросов, уникальную для сеанса. Мне нужно, чтобы он генерировал уникальные «имена пользователей», но при этом не имел коллизий при импорте пользователей с тем же именем пользователя.

гирооптическая астронавигационная система
источник
Кажется, это единственный способ работы в визуальных инструментах вроде Heidi SQL.
Altair7852
2

Я нашел этот вопрос и ответы чрезвычайно полезными, но также сбивающими с толку. У меня было много проблем с тем, чтобы заставить работать цитируемые переменные, поэтому вот как у меня это работает:

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

Таким образом, вы можете определить переменную в одном операторе. Когда вы его используете, в переменную будут вставлены одинарные кавычки.

НОТА! Когда я помещал комментарий после цитируемой переменной, он был засосан как часть переменной, когда я пробовал некоторые методы в других ответах. Это действительно какое-то время меня облажало. С этим методом комментарии обрабатываются так, как и следовало ожидать.

Nate
источник
\set deployment_pass 'string_password' ALTER USER :deployment_user WITH PASSWORD :'deployment_pass';
Jasen
\ set - это не SQL, это встроенная команда psql. Комментарии sql не поддерживаются.
Jasen
2

Я очень скучаю по этой функции. Единственный способ добиться чего-то подобного - использовать функции.

Я использовал его двумя способами:

  • функции perl, использующие переменную $ _SHARED
  • сохраните свои переменные в таблице

Версия 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;

Настольная версия:

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

Ноты:

  • plperlu быстрее Perl
  • pg_backend_pid - не лучшая идентификация сеанса, рассмотрите возможность использования pid в сочетании с backend_start из pg_stat_activity
  • эта версия таблицы также плохая, потому что вы должны время от времени очищать ее (и не удалять текущие рабочие переменные сеанса)
Кайко Каур
источник
1

Переменные в psql отстой. Если вы хотите объявить целое число, вы должны ввести целое число, затем выполнить возврат каретки и завершить оператор точкой с запятой. Заметим:

Допустим, я хочу объявить целочисленную переменную my_varи вставить ее в таблицуtest :

Примерная таблица 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)

Понятно, что в этой таблице пока ничего:

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

Объявляем переменную. Обратите внимание на точку с запятой на следующей строке!

thedatabase=# \set my_var 999
thedatabase=# ;

Теперь мы можем вставить. Мы должны использовать этот " :''" странный синтаксис:

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

Это сработало!

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

Объяснение:

Итак ... что произойдет, если в следующей строке нет точки с запятой? Переменная? Посмотри:

Объявляем my_varбез новой строки.

thedatabase=# \set my_var 999;

Выберем my_var.

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

Что это за хрень? Это не целое число , это строка 999; !

thedatabase=# select 999;
 ?column? 
----------
      999
(1 row)
Отображаемое имя
источник
5
Причина, по которой точка с запятой делает для вас неожиданные вещи, заключается в том, что точка с запятой завершает оператор SQL, но вы вводите команду psql, \ set, которая не является SQL и НЕ принимает завершающую точку с запятой. Установка точки с запятой в следующей строке не повредит, но абсолютно ничего не делает. Это пустое заявление.
volkerk
1

Я опубликовал новое решение для этого в другом потоке .

Он использует таблицу для хранения переменных и может быть обновлен в любое время. Статическая неизменяемая функция-получатель создается динамически (другой функцией), запускается обновлением вашей таблицы. Вы получаете хорошее хранилище таблиц, а также невероятно высокую скорость неизменяемого геттера.

Brev
источник