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

151

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

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

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

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

myvar INTEGER;

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

Теги:
postgresql-8.3

9 ответов

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

В 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) > ...;
  • 14
    Кто-нибудь хочет объяснить, почему этот ответ устарел?
  • 39
    Итак, как вы делаете это в pl / PgSQL ?!
Показать ещё 1 комментарий
162

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

WITH myconstants (var1, var2) as (
   values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
   OR something_else = var2;
  • 2
    Это прекрасно работает в большинстве случаев, когда вам нужны переменные. Однако, если вы хотите использовать переменную для LIMIT (которая не может содержать переменные), вам следует использовать \set как предложено в ответе Шахриара Агаджани.
  • 1
    Это идеально для случая, когда у меня есть скрипт миграции, в который я хочу импортировать некоторые реляционные данные. Очевидно, я не буду знать идентификатор последовательности, в которой даны реляционные данные.
Показать ещё 6 комментариев
58

Вы также можете попробовать это в 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 или новее.

  • 0
    Оператор DO был добавлен в PostgreSQL 9.0 и не работает в 8.3.
  • 10
    Используйте CREATE TEMPORARY TABLE или CREATE TEMP TABLE, а не CREATE TABLE. Но в остальном нормально.
40

Это зависит от вашего клиента.

Однако, если вы используете клиент psql, вы можете использовать следующее:

my_db=> \set myvar 5
my_db=> SELECT :myvar  + 1 AS my_var_plus_1;
 my_var_plus_1 
---------------
             6
  • 1
    \set должен быть в нижнем регистре
  • 0
    db = # \ set profile_id 102 db = #: profile_id; ОШИБКА: синтаксическая ошибка в или около "102" ЛИНИЯ 1: 102; ^
36

Настройки динамической конфигурации

вы можете "злоупотреблять" динамическими настройками конфигурации для этого:

-- choose some prefix that is unlikey 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

  • 1
    может ли это 'установить сессию my.vars.id' = '1'; быть конкретным разрезом ???
  • 3
    @BrijanElwadhi: да, это транзакционный.
Показать ещё 3 комментария
19

Использование таблицы Temp вне 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;
7

Я хочу предложить улучшение @DarioBarrionuevo answer, чтобы упростить использование временных таблиц.

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;
  • 0
    хорошее решение для решения блока DO не может вернуть набор данных!
4

Вот пример использования операторов PREPARE. Вы все еще не можете использовать ? , но вы можете использовать обозначение $n:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;
  • 0
    Работает довольно хорошо! Благодарю.
0

Это решение основано на предложении 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.

Ещё вопросы

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