如何在PostgreSQL中使用VALUES创建临时表


38

我正在学习PostgreSQL,并试图弄清楚如何创建一个临时表或WITH声明来代替常规表,以进行调试。

我查看了CREATE TABLE的文档,它说VALUES可以用作查询,但没有给出示例。VALUES其中链接的子句的文档也没有示例吗?

因此,我编写了一个简单的测试,如下所示:

DROP TABLE IF EXISTS lookup;
CREATE TEMP TABLE lookup (
  key integer,
  val numeric
) AS
VALUES (0,-99999), (1,100);

但是PostgreSQL(9.3)抱怨

“ AS”处或附近的语法错误

我的问题是:

  1. 我该如何修正以上陈述?

  2. 我如何适应它的使用WITH block

提前致谢。


我试图用一些更现代的建议来回答这个问题(由于选择的答案是使用不赞成使用的非标准化语法)dba.stackexchange.com/a/201575/2639
Evan Carroll

Answers:


46

编辑:我将原样保留原来的答案,但是请注意,a_horse_with_no_name建议的以下编辑是使用VALUES创建临时表的首选方法。

如果您只想从一些值中选择,而不是仅创建一个表并将其插入,则可以执行以下操作:

WITH  vals (k,v) AS (VALUES (0,-9999), (1, 100)) 
SELECT * FROM vals;

要以类似方式实际创建临时表,请使用:

WITH  vals (k,v) AS (VALUES (0,-9999), (1, 100)) 
SELECT * INTO temporary table temp_table FROM vals;

编辑:正如a_horse_with_no_name所指出的,在文档中它声明了CREATE TABLE AS...与相似的功能SELECT INTO ...,但是前者是后者的超集,并且SELECT INTO在plpgslq中用于为临时变量分配值-否则它将失败这种情况。因此,尽管以上示例对纯SQL有效,但CREATE TABLE应首选该形式。

CREATE TEMP TABLE temp_table AS                                     
WITH t (k, v) AS (
 VALUES
 (0::int,-99999::numeric), 
 (1::int,100::numeric)
)
SELECT * FROM t;

注意,同样来自a_horse_with_no_name的注释,以及在OP的原始问题中,这包括对值列表内正确数据类型的强制转换,并使用CTE(WITH)语句。

同样,正如Evan Carrol的答案所指出的那样,CTE查询是一个优化围栏,即CTE始终是实体化的。使用CTE的理由很多,但如果使用不当,则会对性能造成重大影响。但是,在许多情况下,优化围栏实际上可以提高性能,因此需要注意这一点,而不是盲目避免。


12
来自文档:“ CREATE TABLE AS在功能上类似于SELECT
INTO。CREATE

优化围栏不一定是一件坏事。因此,我已经看到许多声明,可以调整它们以使其运行更快。
a_horse_with_no_name

当然,我也做了澄清。我一直在空间环境中使用CTE。如果您的where子句带有类似WHERE ST_Intersects(geom, (SELECT geom FROM sometable)或的WHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)字样,则查询计划者通常不使用空间索引,因为geom列中的内容不再是可引用的。如果您在最初的CTE中创建了您感兴趣的领域,那么这个问题就会消失。如果要在同一查询中的多个其他表达式中使用相同的aoi,这也非常方便,这在GIS上下文中并不罕见。
约翰·鲍威尔

25

create table as 需要一条选择语句:

DROP TABLE IF EXISTS lookup;
CREATE TEMP TABLE lookup 
as 
select *
from (
   VALUES 
    (0::int,-99999::numeric), 
    (1::int, 100::numeric)
) as t (key, value);

您也可以使用CTE重新编写此代码:

create temp table lookup 
as 
with t (key, value) as (
  values 
    (0::int,-99999::numeric), 
    (1::int,100::numeric)
)
select * from t;

1
感谢您的评论。由于文档中所述的原因,您的方法显然更好。我已经编辑了答案,尽管已经晚了5年。
约翰·鲍威尔

