Come creare una tabella temporanea usando VALUES in PostgreSQL


38

Sto imparando PostgreSQL e sto cercando di capire come creare una tabella temporanea o una WITHdichiarazione che può essere utilizzata al posto della tabella normale, per scopi di debug.

Ho guardato la documentazione per CREATE TABLE e dice che VALUESpuò essere usato come una query ma non fornisce alcun esempio; la documentazione per la VALUESclausola ivi collegata non ha neanche un esempio?

Quindi, ho scritto un semplice test come segue:

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

Ma PostgreSQL (9.3) si lamenta

errore di sintassi vicino o "AS"

Le mie domande sono:

  1. Come posso risolvere la dichiarazione sopra?

  2. Come posso adattarlo per essere utilizzato in un WITH block?

Grazie in anticipo.


Ho provato a rispondere a questa domanda con alcuni consigli più moderni (poiché la risposta scelta utilizza una sintassi non standardizzata deprecata) dba.stackexchange.com/a/201575/2639
Evan Carroll

Risposte:


46

EDIT: Lascio la risposta originale accettata così com'è, ma tieni presente che la modifica di seguito, come suggerito da a_horse_with_no_name, è il metodo preferito per creare una tabella temporanea usando VALUES.

Se vuoi solo selezionare alcuni valori, anziché creare una tabella e inserirla in essa, puoi fare qualcosa del tipo:

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

Per creare effettivamente una tabella temporanea in modo simile, utilizzare:

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

EDIT: Come sottolineato da a_horse_with_no_name, nei documenti afferma che CREATE TABLE AS...è funzionalmente simile a SELECT INTO ..., ma che il primo è un superset di quest'ultimo e che SELECT INTOviene utilizzato in plpgslq per assegnare un valore a una variabile temporanea - quindi fallirebbe questo caso. Pertanto, mentre gli esempi sopra riportati sono validi per SQL semplice, il CREATE TABLEmodulo dovrebbe essere preferito.

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

Si noti, anche dai commenti di a_horse_with_no_name, e nella domanda originale dell'OP, questo include un cast ai tipi di dati corretti all'interno dell'elenco dei valori e utilizza un'istruzione CTE (WITH).

Inoltre, come sottolineato nella risposta di Evan Carrol, una query CTE è una barriera di ottimizzazione , ovvero la CTE viene sempre materializzata. Ci sono molte buone ragioni per usare i CTE, ma può esserci un notevole calo delle prestazioni, se non usato con attenzione. Vi sono, tuttavia, molti casi in cui il recinto di ottimizzazione può effettivamente migliorare le prestazioni, quindi questo è qualcosa di cui essere consapevoli, da non evitare alla cieca.


12
dalla documentazione : " CREATE TABLE AS è funzionalmente simile a SELECT INTO. CREATE TABLE AS è la sintassi consigliata "
a_horse_with_no_name

La recinzione di ottimizzazione non è necessariamente una cosa negativa. Ho visto molte affermazioni che potrei sintonizzare per correre enormemente più veloce a causa di ciò.
a_horse_with_no_name

Certo, ho chiarito anche quello. Uso i CTE in ogni momento in un contesto spaziale. Se si dispone di una clausola where con qualcosa di simile WHERE ST_Intersects(geom, (SELECT geom FROM sometable)o WHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)spesso il pianificatore di query non utilizza l'indice spaziale perché la colonna geom non è più eseguibile. Se si crea la propria area di interesse in un CTE iniziale, questo problema scompare. È anche molto conveniente, se si desidera utilizzare lo stesso aoi in più ulteriori espressioni nella stessa query, cosa non rara in un contesto GIS.
John Powell,

25

create table as ha bisogno di una dichiarazione selezionata:

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

Puoi anche riscriverlo per usare un CTE:

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

1
Grazie per il tuo commento. L'approccio è ovviamente migliore per i motivi indicati nei documenti. Ho modificato la mia risposta, anche se con quasi 5 anni di ritardo.
John Powell,

11

Il problema sono i tipi di dati. Se li rimuovi, la dichiarazione funzionerà:

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

Puoi definire i tipi lanciando i valori della prima riga:

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

3

Non è davvero necessario creare una tabella né utilizzare un CTE, se tutto ciò che serve è utilizzare alcuni valori nelle query. Puoi incorporarli:

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

Quindi puoi ottenere un prodotto cartesiano con un CROSS JOIN(dove l'altra relazione può essere, ovviamente, una tabella regolare, una vista, ecc.). per esempio:

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

che produce:

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

O JOIN i valori con un'altra relazione (che di nuovo può essere una tabella regolare, una vista, ecc.), Ad esempio:

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;

che produce:

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

OK, ma la domanda era "come creare una tabella temporanea con ...?"
ypercubeᵀᴹ

Sì, ma perché avresti bisogno di una tabella temporanea con alcuni valori di ricerca fissi se non per unirti ad un'altra relazione? Questa soluzione risolve il problema stesso, indipendentemente da come è formulata la domanda.
Isapir,

1
Forse per caso OP ha ridotto l'esempio a qualcosa che sarebbe facile pubblicare come domanda, ma i dati reali hanno migliaia di valori?
stannius

L'OP ha dichiarato espressamente l' uso di valori, quindi la mia risposta si applica ancora in quanto è esattamente ciò che fa
isapir il

2

Innanzitutto usa sempre lo standardizzato CREATE TABLE AS, SELECT INTOcome suggerito in altre risposte è stata una sintassi deprecata per oltre un decennio. È possibile utilizzareCREATE TABLE AS con un CTE

Mentre molte risposte qui suggeriscono di usare un CTE, non è preferibile. In effetti, è probabilmente un po 'più lento. Basta avvolgerlo come un tavolo.

DROP TABLE IF EXISTS lookup;

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

Se devi scrivere un'istruzione select puoi farlo anche tu (e non hai bisogno di un 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);

Un CTE in PostgreSQL forza la materializzazione. È un recinto di ottimizzazione. Per questo motivo, in genere non è una buona idea usarli ovunque, tranne quando si comprendono i costi e si sa che offre un miglioramento delle prestazioni. Puoi vedere il rallentamento qui, ad esempio,

\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

Ho aggiornato la risposta per riflettere lo standard e ho sottolineato come la risposta accettata non sia sempre equivalente a CREATE TABLE AS e ho aggiunto un commento sulla recinzione di ottimizzazione, che è un ottimo punto da mettere in evidenza. I CTE offrono così tanti vantaggi, ma è vero, se usati alla cieca, possono portare a prestazioni orribili.
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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.