Як оголосити змінну в запиті PostgreSQL


241

Як я можу оголосити змінну для використання у запиті PostgreSQL 8.3?

У MS SQL Server я можу це зробити:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

Як зробити те ж саме в PostgreSQL? Згідно з документацією, змінні оголошуються просто як "тип імені;", але це дає мені синтаксичну помилку:

myvar INTEGER;

Чи може хтось надати мені приклад правильного синтаксису?


2
Це можна зробити лише в PostgreSQL. Дивіться відповідь на це пов’язане питання: stackoverflow.com/questions/766657/…
Sean The Bean

2
Ця відповідна відповідь має кращі відповіді: stackoverflow.com/questions/13316773/…
Ервін Брандштеттер

Відповіді:


113

У PostgreSQL такої функції немає. Ви можете це робити тільки в pl / PgSQL (або іншому pl / *), але не в простому SQL.

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

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

Я спробував цей метод CTE використовуватися як змінний. Але я швидко зіткнувся з проблемою, коли різні запити, що змінюють дані в CTE, не гарантовано бачать ефекти один одного. Мені довелося використовувати декілька CTE, як мені потрібно, щоб використовувати цю змінну в декількох запитах.
Zia Ul Rehman

228

Я досягнув тієї ж мети, використовуючи WITHпункт , він ніде не є елегантним, але може зробити те ж саме. Хоча для цього прикладу це справді надмірно. Я також особливо не рекомендую цього.

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

2
Це чудово підходить для більшості випадків, де ви хочете змінних. Однак якщо ви хочете використовувати змінну для LIMIT (яка не може містити змінних), тоді ви хочете використовувати, \setяк пропонується у відповіді Шахріара Агаджані.
cimmanon

1
Це ідеально підходить, коли у мене є сценарій міграції, куди я хочу імпортувати деякі реляційні дані. Очевидно, я не знаю ідентифікатор послідовності, що даються реляційними даними.
Випускний

3
Я просто спробував такий підхід і знайшов, можливо, кращий спосіб: JOIN myconstants ON trueі тоді немає необхідності робити підбір.
vektor

7
Це працює лише в межах одного запиту, ви не можете поділити WITHCTE на запити в транзакції.
Daenyth

2
Старий питання, але тут ось варіант: WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;. CROSS JOIN, будучи з однорядним виразом таблиці, практично дублює дані для всіх рядків у реальній таблиці та спрощує вираз.
Манго

83

Ви також можете спробувати це в 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;

Вищезазначене вимагає Postgres 9.0 або новішої версії.


1
Оператор DO додано в PostgreSQL 9.0 і не працює в 8.3.
Джонні

14
Використовуйте CREATE TEMPORARY TABLE або CREATE TEMP TABLE, а не CREATE TABLE. Але інакше добре.
Стефан Стейгер

60

Параметри динамічної конфігурації

ви можете "зловживати" динамічними налаштуваннями конфігурації для цього:

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

Налаштування конфігурації - це завжди значення варчару, тому потрібно використовувати їх для правильного типу даних при їх використанні. Це працює з будь-яким клієнтом SQL, тоді як \setпрацює лише вpsql

Вищезазначене вимагає Postgres 9.2 або новішої версії.

У попередніх версіях змінна повинна була бути оголошена postgresql.confдо використання, тому вона дещо обмежила її зручність використання. Насправді не змінна повністю, але конфігураційний "клас", який по суті є префіксом. Але як тільки буде визначено префікс, будь-яка змінна може бути використана без зміниpostgresql.conf


3
@BrijanElwadhi: так, це транзакція.
a_horse_with_no_name

2
В якості примітки: деякі слова зарезервовані, наприклад , зміни set session my.vars.id = '1';в set session my.user.id = '1';дастьERROR: syntax error at or near "user"
Dominik

2
@BrijanElwadhi: Для того, щоб зробити змінну конкретну угоду , ви повинні використовувати: SET LOCAL .... sessionЗмінної буде діяти до тих пір , як ви зв'язок. localПоширюється до угоди.
Євген Конков

@dominik Ви можете обійти це обмеження в лапки, наприклад., виклик працює , як очікувалося. set session "my.user.id" = '1';current_setting('my.user.id')
Майлз Елам

58

Це залежить від вашого клієнта.

Однак якщо ви використовуєте клієнт psql , ви можете використовувати наступне:

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

Якщо ви використовуєте текстові змінні, вам потрібно навести цитати.

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

1
\setповинні бути малими літерами
deluan

db = # \ встановити profile_id 102 db = #: profile_id; ПОМИЛКА: помилка синтаксису на або біля лінії "102" ЛІНІЯ 1: 102; ^
AlxVallejo

1
@AlxVallejo вам доведеться використовувати його у консолі оператора та psql . db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
everis

21

Використання таблиці темпів поза pl / PgSQL

Окрім використання pl / pgsql або іншої мови pl / *, як пропонується, це єдина інша можливість, яку я міг би придумати.

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

13

Я хочу запропонувати вдосконалити відповідь @ DarioBarrionuevo , щоб спростити використання тимчасових таблиць.

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;

приємне рішення для вирішення блоку DO не може повернути набір даних!
CodeFarmer

У PostgreSQL 11.0 такий запит повертається 1(імовірно, кількість рядків), а не вміст tmp_table.
Ед Ноепель

9

Це рішення засноване на запропонованому fei0x, але воно має ті переваги, що немає необхідності приєднуватися до списку значень констант у запиті, і константи можуть бути легко перераховані на початку запиту. Він також працює в рекурсивних запитах.

По суті, кожна константа - це таблиця з однозначним значенням, оголошена в пункті WITH, яку потім можна викликати в будь-якій частині решти запиту.

  • Основний приклад з двома константами:
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)

Крім того, ви можете використовувати SELECT * FROM constant_nameзамість TABLE constant_nameяких, можливо, недійсні для інших мов запитів, відмінних від postgresql.



4

Правда, немає жодного яскравого та однозначного способу оголошення однозначної змінної, що ви можете зробити

with myVar as (select "any value really")

тоді, щоб отримати доступ до значення, що зберігається в цій конструкції, ви робите

(select * from myVar)

наприклад

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

3

Ви можете вдатися до спеціальних функцій інструменту. Як і для власного синтаксису власності 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);

Це ближче до корисного: я буду розбиратися, чи підтримує DBeaver списки та циклічність: мені потрібно застосувати один і той же sql до декількох схем, і список буде схемою для їх застосування.
javadba

1

У DBeaver ви можете використовувати параметри в запитах так само, як ви можете з коду, так що це буде працювати:

SELECT *
FROM somewhere
WHERE something = :myvar

При запуску запиту DBeaver запитає вас про значення для: myvar та запустить запит.

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