Jak utworzyć tabelę tymczasową za pomocą VALUES w PostgreSQL

38

Uczę się PostgreSQL i próbuję wymyślić, jak utworzyć tymczasową tabelę lub WITHdeklarację, której można użyć zamiast zwykłej tabeli do celów debugowania.

Przejrzałem dokumentację CREATE TABLE i mówi, że VALUESmożna ją wykorzystać jako zapytanie, ale nie podaje żadnego przykładu; dokumentacja VALUESklauzuli w niej zawartej również nie ma przykładu?

Napisałem więc prosty test w następujący sposób:

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

Ale PostgreSQL (9.3) narzeka

błąd składniowy przy „AS” lub w jego pobliżu

Moje pytania to:

  1. Jak mogę naprawić powyższe stwierdzenie?

  2. Jak mogę go dostosować do użycia w WITH block?

Z góry dziękuję.

tinlyx
źródło
Próbowałem odpowiedzieć na to pytanie bardziej nowoczesną radą (ponieważ wybrana odpowiedź używa przestarzałej, niestandardowej składni) dba.stackexchange.com/a/201575/2639
Evan Carroll

Odpowiedzi:

46

EDYCJA: Pozostawiam oryginalną zaakceptowaną odpowiedź taką, jaka jest, ale pamiętaj, że poniższa edycja, sugerowana przez a_horse_w_no_name, jest preferowaną metodą tworzenia tabeli tymczasowej przy użyciu VALUES.

Jeśli chcesz tylko wybrać jedną z wartości, zamiast tworzyć tabelę i wstawiać do niej, możesz zrobić coś takiego:

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

Aby faktycznie utworzyć tymczasowy stół w podobny sposób, użyj:

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

EDYCJA: Jak wskazano przez a_horse_with_no_name, w dokumentach stwierdza, że CREATE TABLE AS...jest funkcjonalnie podobny SELECT INTO ..., ale że ten pierwszy jest nadzbiorem drugiego i który SELECT INTOjest używany w plpgslq do przypisywania wartości do zmiennej tymczasowej - więc nie powiodłaby się ta walizka. Dlatego, chociaż powyższe przykłady dotyczą zwykłego SQL, CREATE TABLEforma powinna być preferowana.

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

Uwaga: również z komentarzy autorstwa_nazwa_nazwy_nazwa oraz w pierwotnym pytaniu PO, obejmuje to rzutowanie na prawidłowe typy danych na liście wartości i użycie instrukcji CTE (Z).

Ponadto, jak wskazano w odpowiedzi Evana Carrola, zapytanie CTE jest ogrodzeniem optymalizacyjnym , tj. CTE jest zawsze zmaterializowane. Istnieje wiele dobrych powodów, aby używać CTE, ale może być dość znaczący spadek wydajności, jeśli nie zostanie zastosowany ostrożnie. Istnieje jednak wiele przypadków, w których płot optymalizacyjny może rzeczywiście poprawić wydajność, więc należy o tym pamiętać, a nie ślepo unikać.

John Powell
źródło
12
z dokumentów : „ UTWÓRZ TABELĘ AS jest funkcjonalnie podobny do WYBIERZ W. UTWÓRZ TABELĘ AS to zalecana składnia
a_konia_na_nazwa
Ogrodzenie optymalizacyjne niekoniecznie jest złe. Widziałem wiele stwierdzeń, które z tego powodu mogłem dostroić, aby biegać znacznie szybciej.
a_horse_w_no_name
Jasne, ja też to wyjaśniłem. Cały czas używam CTE w kontekście przestrzennym. Jeśli masz klauzulę where z czymś podobnym WHERE ST_Intersects(geom, (SELECT geom FROM sometable)lub WHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)często planista zapytań nie używa indeksu przestrzennego, ponieważ kolumna geom nie jest już możliwa do sargowania. Jeśli utworzysz obszar zainteresowania w początkowej CTE, ten problem zniknie. Jest to również bardzo wygodne, jeśli chcesz używać tego samego aoi w wielu innych wyrażeniach w tym samym zapytaniu, co nie jest rzadkością w kontekście GIS.
John Powell,
25

create table as potrzebuje instrukcji 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);

Możesz także ponownie napisać to, aby użyć CTE:

create temp table lookup 
as 
with t (key, value) as (
  values 
    (0::int,-99999::numeric), 
    (1::int,100::numeric)
)
select * from t;
koń bez imienia
źródło
1
Dziękuję za komentarz. Twoje podejście jest oczywiście lepsze z powodów podanych w dokumentach. Zredagowałem swoją odpowiedź, choć prawie 5 lat później.
John Powell,
11

Problemem są typy danych. Jeśli je usuniesz, instrukcja będzie działać:

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

Możesz zdefiniować typy, rzutując wartości z pierwszego wiersza:

CREATE TEMP TABLE lookup 
  (key, val) AS
VALUES 
  (0::bigint, -99999::int), 
  (1, 100) ;
ypercubeᵀᴹ
źródło
3

Naprawdę nie musisz tworzyć tabeli ani używać CTE, jeśli wszystko, czego potrzebujesz, to użyć kilku wartości w zapytaniach. Możesz je wstawić:

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

Następnie możesz uzyskać produkt kartezjański z CROSS JOIN(gdzie drugą relacją może być oczywiście zwykły stół, widok itp.). na przykład:

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

co daje:

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

Lub JOINwartości z inną relacją (która może być zwykłą tabelą, widokiem itp.), Np .:

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;

co daje:

key |val    |color |lookup_key |
----|-------|------|-----------|
1   |100    |Red   |1          |
0   |-99999 |White |0          |
1   |100    |Blue  |1          |
isapir
źródło
OK, ale pytanie brzmiało „jak utworzyć tymczasowy stół z ...?”
ypercubeᵀᴹ
Tak, ale dlaczego potrzebowałbyś tabeli tymczasowej z kilkoma stałymi wartościami wyszukiwania, gdyby nie dołączenie jej do innej relacji? To rozwiązanie rozwiązuje sam problem, niezależnie od tego, jak sformułowane jest pytanie.
isapir
1
Może OP po prostu sprowadza przykład do czegoś, co łatwo byłoby zadać jako pytanie, ale prawdziwe dane mają tysiące wartości?
stannius
OP konkretnie podał wartości, więc moja odpowiedź wciąż obowiązuje, ponieważ właśnie to robi
isapir,
2

Najpierw zawsze używaj standardowego CREATE TABLE AS, SELECT INTOjak sugerowano w innych odpowiedziach, jest to przestarzała składnia od ponad dekady. Możesz używaćCREATE TABLE AS z CTE

Chociaż wiele odpowiedzi tutaj sugeruje użycie CTE, nie jest to preferowane. W rzeczywistości jest to nieco wolniejsze. Po prostu zawiń w stół.

DROP TABLE IF EXISTS lookup;

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

Jeśli musisz napisać instrukcję select, możesz to zrobić (i nie potrzebujesz 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);

CTE w PostgreSQL wymusza materializację. To płot optymalizacyjny. Z tego powodu generalnie nie jest dobrym pomysłem stosowanie ich w dowolnym miejscu, z wyjątkiem sytuacji, gdy rozumiesz koszty i wiesz, że zapewnia to poprawę wydajności. Możesz zobaczyć tutaj spowolnienie, na przykład

\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
źródło
Zaktualizowałem odpowiedź, aby odzwierciedlić standard, i wskazałem, że zaakceptowana odpowiedź nie zawsze jest równoważna z CREATE TABLE AS, i dodałem komentarz dotyczący ogrodzenia optymalizacyjnego, co jest bardzo dobrym punktem do poruszenia. CTE przynoszą tak wiele korzyści, ale prawdą jest, że jeśli stosowane na ślepo, mogą prowadzić do okropnej wydajności.
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;
kauczuk
źródło