So erstellen Sie eine temporäre Tabelle mit VALUES in PostgreSQL

38

Ich lerne PostgreSQL und versuche herauszufinden, wie man eine temporäre Tabelle oder eine WITHDeklaration erstellt, die anstelle einer regulären Tabelle zum Debuggen verwendet werden kann.

Ich habe mir die Dokumentation zu CREATE TABLE angesehen und sie besagt, VALUESdass sie als Abfrage verwendet werden kann, aber es gibt kein Beispiel. Die Dokumentation der VALUESdarin verlinkten Klausel hat auch kein Beispiel.

Also schrieb ich einen einfachen Test wie folgt:

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

Aber PostgreSQL (9.3) beschwert sich darüber

Syntaxfehler bei oder in der Nähe von "AS"

Meine Fragen sind:

  1. Wie kann ich die obige Aussage korrigieren?

  2. Wie kann ich es anpassen, um es in einem zu verwenden WITH block?

Danke im Voraus.

Tinlyx
quelle
Ich habe versucht, diese Frage mit moderneren Ratschlägen zu beantworten (da die gewählte Antwort eine veraltete, nicht standardisierte Syntax verwendet). Dba.stackexchange.com/a/201575/2639
Evan Carroll

Antworten:

46

BEARBEITEN: Ich lasse die ursprünglich akzeptierte Antwort unverändert, beachte jedoch, dass die folgende Bearbeitung, wie von a_horse_with_no_name vorgeschlagen, die bevorzugte Methode zum Erstellen einer temporären Tabelle mit VALUES ist.

Wenn Sie nur aus einigen Werten auswählen möchten, anstatt nur eine Tabelle zu erstellen und in diese einzufügen, können Sie Folgendes tun:

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

Um eine temporäre Tabelle auf ähnliche Weise zu erstellen, verwenden Sie:

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

BEARBEITEN: Wie in a_horse_with_no_name ausgeführt, wird in den Dokumenten angegeben , dass dies CREATE TABLE AS...funktional ähnlich SELECT INTO ...ist, dass jedoch erstere eine Obermenge der letzteren SELECT INTOist und in plpgslq zum Zuweisen eines Werts zu einer temporären Variablen verwendet wird - dies würde fehlschlagen dieser Fall. Während die obigen Beispiele für einfaches SQL gültig sind, CREATE TABLEsollte daher die Form bevorzugt werden.

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

Beachten Sie auch, dass in den Kommentaren von a_horse_with_no_name und in der ursprünglichen Frage des OP eine Umwandlung in die richtigen Datentypen in der Werteliste enthalten ist und eine CTE (WITH) -Anweisung verwendet wird.

Wie in der Antwort von Evan Carrol ausgeführt, ist eine CTE-Abfrage auch ein Optimierungsbereich , dh, der CTE wird immer materialisiert. Es gibt viele gute Gründe für die Verwendung von CTEs, aber wenn sie nicht sorgfältig verwendet werden, kann dies zu erheblichen Leistungseinbußen führen. Es gibt jedoch viele Fälle, in denen der Optimierungsbereich die Leistung tatsächlich verbessern kann. Beachten Sie dies, und vermeiden Sie dies nicht blind.

John Powell
quelle
12
Aus den Dokumenten : " CREATE TABLE AS ist funktionsmäßig SELECT INTO ähnlich. CREATE TABLE AS ist die empfohlene Syntax "
a_horse_with_no_name
Der Optimierungszaun ist nicht unbedingt eine schlechte Sache. Ich habe viele Aussagen gesehen, die ich so einstellen könnte, dass sie massiv schneller ablaufen.
a_horse_with_no_name
Klar habe ich das auch geklärt. Ich benutze CTEs die ganze Zeit in einem räumlichen Kontext. Wenn Sie eine where-Klausel mit so etwas wie WHERE ST_Intersects(geom, (SELECT geom FROM sometable)oder haben, verwendet WHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)der Abfrageplaner häufig nicht den räumlichen Index, da die geom-Spalte nicht mehr sargable ist. Wenn Sie Ihren Interessenbereich in einem anfänglichen CTE erstellen, wird dieses Problem behoben. Es ist auch sehr praktisch, wenn Sie dasselbe aoi in mehreren weiteren Ausdrücken in derselben Abfrage verwenden möchten, was in einem GIS-Kontext nicht ungewöhnlich ist.
John Powell
25

