Як ви використовуєте змінні сценарію в psql?


133

У MS SQL Server я створюю свої сценарії для використання настроюваних змінних:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

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

Як зробити те ж саме з клієнтом PostgreSQL psql?


5
FWIW, здається, що оператор \ set пов'язаний з інструментом командного рядка psql, а не з пакетною мовою pgsql. Я можу помилитися.
Даніель Янковський

1
На якій версії Postgres ви працюєте?
Куберчаун

Відповіді:


180

Змінні постгреси створюються за допомогою команди \ set, наприклад ...

\set myvariable value

... і може бути заміщений, наприклад, як ...

SELECT * FROM :myvariable.table1;

... або ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

редагувати: Станом на 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;

66
\setє лише psqlінструментом, ви не можете використовувати його в збережених процедурах!
sorin

6
@SorinSbarnea ОП запитала про сценарій , а не про процедуру
Даніель Серодіо

35
Ця відповідь змішує psqlметакоманди з командами \setPostgreSQL незмінно.
Erwin Brandstetter

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

    EDIT : починаючи з 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

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:

\ встановити last_password 'thepassword';

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

\ echo: власник_password thepassword;

Тож коли я спробував ним скористатися:

СТВОРИТИ РОЛЬ мірол ВХІД НЕКРИПІЗОВАНОГО ПАРОЛУ: owner_password NOINHERIT CREATEDB CREATEROLE VALID ДО "нескінченності";

...Я отримав це:

СТВОРИТИ РОЛЬ мирольного ВХІДУВАННЯ НЕКРАНТИРОВАНИЙ ПАРОЛЬ словослова; NOINHERIT CREATEDB CREATEROLE VALID ДО "нескінченності";

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

Мораль цієї історії: "змінні" PostgreSQL - це справді макроси, що використовуються для розширення тексту, а не справжні значення. Я впевнений, що це стане в нагоді, але спочатку це хитро.


12

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


5
Це неможливо зробити в самих постграфах, але це можна зробити в клієнтській програмі PSQL.
Philluminati

1
plpgsql можна (зараз) використовувати у postgres (з версії 9.0)) postgresql.org/docs/9.0/static/sql-do.html
Ясен

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.


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;

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

ПРИМІТКА! Коли я додав коментар після цитованої змінної, він потрапив як частина змінної, коли я спробував деякі методи в інших відповідях. Це насправді на певний час накрутило мене. За допомогою цього методу коментарі трапляються так, як ви очікували.


\set deployment_pass 'string_password' ALTER USER :deployment_user WITH PASSWORD :'deployment_pass';
Ясен

\ set не SQL, це вбудована команда sqql sql, коментарі sql не підтримуються.
Ясен

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)

WTF це? Це не ціле число , це рядок 999; !

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

5
Причина, яка з комою робить для вас несподівані речі, полягає в тому, що крапка з комою закінчується оператором SQL, але ви вводите команду psql \ set, яка не є SQL, і НЕ бере крапку з комою. Подавання крапки з комою на наступний рядок не зашкодить, але абсолютно нічого не робить. Це порожнє твердження.
volkerk

1

Я опублікував нове рішення для цього в іншій темі .

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.