Cara membuat tabel sementara menggunakan VALUES di PostgreSQL

38

Saya belajar PostgreSQL dan mencoba mencari cara untuk membuat tabel sementara atau WITHdeklarasi yang dapat digunakan sebagai pengganti tabel biasa, untuk keperluan debugging.

Saya melihat dokumentasi untuk CREATE TABLE dan katanyaVALUES bisa digunakan sebagai query tetapi tidak memberikan contoh; dokumentasi untuk VALUESklausa yang ditautkan di dalamnya juga tidak memiliki contoh?

Jadi, saya menulis tes sederhana sebagai berikut:

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

Tapi PostgreSQL (9.3) mengeluhkan

kesalahan sintaks di atau dekat "AS"

Pertanyaan saya adalah:

  1. Bagaimana saya bisa memperbaiki pernyataan di atas?

  2. Bagaimana saya bisa menyesuaikannya untuk digunakan dalam WITH block?

Terima kasih sebelumnya.

tinlyx
sumber
Saya telah mencoba menjawab pertanyaan ini dengan beberapa saran yang lebih modern (karena jawaban yang dipilih menggunakan sintaksis non-standar yang sudah usang) dba.stackexchange.com/a/201575/2639
Evan Carroll

Jawaban:

46

EDIT: Saya meninggalkan jawaban asli yang diterima apa adanya, tetapi harap perhatikan bahwa edit di bawah ini, seperti yang disarankan oleh a_horse_with_no_name, adalah metode yang disukai untuk membuat tabel sementara menggunakan VALUES.

Jika Anda hanya ingin memilih dari beberapa nilai, bukan hanya membuat tabel dan memasukkannya, Anda dapat melakukan sesuatu seperti:

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

Untuk benar-benar membuat tabel sementara dengan cara yang serupa, gunakan:

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

EDIT: Seperti yang ditunjukkan oleh a_horse_with_no_name, dalam dokumen itu menyatakan bahwa CREATE TABLE AS...secara fungsional mirip dengan SELECT INTO ..., tetapi yang pertama adalah superset dari yang terakhir dan yang SELECT INTOdigunakan dalam plpgslq untuk menetapkan nilai ke variabel sementara - sehingga akan gagal dalam kasus itu. Oleh karena itu, sementara contoh di atas berlaku untuk SQL biasa, CREATE TABLEformulir harus lebih disukai.

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

Catatan, juga dari komentar oleh a_horse_with_no_name, dan dalam pertanyaan awal OP, ini termasuk pemeran untuk tipe data yang benar di dalam daftar nilai dan menggunakan pernyataan CTE (WITH).

Juga, sebagaimana ditunjukkan dalam jawaban Evan Carrol, permintaan CTE adalah pagar pengoptimalan , yaitu, CTE selalu terwujud. Ada banyak alasan bagus untuk menggunakan CTE, tetapi bisa ada pencapaian kinerja yang cukup signifikan, jika tidak digunakan dengan hati-hati. Namun, ada banyak contoh di mana pagar pengoptimalan sebenarnya dapat meningkatkan kinerja, jadi ini adalah sesuatu yang harus diperhatikan, bukan untuk dihindari secara membabi buta.

John Powell
sumber
12
dari docs : " CREATE TABLE AS secara fungsional mirip dengan SELECT INTO. CREATE TABLE AS adalah sintaks yang direkomendasikan "
a_horse_with_no_name
Pagar optimasi belum tentu hal yang buruk. Saya telah melihat banyak pernyataan bahwa saya bisa menyetel untuk berlari lebih cepat karena itu.
a_horse_with_no_name
Tentu, saya sudah mengklarifikasi itu juga. Saya menggunakan CTE sepanjang waktu dalam konteks spasial. Jika Anda memiliki klausa tempat dengan sesuatu seperti WHERE ST_Intersects(geom, (SELECT geom FROM sometable)atau WHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)sering perencana kueri tidak menggunakan indeks spasial karena kolom geom tidak lagi dapat ditagih. Jika Anda membuat bidang yang Anda minati dalam CTE awal, masalah ini akan hilang. Ini juga sangat nyaman, jika Anda ingin menggunakan aoi yang sama dalam beberapa ekspresi lebih lanjut dalam kueri yang sama, yang tidak biasa dalam konteks GIS.
John Powell
25

