如何在PostgreSQL查询中声明变量

EMP*_*EMP 205 sql postgresql postgresql-8.3

如何声明变量以用于PostgreSQL 8.3查询?

在MS SQL Server中,我可以这样做:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar
Run Code Online (Sandbox Code Playgroud)

我如何在PostgreSQL中做同样的事情?根据文档变量被简单地声明为"名称类型;",但这给了我一个语法错误:

myvar INTEGER;
Run Code Online (Sandbox Code Playgroud)

有人能给我一个正确语法的例子吗?

fei*_*i0x 196

我通过使用一个WITH条款完成了同样的目标,它没有那么优雅,但可以做同样的事情.虽然这个例子真的有点过分.我也不特别推荐这个.

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

  • 这仅适用于单个查询,您不能在事务中的查询之间共享`WITH`CTE. (5认同)
  • 我只是尝试了这种方法,并找到了一种更好的方法:`JOIN myconstants ON true`然后不需要进行子选择. (3认同)
  • 这适用于大多数需要变量的实例.但是,如果你想为LIMIT使用一个变量(它不能包含变量),那么你应该按照Shahriar Aghajani的回答中的建议使用`\ set`. (2认同)
  • 当我有一个想要导入一些关系数据的迁移脚本时,这是理想的选择。显然我不知道给出的关系数据的序列id。 (2认同)
  • 老问题,但这里是一个变体:`WITH常量AS(SELECT 5 AS var)SELECT*FROM某处CROSS JOIN常量WHERE someting = var;`.CROSS JOIN是一个带有单行表表达式的表达式,它实际上复制了实际表中所有行的数据,并简化了表达式. (2认同)

小智 94

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) > ...;
Run Code Online (Sandbox Code Playgroud)

  • 我尝试了这种将 CTE 用作变量的方法。但是很快我就遇到了一个问题,即 CTE 中不同的数据修改查询不能保证看到彼此的效果。我必须使用多个 CTE,因为我需要在多个查询中使用该变量。 (3认同)

Dar*_*evo 74

你也可以在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;
Run Code Online (Sandbox Code Playgroud)

以上要求Postgres 9.0或更高版本.

  • 使用CREATE TEMPORARY TABLE或CREATE TEMP TABLE,而不是CREATE TABLE.但其他方面很好. (12认同)
  • DO 语句是在 PostgreSQL 9.0 中添加的,在 8.3 中不起作用。 (2认同)

a_h*_*ame 49

动态配置设置

你可以"滥用"动态配置设置:

-- 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;
Run Code Online (Sandbox Code Playgroud)

配置设置始终是varchar值,因此在使用它们时需要将它们转换为正确的数据类型.这适用于任何SQL客户端,而\set只适用于psql

以上要求Postgres 9.2或更高版本.

对于以前的版本,变量必须在使用postgresql.conf之前声明,因此它在某种程度上限制了它的可用性.实际上不是变量完全,但配置"类"本质上是前缀.但是一旦定义了前缀,就可以使用任何变量而无需更改postgresql.conf

  • @BrijanElwadhi:是的,这是交易性的. (3认同)
  • 作为一个方面说明:一些字保留,例如改变`设置会话my.vars.id ="1";``到设置会话my.user.id ="1";`将产生`ERROR:语法错误在或靠近"用户"` (2认同)
  • @BrijanElwadhi:要使可变事务具体化,您必须使用:`SET LOCAL ...`。只要您有连接,`session` 变量就会生效。`local` 的范围是事务。 (2认同)

小智 47

这取决于您的客户.

但是,如果您使用的是psql客户端,则可以使用以下命令:

my_db=> \set myvar 5
my_db=> SELECT :myvar  + 1 AS my_var_plus_1;
 my_var_plus_1 
---------------
             6
Run Code Online (Sandbox Code Playgroud)


Man*_*ngo 21