create table as braucht eine select-Anweisung:

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

Sie können dies auch neu schreiben, um einen CTE zu verwenden:

create temp table lookup 
as 
with t (key, value) as (
  values 
    (0::int,-99999::numeric), 
    (1::int,100::numeric)
)
select * from t;
ein Pferd ohne Name
quelle
1
Danke für deinen Kommentar. Ihr Ansatz ist offensichtlich aus den in den Dokumenten angegebenen Gründen besser. Ich habe meine Antwort bearbeitet, wenn auch fast 5 Jahre zu spät.
John Powell
11

Das Problem sind die Datentypen. Wenn Sie sie entfernen, funktioniert die Anweisung:

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

Sie können die Typen definieren, indem Sie die Werte der ersten Zeile umwandeln:

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

Sie müssen weder eine Tabelle erstellen noch einen CTE verwenden, wenn Sie nur einige Werte in Ihren Abfragen verwenden möchten. Sie können sie einbinden:

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

Dann können Sie ein kartesisches Produkt mit a erhalten CROSS JOIN(wobei die andere Beziehung natürlich eine reguläre Tabelle, eine Ansicht usw. sein kann). z.B:

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

was ergibt:

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

Oder JOINdie Werte mit einer anderen Beziehung (die wiederum eine reguläre Tabelle, Ansicht usw. sein kann), z.

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;

was ergibt:

key |val    |color |lookup_key |
----|-------|------|-----------|
1   |100    |Red   |1          |
0   |-99999 |White |0          |
1   |100    |Blue  |1          |
Isapir
quelle
OK, aber die Frage war "Wie erstelle ich eine temporäre Tabelle mit ...?"
ypercubeᵀᴹ 20.10.17
Ja, aber warum benötigen Sie eine temporäre Tabelle mit einigen festen Suchwerten, wenn Sie sie nicht in einer anderen Beziehung verbinden möchten? Diese Lösung löst das Problem selbst, unabhängig davon, wie die Frage formuliert ist.
Isapir
1
Vielleicht hat OP das Beispiel einfach auf etwas reduziert, das leicht als Frage zu veröffentlichen wäre, aber die realen Daten haben Tausende von Werten?
Stannius
Das OP hat ausdrücklich angegeben , Werte zu verwenden, daher gilt meine Antwort immer noch, da dies genau das ist, was es tut
isapir
2

Verwenden Sie zunächst immer die standardisierte CREATE TABLE AS, SELECT INTOwie in anderen Antworten bereits seit über einem Jahrzehnt veraltete Syntax. Sie könnenCREATE TABLE AS mit einem CTE verwenden

Während viele Antworten hier die Verwendung eines CTE vorschlagen, ist dies nicht vorzuziehen. In der Tat ist es wahrscheinlich etwas langsamer. Einfach als Tisch einpacken.

DROP TABLE IF EXISTS lookup;

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

Wenn Sie eine select-Anweisung schreiben müssen, können Sie dies auch tun (und Sie benötigen keinen 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);

Ein CTE in PostgreSQL erzwingt die Materialisierung. Es ist ein Optimierungszaun. Aus diesem Grund ist es im Allgemeinen keine gute Idee, sie überall zu verwenden, es sei denn, Sie kennen die Kosten und wissen, dass sie eine Leistungsverbesserung bewirken. Sie können die Verlangsamung hier sehen, zum Beispiel,

\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
quelle
Ich habe die Antwort aktualisiert, um den Standard widerzuspiegeln, und darauf hingewiesen, dass die akzeptierte Antwort nicht immer mit CREATE TABLE AS übereinstimmt, und einen Kommentar zum Optimierungsbereich hinzugefügt, der ein sehr guter Punkt ist. CTEs bringen so viele Vorteile, aber es ist wahr, wenn sie blind verwendet werden, können sie zu einer schrecklichen Leistung führen.
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
quelle