create table as membutuhkan pernyataan pilih:

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

Anda juga dapat menulis ulang ini untuk menggunakan CTE:

create temp table lookup 
as 
with t (key, value) as (
  values 
    (0::int,-99999::numeric), 
    (1::int,100::numeric)
)
select * from t;
seekor kuda tanpa nama
sumber
1
Terima kasih atas komentar Anda. Pendekatan Anda jelas lebih baik karena alasan yang dinyatakan dalam dokumen. Saya telah mengedit jawaban saya, meskipun terlambat hampir 5 tahun.
John Powell
11

Masalahnya adalah tipe data. Jika Anda menghapusnya, pernyataan itu akan berfungsi:

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

Anda dapat menentukan tipe dengan melemparkan nilai-nilai dari baris pertama:

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

Anda benar-benar tidak perlu membuat tabel atau menggunakan CTE, jika yang Anda butuhkan hanyalah menggunakan beberapa nilai dalam kueri Anda. Anda dapat mengikutinya:

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

Kemudian Anda bisa mendapatkan produk Cartesian dengan CROSS JOIN(di mana hubungan lainnya bisa, tentu saja, tabel biasa, tampilan, dll.). misalnya:

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

yang menghasilkan:

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

Atau JOINnilai-nilai dengan hubungan lain (yang lagi-lagi bisa berupa tabel, tampilan, dll.), Misalnya:

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;

yang menghasilkan:

key |val    |color |lookup_key |
----|-------|------|-----------|
1   |100    |Red   |1          |
0   |-99999 |White |0          |
1   |100    |Blue  |1          |
isapir
sumber
OK tapi pertanyaannya adalah "bagaimana cara membuat tabel sementara dengan ...?"
ypercubeᵀᴹ
Ya, tetapi mengapa Anda membutuhkan tabel sementara dengan beberapa nilai pencarian tetap jika tidak bergabung dengan itu pada hubungan lain? Solusi ini memecahkan masalah itu sendiri, terlepas dari bagaimana pertanyaan itu diucapkan.
isapir
1
Mungkin OP kebetulan meringkas contoh untuk sesuatu yang mudah diposkan sebagai pertanyaan, tetapi data sebenarnya memiliki ribuan nilai?
stannius
OP secara khusus menyatakan menggunakan nilai-nilai sehingga jawaban saya masih berlaku karena itulah yang dilakukannya
isapir
2

Pertama selalu menggunakan standar CREATE TABLE AS, SELECT INTOseperti yang disarankan dalam jawaban lain telah menjadi sintaks yang sudah usang selama lebih dari satu dekade. Anda dapat menggunakannyaCREATE TABLE AS dengan CTE

Sementara banyak jawaban di sini menyarankan menggunakan CTE, itu tidak disukai. Bahkan, sepertinya agak lambat. Hanya membungkusnya sebagai meja.

DROP TABLE IF EXISTS lookup;

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

Jika Anda harus menulis pernyataan pilih, Anda juga dapat melakukannya (dan Anda tidak memerlukan 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 di PostgreSQL memaksa materialisasi. Ini adalah pagar optimasi. Untuk alasan itu, umumnya bukan ide yang baik untuk menggunakannya di mana saja kecuali ketika Anda memahami biayanya dan Anda tahu itu untuk memberikan peningkatan kinerja. Anda dapat melihat lambatnya di sini, misalnya,

\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
sumber
Saya telah memperbarui jawaban untuk mencerminkan standar, dan menunjukkan bagaimana jawaban yang diterima tidak selalu setara dengan CREATE TABLE AS dan menambahkan komentar pada pagar pengoptimalan, yang merupakan poin yang sangat baik untuk diangkat. CTE membawa banyak keuntungan, tetapi memang benar, jika digunakan secara membabi buta, dapat menyebabkan kinerja yang mengerikan.
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
sumber