正如您从其他答案中了解到的那样,PostgreSQL 在直接 SQL 中没有这种机制,尽管您现在可以使用匿名块。但是,您可以使用公用表表达式 (CTE) 执行类似的操作:

\n
WITH vars AS (\n    SELECT 5 AS myvar\n)\nSELECT *\nFROM somewhere,vars\nWHERE something = vars.myvar;\n
Run Code Online (Sandbox Code Playgroud)\n

当然,您可以拥有任意多个变量,并且它们也可以导出。例如:

\n
WITH vars AS (\n    SELECT\n        \'1980-01-01\'::date AS start,\n        \'1999-12-31\'::date AS end,\n        (SELECT avg(height) FROM customers) AS avg_height\n)\nSELECT *\nFROM customers,vars\nWHERE (dob BETWEEN vars.start AND vars.end) AND height<vars.avg_height;\n
Run Code Online (Sandbox Code Playgroud)\n

其过程是:

\n
    \n
  • 使用不带表的方式生成单行 cte SELECT(在 Oracle 中,您需要包含FROM DUAL)。
  • \n
  • CROSS JOIN cte 与另一个表。虽然有CROSS JOIN语法,但较旧的逗号语法可读性稍好一些。
  • \n
  • 请注意,我已经转换了日期以避免该SELECT条款中可能出现的问题。我使用了 PostgreSQL\xe2\x80\x99s 较短的语法,但您可以使用更正式的CAST(\'1980-01-01\' AS date)跨方言兼容性。
  • \n
\n

通常,您希望避免交叉联接,但由于您\xe2\x80\x99 仅交叉联接单个行,因此这会产生简单地使用可变数据扩大表的效果。

\n

vars.在许多情况下,如果名称与其他表中的名称不冲突,则不需要包含前缀。我把它放在这里是为了明确这一点。

\n

此外,您还可以继续添加更多 CTE。

\n

这也适用于支持变量的所有当前版本的 MSSQL 和 MySQL,以及不支持变量的 SQLite,以及支持变量和不支持变量的 Oracle。

\n


小智 20

使用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;
Run Code Online (Sandbox Code Playgroud)


blu*_*ish 11

我想提议对@ 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;
Run Code Online (Sandbox Code Playgroud)


The*_*der 10

在 DBeaver 中,您可以像在代码中一样在查询中使用参数,因此这将起作用:

SELECT *
FROM somewhere
WHERE something = :myvar
Run Code Online (Sandbox Code Playgroud)

当您运行查询时,DBeaver 将询问您 :myvar 的值并运行查询。


Jor*_*uis 7

此解决方案基于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)
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用SELECT * FROM constant_name代替TABLE constant_name对其他不同于postgresql的查询语言无效的语言。

  • 它只能工作一次。如果您编写选择查询两次使用相同的值,则会给出错误“SQL 错误 [42P01]:错误:关系“constant_1_str”不存在位置:20” (2认同)
  • @SatishPatro 是的,这是 CTE 方法的唯一缺点 - 它仅适用于创建 CTE 之后的第一个查询。这个例子可能是 CTE 变量方法的最好版本,IMO (2认同)

Mar*_*sky 5

这是使用PREPARE语句的示例。您仍然不能使用?,但是可以使用$n符号:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;
Run Code Online (Sandbox Code Playgroud)


小智 5

的确,没有一种生动明确的方法来声明单值变量,您可以做的是

with myVar as (select "any value really")
Run Code Online (Sandbox Code Playgroud)

然后,要访问此构造中存储的值,您需要

(select * from myVar)
Run Code Online (Sandbox Code Playgroud)

例如

with var as (select 123)    
... where id = (select * from var)
Run Code Online (Sandbox Code Playgroud)


gav*_*koa 5

您可以求助于工具的特殊功能。就像 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);
Run Code Online (Sandbox Code Playgroud)

  • 这更接近可用:我将研究 DBeaver 是否支持列表和循环:我需要将相同的 sql 应用到多个模式,并且列表将是要应用它们的模式。 (4认同)