如何在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/…–
肖恩·比恩

2
:此相关答案有更好的答案stackoverflow.com/questions/13316773/...
欧文Brandstetter修改

Answers:


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 Mughal

227

我通过使用WITH子句实现了相同的目标,虽然它虽然不那么优雅,但可以做同样的事情。尽管对于这个例子来说,这确实太过分了。我也不特别推荐这样做。

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

2
这对于需要变量的大多数情况非常有用。但是,如果您想对LIMIT使用变量(不能包含变量),那么您将\set按照Shahriar Aghajani的答案中的建议使用。
cimmanon

1
当我具有要导入一些关系数据的迁移脚本时,这是理想的选择。显然,我不知道给出相关数据的序列ID。
Rerequestual '02

3
我只是尝试了这种方法,发现了一个也许更好的方法:JOIN myconstants ON true然后就不需要进行子选择了。
vektor

7
这仅在单个查询中有效,您不能WITH在事务中的所有查询之间共享CTE。
丹妮丝'16

2
旧问题,但这是一个变体:WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;。带有单行表表达式的CROSS JOIN实际上复制了真实表中所有行的数据,并简化了表达式。
曼戈

82

您也可以在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中不起作用。
约翰尼,2015年

14
使用CREATE TEMPORARY TABLE或CREATE TEMP TABLE,而不是CREATE TABLE。但否则很好。
Stefan Steiger,

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属性的范围交易。
Eugen Konkov,

@dominik您可以使用引号来解决该限制,例如,set session "my.user.id" = '1';current_setting('my.user.id')呼叫按预期运行。
Miles Elam

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 '17

db =#\ set profile_id 102 db =#:profile_id; 错误:“ 102”或附近的语法错误LINE 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
Ed Noepel

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的值并运行查询。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.