web-dev-qa-db-fra.com

Comment créer une table temporaire à l'aide de VALUES dans PostgreSQL

J'apprends PostgreSQL et j'essaie de comprendre comment créer une table temporaire ou une déclaration WITH qui peut être utilisée à la place d'une table régulière, à des fins de débogage.

J'ai regardé la documentation de CREATE TABLE et il est dit que VALUES peut être utilisé comme requête mais ne donne aucun exemple; la documentation de la clause VALUES qui y est liée n'a pas non plus d'exemple?

J'ai donc é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 près 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.

43
tinlyx

EDIT: Je laisse la réponse acceptée d'origine telle quelle, mais veuillez noter que la modification ci-dessous, comme suggéré par a_horse_with_no_name, est la méthode préférée pour créer une table temporaire utilisant VALUES.

Si vous souhaitez simplement sélectionner certaines valeurs, plutôt que de simplement créer un tableau et de l'insérer, vous pouvez faire quelque chose comme:

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

Pour créer une table temporaire de manière similaire, 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, dans la documentation il indique que CREATE TABLE AS... est fonctionnellement similaire à SELECT INTO ..., mais que le premier est un sur-ensemble du second et que SELECT INTO est utilisé dans plpgslq pour assigner une valeur à une variable temporaire - donc il échouerait dans ce cas. Par conséquent, bien que les exemples ci-dessus soient valables pour SQL simple, le CREATE TABLE la forme devrait être préférée.

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 dans la question d'origine de l'OP, cela inclut une conversion vers les types de données corrects à l'intérieur de 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 clôture d'optimisation , c'est-à-dire que le CTE est toujours matérialisé. Il existe de nombreuses bonnes raisons d'utiliser les CTE, mais il peut y avoir un impact significatif sur les performances, s'il n'est pas utilisé avec précaution. Il y a cependant de nombreux cas où la clôture d'optimisation peut réellement améliorer les performances, c'est donc quelque chose à prendre en compte, à ne pas éviter aveuglément.

53
John Powell

create table as a besoin d'une instruction 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;
26

Le problème, ce sont les types de données. Si vous les supprimez, l'instruction 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) ;
12
ypercubeᵀᴹ

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)

Ensuite, vous pouvez obtenir un produit cartésien avec un CROSS JOIN (où l'autre relation peut être, bien sûr, une table régulière, 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);

ce qui donne:

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

Ou JOIN les valeurs avec une autre relation (qui peut encore être une table régulière, 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;

ce qui donne:

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

D'abord, utilisez toujours le standardisé CREATE TABLE AS, SELECT INTO comme suggéré dans d'autres réponses est une syntaxe déconseillée depuis plus d'une décennie. Vous pouvez utiliser CREATE TABLE AS avec un CTE

Bien que de nombreuses réponses suggèrent ici l'utilisation d'un CTE, ce n'est pas préférable. En fait, c'est probablement un peu plus lent. Enveloppez-le simplement sous forme de tableau.

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 le faire aussi (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 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
2
Evan Carroll