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

110

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

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

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

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

  • 5
    FWIW, то \ набор оператор - видимому, связан с Psql инструмента командной строки, а не на PgSQL пакетного языка. Я могу ошибаться.
  • 0
    На какой версии Postgres вы работаете?
Теги:
variables
psql

13 ответов

145
Лучший ответ

Переменные 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;
  • 52
    \set предназначен только для инструмента psql , его нельзя использовать в хранимых процедурах!
  • 5
    @SorinSbarnea ОП спросил о сценарии , а не процедуры
Показать ещё 12 комментариев
55

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

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

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

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

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

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
    
  • 10
    Приветствия для :'myvariable'
  • 0
    Именно то, что я искал!
35

Вы можете попробовать использовать 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;
  • 0
    Этот способ наиболее удобен, когда вы используете одни и те же вычисленные значения несколько раз в своем запросе.
  • 2
    Кажется, он не работает в предложении WHERE . справедливое предупреждение.
Показать ещё 4 комментария
27

В частности, для 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)

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

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

  • 0
    Переменные psql, например: «filepath», вы указали: «Обратите внимание, что двоеточие не заключено в кавычки, тогда имя переменной, в которой она находится, заключено в кавычки». Поблагодарить! Вы! Я уже положил кучу вмятин в форме лба на свой стол, пытаясь сделать эту работу, а вы просто сэкономили мне кучу больше. Именно то, что мне нужно для некоторых сценариев.
11

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

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

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

\ set owner_password 'thepassword';

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

\ echo: owner_password thepassword;

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

СОЗДАТЬ РОЛЬ myrole LOGIN UNENCRYPTED PASSWORD: owner_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'бесконечность';

... Я получил это:

СОЗДАТЬ РОЛЬ myrole LOGIN UNENCRYPTED PASSWORD thepassword; NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'бесконечность';

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

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

7

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-инъекций.

2

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

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

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

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

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

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

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

1

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

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

  • функции 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

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

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

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

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

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

Я разместил новое решение для этого в другой теме.

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

0

Переменные в 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)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню