我如何在PostgreSQL 8.3查询中声明一个变量?

在MS SQL Server我可以这样做:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

我如何在PostgreSQL做同样的事情?根据文档,变量被简单地声明为“name type;”,但这给了我一个语法错误:

myvar INTEGER;

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


当前回答

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

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

其他回答

你也可以在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或更高版本。

这取决于你的客户。

然而,如果你正在使用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';

正如您将从其他答案中了解到的那样,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)。 交叉连接cte与另一个表。虽然有CROSS JOIN语法,但旧的逗号语法可读性稍好一些。 注意,我对日期进行了强制转换,以避免SELECT子句中可能出现的问题。我使用了PostgreSQL更短的语法,但您可以使用更正式的CAST('1980-01-01' AS日期)来实现跨方言兼容性。

通常,您希望避免交叉连接,但由于您只是交叉连接单行,因此这只会使用变量数据扩大表。

在许多情况下,不需要包含变量。如果名称与其他表中的名称不冲突,则添加前缀。我把它写在这里是为了说明这一点。

另外,你可以继续添加更多的cte。

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

的确,没有明确的方式来声明单值变量,你能做的是

with myVar as (select "any value really")

然后,要访问存储在这个结构中的值,就必须这样做

(select * from myVar)

例如

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

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