Как объявить переменную в запросе PostgreSQL


242

Как мне объявить переменную для использования в запросе PostgreSQL 8.3?

В MS SQL Server я могу сделать это:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

Как мне сделать то же самое в PostgreSQL? В соответствии с документацией переменные объявляются просто как «name type;», но это дает мне синтаксическую ошибку:

myvar INTEGER;

Может ли кто-нибудь привести пример правильного синтаксиса?


2
Это можно сделать только в PostgreSQL. См. Ответ на этот связанный вопрос: stackoverflow.com/questions/766657/…
Шон Боб

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 как vriables. Но затем я быстро столкнулся с проблемой, когда различные запросы на изменение данных в CTE не гарантируют увидеть влияние друг друга. Мне пришлось использовать несколько CTE, так как мне нужно было использовать эту переменную в нескольких запросах.
Зия Уль Рехман

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
Это идеально для случая, когда у меня есть скрипт миграции, в который я хочу импортировать некоторые реляционные данные. Очевидно, я не буду знать идентификатор последовательности, в которой даны реляционные данные.
Relequestual

3
Я только что попробовал этот подход и нашел, возможно, лучший способ: JOIN myconstants ON trueи тогда нет необходимости делать подвыбор.
вектор

7
Это работает только в пределах одного запроса, вы не можете совместно использовать WITHCTE для запросов в транзакции.
Дейнит

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;

Настройки конфигурации всегда являются значениями varchar, поэтому вам необходимо привести их к правильному типу данных при их использовании. Это работает с любым клиентом 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должно быть в нижнем регистре
Делюан

db = # \ set profile_id 102 db = #: profile_id; ОШИБКА: синтаксическая ошибка в или около "102" ЛИНИЯ 1: 102; ^
AlxVallejo

1
@AlxVallejo вы должны использовать его в операторах и консоли PSQL . db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
Эверис

21

Использование Temp Table вне 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 к нескольким схемам, и список будет из схем, к которым они будут применяться.
Джавадба

1

В DBeaver вы можете использовать параметры в запросах так же, как в коде, так что это будет работать:

SELECT *
FROM somewhere
WHERE something = :myvar

Когда вы запустите запрос, DBeaver запросит у вас значение для: myvar и запустите запрос.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.