11

问题是数据类型。如果删除它们,该语句将起作用:

CREATE TEMP TABLE lookup
  (key, val) AS
VALUES 
  (0, -99999), 
  (1, 100) ;

您可以通过强制转换第一行的值来定义类型:

CREATE TEMP TABLE lookup 
  (key, val) AS
VALUES 
  (0::bigint, -99999::int), 
  (1, 100) ;

3

如果您只需要在查询中使用一些值,则您实际上不需要创建表或使用CTE。您可以内联它们:

SELECT  *
FROM    (VALUES(0::INT, -99999::NUMERIC), (1, 100)) AS lookup(key, val)

然后,您可以获得具有的笛卡尔积CROSS JOIN(其他关系当然可以是正则表,视图等)。例如:

SELECT  *
FROM    (VALUES(0::int, -99999::numeric), (1, 100)) AS lookup(key, val)
       ,(VALUES('Red'), ('White'), ('Blue')) AS colors(color);

产生:

key |val    |color |
----|-------|------|
0   |-99999 |Red   |
1   |100    |Red   |
0   |-99999 |White |
1   |100    |White |
0   |-99999 |Blue  |
1   |100    |Blue  |

JOIN具有其他关系的值(也可以是常规表,视图等),例如:

SELECT  *
FROM    (VALUES(0::int, -99999::numeric), (1, 100)) AS lookup(key, val)
  JOIN  (VALUES('Red', 1), ('White', 0), ('Blue', 1)) AS colors(color, lookup_key)
          ON colors.lookup_key = lookup.key;

产生:

key |val    |color |lookup_key |
----|-------|------|-----------|
1   |100    |Red   |1          |
0   |-99999 |White |0          |
1   |100    |Blue  |1          |

可以,但是问题是“如何使用...创建临时表?”
ypercubeᵀᴹ

是的,但是为什么您需要一个带有一些固定查找值的临时表,如果不将其加入另一个关系中呢?无论问题的措辞如何,此解决方案都会解决问题本身。
isapir

1
也许OP只是碰巧将示例简化为一个易于发布的问题,但实际数据具有数千个值?
stannius

OP特别声明了使用值,因此我的答案仍然适用,因为这正是它的作用
isapir

2

首先,始终使用标准化的方法CREATE TABLE ASSELECT INTO如其他答案所建议的那样,十多年来已弃用该语法。您可以CREATE TABLE AS与CTE一起使用

虽然这里有许多答案建议使用CTE,但这不是可取的。实际上,它可能会慢一些。只需将其包装成桌子即可。

DROP TABLE IF EXISTS lookup;

CREATE TEMP TABLE lookup(key, value) AS
  VALUES
  (0::int,-99999::numeric),
  (1,100);

如果您必须编写一条select语句,您也可以这样做(并且您不需要CTE)。

CREATE TEMP TABLE lookup(key, value) AS
  SELECT key::int, value::numeric
  FROM ( VALUES
    (0::int,-99999::numeric),
    (1,100)
  ) AS t(key, value);

PostgreSQL中的CTE强制实现。这是一个优化围栏。因此,除非了解成本并知道可以提高性能,否则在任何地方使用它们通常不是一个好主意。例如,您可以在此处看到速度变慢,

\timing
CREATE TABLE foo AS
  SELECT * FROM generate_series(1,1e7);
Time: 5699.070 ms

CREATE TABLE foo AS
  WITH t AS ( SELECT * FROM generate_series(1,1e7) ) 
  SELECT * FROM t;
Time: 6484.516 ms

我已经更新了答案以反映标准,并指出了可接受的答案并不总是等同于CREATE TABLE AS,并在优化围栏上添加了注释,这是提出的一个很好的观点。CTE具有许多优点,但是,如果盲目使用,确实会导致糟糕的性能。
约翰·鲍威尔

-2
WITH u AS (
    SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS account (id,name)
)
SELECT id, name, length(name) from u;
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.