Comment créer une table temporaire en utilisant VALUES dans PostgreSQL

38

J'apprends PostgreSQL et j'essaie de comprendre comment créer une table temporaire ou une WITHdéclaration pouvant être utilisée à la place d'une table normale, à des fins de débogage.

J'ai consulté la documentation de CREATE TABLE et il est indiqué VALUESque cette requête peut être utilisée comme requête mais ne donne aucun exemple. la documentation de la VALUESclause qui y est liée n'a pas d'exemple non plus?

Donc, j'ai écrit un test simple comme suit:

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

Mais PostgreSQL (9.3) se plaint de

erreur de syntaxe à ou proche de "AS"

Mes questions sont:

  1. Comment puis-je corriger la déclaration ci-dessus?

  2. Comment puis-je l'adapter pour l'utiliser dans un WITH block?

Merci d'avance.

tinlyx
la source
J'avais essayé de répondre à cette question avec des conseils plus modernes (car la réponse choisie utilise une syntaxe non standard dépréciée
Evan Carroll

Réponses:

46

EDIT: Je laisse la réponse acceptée telle quelle, mais veuillez noter que la modification ci-dessous, suggérée par a_horse_with_no_name, est la méthode recommandée pour créer une table temporaire à l'aide de VALUES.

Si vous souhaitez simplement sélectionner certaines valeurs plutôt que de simplement créer un tableau et y insérer des éléments, vous pouvez procéder de la manière suivante:

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

Pour créer une table temporaire de la même manière, utilisez:

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

EDIT: Comme indiqué par a_horse_with_no_name, la documentation indique que CREATE TABLE AS...son fonctionnement est similaire à celui-ci SELECT INTO ..., mais que le premier est un sur-ensemble de ce dernier et SELECT INTOest utilisé dans plpgslq pour attribuer une valeur à une variable temporaire - il échouera donc dans ce cas. Par conséquent, bien que les exemples ci-dessus soient valides pour le SQL pur, le CREATE TABLEformulaire doit être privilégié.

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

Remarque, également à partir des commentaires de a_horse_with_no_name et de la question initiale du PO, cela inclut une conversion vers les types de données appropriés dans la liste de valeurs et utilise une instruction CTE (WITH).

En outre, comme indiqué dans la réponse d'Evan Carrol, une requête CTE est une barrière d'optimisation , c'est-à-dire que le CTE est toujours matérialisé. Il existe de nombreuses bonnes raisons d'utiliser des CTE, mais si elles ne sont pas utilisées avec précaution, les performances peuvent être sérieusement compromises. Cependant, dans de nombreux cas, la barrière d'optimisation peut réellement améliorer les performances. Il faut donc en prendre conscience, et non pas l'aveugler.

John Powell
la source
12
de la documentation : " CREATE TABLE AS est fonctionnellement similaire à SELECT INTO. CREATE TABLE AS est la syntaxe recommandée "
a_horse_with_no_name
La clôture d'optimisation n'est pas nécessairement une mauvaise chose. J'ai vu de nombreuses déclarations que je pourrais accorder pour courir massivement plus vite à cause de cela.
a_horse_with_no_name
Bien sûr, j'ai clarifié cela aussi. J'utilise les CTE tout le temps dans un contexte spatial. Si vous avez une clause where avec quelque chose comme WHERE ST_Intersects(geom, (SELECT geom FROM sometable)ou WHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)alors, souvent, le planificateur de requêtes n'utilise pas l'index spatial car la colonne geom n'est plus sargable. Si vous créez votre domaine d’intérêt dans un CTE initial, ce problème disparaît. C'est également très pratique si vous souhaitez utiliser le même objet dans plusieurs expressions supplémentaires dans la même requête, ce qui n'est pas inhabituel dans un contexte SIG.
John Powell le
25

create table as a besoin d'une déclaration 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);

Vous pouvez également réécrire ceci pour utiliser un CTE:

create temp table lookup 
as 
with t (key, value) as (
  values 
    (0::int,-99999::numeric), 
    (1::int,100::numeric)
)
select * from t;
un cheval sans nom
la source
1
Merci pour votre commentaire. Votre approche est évidemment meilleure pour les raisons énoncées dans la documentation. J'ai édité ma réponse, bien que près de 5 ans de retard.
John Powell
11

Le problème concerne les types de données. Si vous les supprimez, la déclaration fonctionnera:

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

Vous pouvez définir les types en convertissant les valeurs de la première ligne:

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

Vous n'avez vraiment pas besoin de créer une table ni d'utiliser un CTE, si tout ce dont vous avez besoin est d'utiliser quelques valeurs dans vos requêtes. Vous pouvez les aligner:

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

Vous pouvez ensuite obtenir un produit cartésien avec un CROSS JOIN(où l'autre relation peut être, bien sûr, une table, une vue, etc.). par exemple:

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

qui donne:

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

Ou JOINles valeurs avec une autre relation (qui peut encore être une table, une vue, etc.), par exemple:

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;

qui donne:

key |val    |color |lookup_key |
----|-------|------|-----------|
1   |100    |Red   |1          |
0   |-99999 |White |0          |
1   |100    |Blue  |1          |
isapir
la source
OK mais la question était "comment créer une table temporaire avec ...?"
Ypercubeᵀᴹ
Oui, mais pourquoi auriez-vous besoin d'une table temporaire avec quelques valeurs de recherche fixes si vous ne la joignez pas à une autre relation? Cette solution résout le problème lui-même, quelle que soit la formulation de la question.
Isapir
1
Peut-être que OP vient de résumer l’exemple à une question qu’il serait facile de poser comme question, mais les données réelles contiennent-elles des milliers de valeurs?
Stannius
Le PO a expressément indiqué l' utilisation de valeurs, donc ma réponse est toujours valable, car c'est exactement ce qu'elle fait
isapir
2

D'abord, utilisez toujours le standard CREATE TABLE AS, SELECT INTOcomme suggéré dans d'autres réponses est une syntaxe obsolète depuis plus de dix ans. Vous pouvez utiliserCREATE TABLE AS avec un CTE

Bien que de nombreuses réponses suggèrent d'utiliser un CTE, ce n'est pas préférable. En fait, c'est probablement un peu plus lent. Juste envelopper comme une table.

DROP TABLE IF EXISTS lookup;

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

Si vous devez écrire une instruction select, vous pouvez également le faire (et vous n'avez pas besoin d'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 dans PostgreSQL force la matérialisation. C'est une clôture d'optimisation. Pour cette raison, ce n’est généralement pas une bonne idée de les utiliser n’importe où, sauf lorsque vous en comprenez les coûts et que vous le savez pour améliorer les performances. Vous pouvez voir le ralentissement ici, par exemple,

\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
la source
J'ai mis à jour la réponse pour refléter la norme et indiquer comment la réponse acceptée n'est pas toujours équivalente à CREATE TABLE AS et ajouté un commentaire sur la barrière d'optimisation, ce qui est un très bon point à aborder. Les CTE apportent de nombreux avantages, mais il est vrai que s'ils sont utilisés à l'aveuglette, ils peuvent conduire à des performances horribles.
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
la source