在 PostgreSQL 查询中声明变量

发布于 2024-08-06 06:27:17 字数 403 浏览 9 评论 0原文

如何声明在 PostgreSQL 8.3 查询中使用的变量?

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

DECLARE @myvar INT;
SET @myvar = 5/
    
SELECT * FROM somewhere WHERE something = @myvar;

How do I do the same in PostgreSQL?根据文档,变量被简单地声明为“名称类型;”,但这给了我一个语法错误:

myvar INTEGER;

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

How do I declare a variable for use in a PostgreSQL 8.3 query?

In MS SQL Server I can do this:

DECLARE @myvar INT;
SET @myvar = 5/
    
SELECT * FROM somewhere WHERE something = @myvar;

How do I do the same in PostgreSQL? According to the documentation variables are declared simply as "name type;", but this gives me a syntax error:

myvar INTEGER;

Could someone give me an example of the correct syntax?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

半边脸i 2024-08-13 06:27:17

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

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

I accomplished the same goal by using a WITH clause, it's nowhere near as elegant but can do the same thing. Though for this example it's really overkill. I also don't particularly recommend this.

WITH myconstants (var1, var2) as (
   values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
   OR something_else = var2;
凌乱心跳 2024-08-13 06:27:17

PostgreSQL 中没有这样的功能。您只能在 pl/PgSQL(或其他 pl/*)中执行此操作,但不能在普通 SQL 中执行此操作。

一个例外是 WITH () 查询,它可以用作变量,甚至可以用作变量的元组。它允许您返回临时值表。

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

There is no such feature in PostgreSQL. You can do it only in pl/PgSQL (or other pl/*), but not in plain SQL.

An exception is WITH () query which can work as a variable, or even tuple of variables. It allows you to return a table of temporary values.

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) > ...;
灯下孤影 2024-08-13 06:27:17

您也可以在 PLPGSQL 中尝试此操作:

DO $
DECLARE myvar integer;
BEGIN
    SELECT 5 INTO myvar;
    
    DROP TABLE IF EXISTS tmp_table;
    CREATE TEMPORARY TABLE tmp_table AS
    SELECT * FROM yourtable WHERE   id = myvar;
END $;

SELECT * FROM tmp_table;

以上需要 Postgres 9.0 或更高版本。

You could also try this in PLPGSQL:

DO $
DECLARE myvar integer;
BEGIN
    SELECT 5 INTO myvar;
    
    DROP TABLE IF EXISTS tmp_table;
    CREATE TEMPORARY TABLE tmp_table AS
    SELECT * FROM yourtable WHERE   id = myvar;
END $;

SELECT * FROM tmp_table;

The above requires Postgres 9.0 or later.

贩梦商人 2024-08-13 06:27:17

动态配置设置

您可以为此“滥用”动态配置设置:

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

Dynamic Config Settings

you can "abuse" dynamic config settings for this:

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

Config settings are always varchar values, so you need to cast them to the correct data type when using them. This works with any SQL client whereas \set only works in psql

The above requires Postgres 9.2 or later.

For previous versions, the variable had to be declared in postgresql.conf prior to being used, so it limited its usability somewhat. Actually not the variable completely, but the config "class" which is essentially the prefix. But once the prefix was defined, any variable could be used without changing postgresql.conf

萌︼了一个春 2024-08-13 06:27:17

这取决于您的客户。

但是,如果您使用的是 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';

It depends on your client.

However, if you're using the psql client, then you can use the following:

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

If you are using text variables you need to quote.

\set myvar 'sometextvalue'
select * from sometable where name = :'myvar';
不甘平庸 2024-08-13 06:27:17

该方案基于 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 Con​​stant_name,这对于不同于 postgresql 的其他查询语言可能无效。

This solution is based on the one proposed by fei0x but it has the advantages that there is no need to join the value list of constants in the query and constants can be easily listed at the start of the query. It also works in recursive queries.

Basically, every constant is a single-value table declared in a WITH clause which can then be called anywhere in the remaining part of the query.

  • Basic example with two constants:
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)

Alternatively you can use SELECT * FROM constant_name instead of TABLE constant_name which might not be valid for other query languages different to postgresql.

墨落成白 2024-08-13 06:27:17

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

Using a Temp Table outside of pl/PgSQL

Outside of using pl/pgsql or other pl/* language as suggested, this is the only other possibility I could think of.

begin;
select 5::int as var into temp table myvar;
select *
  from somewhere s, myvar v
 where s.something = v.var;
commit;
情归归情 2024-08-13 06:27:17

我想对 @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;

I want to propose an improvement to @DarioBarrionuevo's answer, to make it simpler leveraging temporary tables.

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;
九歌凝 2024-08-13 06:27:17

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

WITH vars AS (
    SELECT 5 AS myvar
)
SELECT *
FROM somewhere,vars
WHERE something = vars.myvar;

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

WITH vars AS (
    SELECT
        '1980-01-01'::date AS start,
        '1999-12-31'::date AS end,
        (SELECT avg(height) FROM customers) AS avg_height
)
SELECT *
FROM customers,vars
WHERE (dob BETWEEN vars.start AND vars.end) AND height<vars.avg_height;

该过程是:

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

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

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

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

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

As you will have gathered from the other answers, PostgreSQL doesn’t have this mechanism in straight SQL, though you can now use an anonymous block. However, you can do something similar with a Common Table Expression (CTE):

WITH vars AS (
    SELECT 5 AS myvar
)
SELECT *
FROM somewhere,vars
WHERE something = vars.myvar;

You can, of course, have as many variables as you like, and they can also be derived. For example:

WITH vars AS (
    SELECT
        '1980-01-01'::date AS start,
        '1999-12-31'::date AS end,
        (SELECT avg(height) FROM customers) AS avg_height
)
SELECT *
FROM customers,vars
WHERE (dob BETWEEN vars.start AND vars.end) AND height<vars.avg_height;

The process is:

  • Generate a one-row cte using SELECT without a table (in Oracle you will need to include FROM DUAL).
  • CROSS JOIN the cte with the other table. Although there is a CROSS JOIN syntax, the older comma syntax is slightly more readable.
  • Note that I have cast the dates to avoid possible issues in the SELECT clause. I used PostgreSQL’s shorter syntax, but you could have used the more formal CAST('1980-01-01' AS date) for cross-dialect compatibility.

Normally, you want to avoid cross joins, but since you’re only cross joining a single row, this has the effect of simply widening the table with the variable data.

In many cases, you don’t need to include the vars. prefix if the names don’t clash with the names in the other table. I include it here to make the point clear.

Also, you can go on to add more CTEs.

This also works in all current versions of MSSQL and MySQL, which do support variables, as well as SQLite which doesn’t, and Oracle which sort of does and sort of doesn’t.

烈酒灼喉 2024-08-13 06:27:17

您可以诉诸工具的特殊功能。就像 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);

You may resort to tool special features. Like for DBeaver own proprietary syntax:

@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);
爱人如己 2024-08-13 06:27:17

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

with myVar as (select "any value really")

,要访问此构造中存储的值,您可以

(select * from myVar)

这样做

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

True, there is no vivid and unambiguous way to declare a single-value variable, what you can do is

with myVar as (select "any value really")

then, to get access to the value stored in this construction, you do

(select * from myVar)

for example

with var as (select 123)    
... where id = (select * from var)
命比纸薄 2024-08-13 06:27:17

以下是使用 PREPARE 语句 的示例。您仍然不能使用 ?,但可以使用 $n 表示法:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;

Here is an example using PREPARE statements. You still can't use ?, but you can use $n notation:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;
没有你我更好 2024-08-13 06:27:17

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

SELECT *
FROM somewhere
WHERE something = :myvar

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

In DBeaver you can use parameters in queries just like you can from code, so this will work:

SELECT *
FROM somewhere
WHERE something = :myvar

When you run the query DBeaver will ask you for the value for :myvar and run the query.

方觉久 2024-08-13 06:27:17

这是在 postges 终端中使用普通变量的代码段。我已经用过几次了。但需要想出更好的办法。在这里我正在使用字符串变量。使用整数变量,不需要三引号。三引号在查询时变成单引号;否则你会遇到语法错误。在使用字符串变量时,可能有一种方法可以消除三引号的需要。如果您找到改进的方法,请更新。

\set strainname '''B.1.1.7'''

select *
from covid19strain
where name = :strainname ;

Here is a code segment using plain variable in postges terminal. I have used it a few times. But need to figure a better way. Here I am working with string variable. Working with integer variable, you don't need the triple quote. Triple quote becomes single quote at query time; otherwise you got syntax error. There might be a way to eliminate the need of triple quote when working with string variables. Please update if you find a way to improve.

\set strainname '''B.1.1.7'''

select *
from covid19strain
where name = :strainname ;
平生欢 2024-08-13 06:27:17

在 psql 中,您可以将这些“变量”用作宏。请注意,它们在每次使用时都会“评估”,而不是在“设置”时进行。

简单的例子:

\set my_random '(SELECT random())'
select :my_random;  -- gives  0.23330629315990592
select :my_random;  -- gives  0.67458399344433542

每次都会给出两个不同答案。

但是,您仍然可以使用它们作为有价值的速记,以避免重复大量子选择。

\set the_id '(SELECT id FROM table_1 WHERE name = ''xxx'' LIMIT 1)'

然后在稍后的查询中使用它,

:the_id 

例如

INSERT INTO table2 (table1_id,x,y,z) VALUES (:the_id, 1,2,3)

请注意,您必须对变量中的字符串加双引号,因为整个内容都会被字符串插值(即宏扩展)到您的查询中。

In psql, you can use these 'variables' as macros. Note that they get "evaluated" every time they are used, rather than at the time that they are "set".

Simple example:

\set my_random '(SELECT random())'
select :my_random;  -- gives  0.23330629315990592
select :my_random;  -- gives  0.67458399344433542

this gives two different answers each time.

However, you can still use these as a valuable shorthand to avoid repeating lots of subselects.

\set the_id '(SELECT id FROM table_1 WHERE name = ''xxx'' LIMIT 1)'

and then use it in your queries later as

:the_id 

e.g.

INSERT INTO table2 (table1_id,x,y,z) VALUES (:the_id, 1,2,3)

Note you have to double-quote the strings in the variables, because the whole thing is then string-interpolated (i.e. macro-expanded) into your query.

倾城月光淡如水﹏ 2024-08-13 06:27:17

例如,您可以设置 自定义选项 my.nummy.first.name 作为变量,如下所示。 *一个自定义选项可以有多个.,并且自定义选项在注销后将被删除:

SET my.num = 2;
SET my.first.name = 'John';

或者,您可以使用set_config() 如下所示。 *对于 set_config(),第三个参数中的 false 表示新值适用于当前会话并且 <第三个参数中的 code>true 表示新值适用于当前交易并且我的回答详细解释了set_config()

SELECT set_config('my.num', '2', false);
SELECT set_config('my.first.name', 'John', false);

那么,你必须使用current_setting() 获取自定义选项的值 my.num< /code> 和 my.first.name 如下所示:

postgres=# SELECT current_setting('my.num');
 current_setting
-----------------
 2
(1 row)
postgres=# SELECT current_setting('my.first.name');
 current_setting
-----------------
 John
(1 row)

小心,设置自定义选项时不使用 . 获取 错误如下所示:

postgres=# SET num = 2;
ERROR:  unrecognized configuration parameter "num"

或者:

postgres=# SELECT set_config('num', '2', false);
ERROR:  unrecognized configuration parameter "num"

并且,2 中没有 '' set_config() 获取 错误如下所示:

postgres=# SELECT set_config('my.num', 2, false);
ERROR:  function set_config(unknown, integer, boolean) does not exist
LINE 1: SELECT set_config('my.num', 2, false);
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

并且,使用不带current_setting()的自定义选项无法获取如下所示的值:

postgres=# SELECT 'my.num';
 ?column?
----------
 my.num
(1 row)

并且,使用不带''的自定义选项并且 current_setting() 得到错误 如下图:

postgres=# SELECT my.num;
ERROR:  missing FROM-clause entry for table "my"
LINE 1: SELECT my.num;

接下来例如可以使用 \set 将数字 2 设置为 num ,如下所示。 *您必须根据文档 否则会出现错误,num 会在注销后被删除:

postgres=# \set num 2
postgres=# SELECT :num;
 ?column?
----------
        2
(1 row)

并且,您可以使用 \set将带有 '' 的文本 John Smith 设置为 name,如下所示。 *请勿将 "" 用于 John Smith,因为 "" 包含在文本中,因此输出为 "John Smith" 并且您必须使用 '' 作为 name,否则会出现错误,并且 name 在注销后会被删除:

postgres=# \set name 'John Smith'
postgres=# SELECT :'name';
  ?column?
------------
 John Smith
(1 row)

设置文本时要小心John Smith 不带 ''name 会删除 JohnSmith 之间的空格,如下所示如下所示:

postgres=# \set name John Smith
postgres=# SELECT :'name';
 ?column?
-----------
 JohnSmith
(1 row)

并且,仅使用 \set 即可显示所有变量,如下所示:

postgres=# \set
AUTOCOMMIT = 'on'
COMP_KEYWORD_CASE = 'preserve-upper'
...
name = 'John Smith'
num = '2'

并且,您可以使用 \unset 取消设置(删除)numname 作为如下所示:

postgres=# \unset num
postgres=# \unset name

另外,您可以使用 \echo 输出 num 如下所示:

postgres=# \set num 2
postgres=# \echo :num
2

并且,您可以使用 \echo 输出 name 如下所示。 *请勿使用 '' 作为 name,因为 '' 包含在文本中,因此输出为 'John Smith'< /代码>:

postgres=# \set name 'John Smith'
postgres=# \echo :name
John Smith

For example, you can set the custom options my.num and my.first.name as variables as shown below. *A custom option can have multiple . and custom options are deleted after logout:

SET my.num = 2;
SET my.first.name = 'John';

Or, you can set the custom options with set_config() as shown below. *For set_config(), false in the 3rd argument means the new value applies to the current session and true in the 3rd argument means the new value applies to the current transaction and my answer explains set_config() in detail:

SELECT set_config('my.num', '2', false);
SELECT set_config('my.first.name', 'John', false);

Then, you must use current_setting() to get the values of the custom options my.num and my.first.name as shown below:

postgres=# SELECT current_setting('my.num');
 current_setting
-----------------
 2
(1 row)
postgres=# SELECT current_setting('my.first.name');
 current_setting
-----------------
 John
(1 row)

Be careful, setting a custom option without . gets the error as shown below:

postgres=# SET num = 2;
ERROR:  unrecognized configuration parameter "num"

Or:

postgres=# SELECT set_config('num', '2', false);
ERROR:  unrecognized configuration parameter "num"

And, 2 without '' in set_config() gets the error as shown below:

postgres=# SELECT set_config('my.num', 2, false);
ERROR:  function set_config(unknown, integer, boolean) does not exist
LINE 1: SELECT set_config('my.num', 2, false);
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

And, using a custom option without current_setting() cannot get the value as shown below:

postgres=# SELECT 'my.num';
 ?column?
----------
 my.num
(1 row)

And, using a custom option without '' and current_setting() gets the error as shown below:

postgres=# SELECT my.num;
ERROR:  missing FROM-clause entry for table "my"
LINE 1: SELECT my.num;

Next for example, you can use \set to set the number 2 to num as shown below. *You must use : to access num according to the doc otherwise there is error and num is removed after logout:

postgres=# \set num 2
postgres=# SELECT :num;
 ?column?
----------
        2
(1 row)

And, you can use \set to set the text John Smith with '' to name as shown below. *Don't use "" for John Smith because "" is included in the text so the output is "John Smith" and you must use '' for name otherwise there is error and name is removed after logout:

postgres=# \set name 'John Smith'
postgres=# SELECT :'name';
  ?column?
------------
 John Smith
(1 row)

Be careful, setting the text John Smith without '' to name removes the space between John and Smith as shown below:

postgres=# \set name John Smith
postgres=# SELECT :'name';
 ?column?
-----------
 JohnSmith
(1 row)

And, only using \set can show all variables as shown below:

postgres=# \set
AUTOCOMMIT = 'on'
COMP_KEYWORD_CASE = 'preserve-upper'
...
name = 'John Smith'
num = '2'

And, you can use \unset to unset(remove) num and name as shown below:

postgres=# \unset num
postgres=# \unset name

In addition, you can use \echo to output num as shown below:

postgres=# \set num 2
postgres=# \echo :num
2

And, you can use \echo to output name as shown below. *Don't use '' for name because '' is included in the text so the output is 'John Smith':

postgres=# \set name 'John Smith'
postgres=# \echo :name
John Smith
千年*琉璃梦 2024-08-13 06:27:17

使用fei0x的答案bluish的建议 您可以将它们组合起来将您的值存储在 TEMP 表中。如果有人想使用日期/时间戳保存到变量中,以下是一个片段。

drop table if exists tempdb_var;
CREATE TEMP TABLE tempdb_var as
with varInit (v_startdate, v_enddate) as (
    values(CURRENT_DATE - '1 day'::interval, CURRENT_DATE + '1 day'::interval)

)
select a.* from varInit a;

select v_startdate,v_enddate from tempdb_var;

您可以在任何查询中使用变量 v_startdatev_enddate

Using fei0x's answer and bluish's suggestion you can combine them to store your values in a TEMP table. Following is a snippet if someone wants to use the date/timestamp to save into the variables.

drop table if exists tempdb_var;
CREATE TEMP TABLE tempdb_var as
with varInit (v_startdate, v_enddate) as (
    values(CURRENT_DATE - '1 day'::interval, CURRENT_DATE + '1 day'::interval)

)
select a.* from varInit a;

select v_startdate,v_enddate from tempdb_var;

You can use the variables v_startdate and v_enddate in any query.

物价感观 2024-08-13 06:27:17

在简单查询中(Postgress)

WITH variables AS ( SELECT 11 AS offerId)

select * from offer_config,variables where offer_id = offerId

In simple query (Postgress)

WITH variables AS ( SELECT 11 AS offerId)

select * from offer_config,variables where offer_id = offerId
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文