Como criar uma tabela temporária usando VALUES no PostgreSQL

38

Estou aprendendo o PostgreSQL e tentando descobrir como criar uma tabela temporária ou uma WITHdeclaração que possa ser usada no lugar da tabela regular, para fins de depuração.

Eu olhei a documentação para CREATE TABLE e ela diz que VALUESpode ser usada como uma consulta, mas não dá exemplo; a documentação da VALUEScláusula nela vinculada também não tem um exemplo?

Então, eu escrevi um teste simples da seguinte maneira:

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

Mas o PostgreSQL (9.3) está reclamando

erro de sintaxe no "AS" ou próximo a ele

Minhas perguntas são:

  1. Como posso corrigir a declaração acima?

  2. Como posso adaptá-lo para ser usado em um WITH block?

Desde já, obrigado.

tinlyx
fonte
Eu tentei responder a esta pergunta com alguns conselhos mais modernos (como a resposta escolhida está usando um não-padronizados sintaxe obsoleta) dba.stackexchange.com/a/201575/2639
Evan Carroll

Respostas:

46

EDIT: Estou deixando a resposta original aceita como está, mas observe que a edição abaixo, conforme sugerido por a_horse_with_no_name, é o método preferido para criar uma tabela temporária usando VALUES.

Se você deseja selecionar apenas alguns valores, em vez de apenas criar uma tabela e inseri-la, é possível fazer algo como:

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

Para realmente criar uma tabela temporária de maneira semelhante, use:

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

EDIT: Como apontado por a_horse_with_no_name, nos documentos afirma que CREATE TABLE AS...é funcionalmente semelhante a SELECT INTO ..., mas que o primeiro é um superconjunto do último e que SELECT INTOé usado no plpgslq para atribuir um valor a uma variável temporária - por isso falharia em Aquele caso. Portanto, enquanto os exemplos acima são válidos para SQL simples, o CREATE TABLEformulário deve ser preferido.

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

Observe, também dos comentários de a_horse_with_no_name, e na pergunta original do OP, isso inclui uma conversão para os tipos de dados corretos na lista de valores e usa uma instrução CTE (WITH).

Além disso, como apontado na resposta de Evan Carrol, uma consulta CTE é uma cerca de otimização , ou seja, a CTE é sempre materializada. Há muitas boas razões para usar CTEs, mas pode haver um impacto significativo no desempenho, se não for usado com cuidado. Existem, no entanto, muitos casos em que a cerca de otimização pode realmente melhorar o desempenho; portanto, isso é algo que você deve estar ciente, para não evitar cegamente.

John Powell
fonte
12
dos documentos : " CREATE TABLE AS é funcionalmente semelhante a SELECT INTO. CREATE TABLE AS é a sintaxe recomendada "
a_horse_with_no_name
A barreira da otimização não é necessariamente uma coisa ruim. Eu já vi muitas declarações que eu poderia sintonizar para correr muito mais rápido por causa disso.
a_horse_with_no_name 19/07
Claro, eu esclareci isso também. Eu uso CTEs o tempo todo em um contexto espacial. Se você tiver uma cláusula where com algo parecido WHERE ST_Intersects(geom, (SELECT geom FROM sometable)ou WHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)frequentemente, o planejador de consultas não usará o índice espacial porque a coluna geom não será mais sargável. Se você criar sua área de interesse em uma CTE inicial, esse problema desaparecerá. Também é super conveniente, se você deseja usar a mesma AOI em várias expressões adicionais na mesma consulta, o que não é incomum em um contexto GIS.
John Powell
25

create table as precisa de uma instrução select:

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

Você também pode reescrever isso para usar um CTE:

create temp table lookup 
as 
with t (key, value) as (
  values 
    (0::int,-99999::numeric), 
    (1::int,100::numeric)
)
select * from t;
um cavalo sem nome
fonte
1
Obrigado pelo seu comentário. Sua abordagem é obviamente melhor pelas razões indicadas nos documentos. Eu editei minha resposta, embora quase cinco anos atrasada.
John Powell
11

O problema são os tipos de dados. Se você removê-los, a instrução funcionará:

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

Você pode definir os tipos lançando os valores da primeira linha:

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

Você realmente não precisa criar uma tabela nem usar um CTE, se tudo o que você precisa é usar alguns valores em suas consultas. Você pode incorporá-los:

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

Em seguida, você pode obter um produto cartesiano com um CROSS JOIN(onde o outro relacionamento pode ser, é claro, uma tabela regular, exibição etc.). por exemplo:

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

que produz:

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

Ou JOINos valores com outro relacionamento (que novamente pode ser uma tabela, exibição etc.) regulares, por exemplo:

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;

que produz:

key |val    |color |lookup_key |
----|-------|------|-----------|
1   |100    |Red   |1          |
0   |-99999 |White |0          |
1   |100    |Blue  |1          |
isapir
fonte
OK, mas a pergunta era "como criar uma tabela temporária com ...?"
ypercubeᵀᴹ
Sim, mas por que você precisaria de uma tabela temporária com alguns valores de pesquisa fixos, se não fosse para ingressar em outro relacionamento? Esta solução resolve o problema em si, independentemente de como a pergunta está formulada.
Isapir
1
Talvez o OP tenha resumido o exemplo a algo que seria fácil postar como uma pergunta, mas os dados reais têm milhares de valores?
Stannius
O OP declarou especificamente usando valores, então minha resposta ainda se aplica, pois é exatamente o que faz
isapir 10/10
2

Primeiro, use sempre o padronizado CREATE TABLE AS, SELECT INTOcomo sugerido em outras respostas, há uma sintaxe obsoleta há mais de uma década. Você pode usarCREATE TABLE AS com um CTE

Embora muitas respostas aqui estejam sugerindo o uso de um CTE, isso não é preferível. De fato, é provavelmente um pouco mais lento. Apenas embrulhe como uma mesa.

DROP TABLE IF EXISTS lookup;

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

Se você precisar escrever uma instrução select, também poderá fazer isso (e não precisará de um 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);

Um CTE no PostgreSQL força a materialização. É uma cerca de otimização. Por esse motivo, geralmente não é uma boa ideia usá-los em qualquer lugar, exceto quando você entende os custos e sabe que isso proporciona uma melhoria de desempenho. Você pode ver a desaceleração aqui, por exemplo,

\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
Evan Carroll
fonte
Atualizei a resposta para refletir o padrão e aponto como a resposta aceita nem sempre é equivalente a CREATE TABLE AS e adicionei um comentário sobre a cerca de otimização, que é um ponto muito bom para abordar. Os CTEs trazem muitas vantagens, mas é verdade que, se usado cegamente, pode levar a um desempenho horrível.
John Powell
-2
WITH u AS (
    SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS account (id,name)
)
SELECT id, name, length(name) from u;
caub
fonte