วิธีสร้างตารางชั่วคราวโดยใช้ VALUES ใน PostgreSQL


38

ฉันกำลังเรียนรู้ PostgreSQL และพยายามหาวิธีสร้างตารางชั่วคราวหรือWITHประกาศที่สามารถใช้แทนตารางปกติเพื่อจุดประสงค์ในการดีบั๊ก

ฉันดูเอกสารสำหรับCREATE TABLEและมันบอกว่าVALUESสามารถใช้เป็นแบบสอบถาม แต่ไม่ได้ยกตัวอย่าง เอกสารสำหรับVALUESส่วนเชื่อมโยงนั้นไม่มีตัวอย่างหรือไม่?

ดังนั้นฉันจึงเขียนแบบทดสอบง่ายๆดังนี้:

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

แต่ PostgreSQL (9.3) กำลังบ่นเกี่ยวกับ

ข้อผิดพลาดทางไวยากรณ์ที่หรือใกล้ "AS"

คำถามของฉันคือ:

  1. ฉันจะแก้ไขข้อความข้างต้นได้อย่างไร

  2. ฉันจะสามารถปรับตัวเข้ากับมันเพื่อนำมาใช้ในWITH block?

ขอบคุณล่วงหน้า.


ฉันพยายามตอบคำถามนี้พร้อมคำแนะนำที่ทันสมัยกว่าเดิม (เนื่องจากคำตอบที่เลือกใช้รูปแบบที่ไม่ได้มาตรฐาน) dba.stackexchange.com/a/201575/2639
Evan Carroll

คำตอบ:


46

แก้ไข:ฉันออกจากคำตอบที่ยอมรับเดิมตามที่เป็นอยู่ แต่โปรดทราบว่าการแก้ไขด้านล่างตามที่แนะนำโดย a_horse_with_no_name เป็นวิธีที่ต้องการสำหรับการสร้างตารางชั่วคราวโดยใช้ VALUES

หากคุณต้องการเลือกจากค่าบางอย่างแทนที่จะสร้างเพียงตารางและแทรกเข้าไปคุณสามารถทำสิ่งต่อไปนี้

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

หากต้องการสร้างตารางชั่วคราวในแบบเดียวกันให้ใช้:

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

แก้ไข:ตามที่ระบุโดย a_horse_with_no_name ในเอกสารนั้นระบุว่าCREATE TABLE AS...คล้ายกับการใช้SELECT INTO ...งาน แต่ในอดีตนั้นเป็น superset ของหลังและSELECT INTOใช้ใน plpgslq เพื่อกำหนดค่าให้กับตัวแปรชั่วคราว - ดังนั้นมันจะล้มเหลวใน กรณีนั้น ดังนั้นในขณะที่ตัวอย่างข้างต้นใช้งานได้กับ SQL ธรรมดาCREATE TABLEรูปแบบที่ต้องการ

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

หมายเหตุจากความคิดเห็นโดย a_horse_with_no_name และในคำถามดั้งเดิมของ OP สิ่งนี้รวมถึงการส่งไปยังประเภทข้อมูลที่ถูกต้องภายในรายการค่าและใช้คำสั่ง CTE (WITH)

นอกจากนี้ตามที่ระบุไว้ในคำตอบของ Evan Carrol แบบสอบถาม CTE เป็นกรอบการปรับให้เหมาะสมเช่น CTE จะปรากฏขึ้นเสมอ มีเหตุผลที่ดีหลายประการสำหรับการใช้ CTEs แต่อาจมีผลกระทบต่อประสิทธิภาพอย่างมากหากไม่ได้ใช้อย่างระมัดระวัง อย่างไรก็ตามมีหลายกรณีที่รั้วการปรับให้เหมาะสมสามารถเพิ่มประสิทธิภาพได้จริงดังนั้นนี่เป็นสิ่งที่ควรระวังอย่าหลีกเลี่ยงการสุ่มสี่สุ่มห้า


12
จาก docs : " CREATE TABLE AS คล้ายกับ SELECT INTO ซึ่ง CREATE TABLE AS เป็นไวยากรณ์ที่แนะนำ "
a_horse_with_no_name

รั้วการปรับให้เหมาะสมไม่จำเป็นต้องเป็นสิ่งที่ไม่ดี ฉันได้เห็นข้อความมากมายที่ฉันสามารถปรับให้ทำงานเร็วขึ้นอย่างมากได้
a_horse_with_no_name

แน่นอนว่าฉันได้ชี้แจงด้วยเช่นกัน ฉันใช้ CTE ตลอดเวลาในบริบทเชิงพื้นที่ หากคุณมีส่วนคำสั่งที่มีบางสิ่งที่ชอบWHERE ST_Intersects(geom, (SELECT geom FROM sometable)หรือWHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)บ่อยครั้งที่ตัววางแผนคิวรีไม่ได้ใช้ดัชนีปริภูมิเนื่องจากคอลัมน์ geom ไม่สามารถส่งต่อได้อีกต่อไป หากคุณสร้างพื้นที่ที่คุณสนใจใน CTE เริ่มต้นปัญหานี้จะหายไป นอกจากนี้ยังสะดวกสบายมากหากคุณต้องการใช้ aoi เดียวกันในหลาย ๆ นิพจน์ในข้อความค้นหาเดียวกันซึ่งไม่ใช่เรื่องแปลกในบริบท GIS
John Powell

25

create table as ต้องการคำสั่งที่เลือก:

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

คุณสามารถเขียนสิ่งนี้ใหม่เพื่อใช้ CTE:

create temp table lookup 
as 
with t (key, value) as (
  values 
    (0::int,-99999::numeric), 
    (1::int,100::numeric)
)
select * from t;

1
ขอบคุณสำหรับความคิดเห็นของคุณ คุณเข้าใกล้จะดีกว่าด้วยเหตุผลที่ระบุไว้ในเอกสาร ฉันได้แก้ไขคำตอบของฉันแม้ว่าจะสายเกือบ 5 ปี
John Powell

11

ปัญหาคือประเภทข้อมูล หากคุณลบพวกเขาคำสั่งจะทำงาน:

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

คุณสามารถกำหนดประเภทโดยการหล่อค่าของแถวแรก:

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

3

คุณไม่จำเป็นต้องสร้างตารางหรือใช้ CTE หากคุณจำเป็นต้องใช้ค่าบางอย่างในแบบสอบถามของคุณ คุณสามารถอินไลน์ได้:

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

จากนั้นคุณจะได้รับผลิตภัณฑ์คาร์ทีเซียนด้วยCROSS JOIN(ซึ่งเป็นความสัมพันธ์อื่น ๆ แน่นอนตารางปกติมุมมอง ฯลฯ ) เช่น:

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

ซึ่งให้:

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

หรือJOINค่าที่มีความสัมพันธ์อื่น (ซึ่งอาจเป็นตารางมุมมอง ฯลฯ ) อีกครั้งเช่น:

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;

ซึ่งให้:

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

ตกลง แต่คำถามคือ"วิธีสร้างตารางชั่วคราวด้วย ... ?"
ypercubeᵀᴹ

ใช่ แต่ทำไมคุณต้องมีตารางชั่วคราวที่มีค่าการค้นหาคงที่สองสามค่าหากไม่ใช่เพื่อเข้าร่วมในความสัมพันธ์อื่น วิธีการแก้ปัญหานี้แก้ปัญหาเองโดยไม่คำนึงถึงวิธีการตั้งคำถาม
isapir

1
บางที OP เพิ่งเกิดการต้มตัวอย่างกับสิ่งที่ง่ายต่อการโพสต์เป็นคำถาม แต่ข้อมูลจริงมีค่านับพันหรือไม่
stannius

OP ระบุไว้โดยเฉพาะโดยใช้ค่าดังนั้นคำตอบของฉันยังคงใช้ตามที่เป็นจริง
isapir

2

ครั้งแรกมักจะใช้มาตรฐานCREATE TABLE AS, SELECT INTOตามที่แนะนำในคำตอบอื่น ๆ ได้รับการไวยากรณ์ที่เลิกใช้มานานกว่าทศวรรษ คุณสามารถใช้CREATE TABLE ASกับ CTE

ในขณะที่คำตอบมากมายที่นี่แนะนำให้ใช้ CTE แต่ก็ไม่เหมาะ ที่จริงแล้วมันค่อนข้างช้ากว่า เพียงห่อมันเป็นตาราง

DROP TABLE IF EXISTS lookup;

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

หากคุณต้องเขียนคำสั่ง select คุณสามารถทำได้เช่นกัน (และคุณไม่จำเป็นต้องมี 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 ใน PostgreSQL บังคับให้เป็นรูปเป็นร่าง มันเป็นรั้วการเพิ่มประสิทธิภาพ ด้วยเหตุผลดังกล่าวจึงไม่ควรใช้ที่ใดก็ได้ยกเว้นเมื่อคุณเข้าใจค่าใช้จ่ายและคุณรู้เพื่อให้มีการปรับปรุงประสิทธิภาพ คุณสามารถดูการชะลอตัวที่นี่เช่น

\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

ฉันได้อัปเดตคำตอบเพื่อให้สอดคล้องกับมาตรฐานและชี้ให้เห็นว่าคำตอบที่ยอมรับนั้นไม่ได้เสมอกับ CREATE TABLE AS และเพิ่มความคิดเห็นเกี่ยวกับรั้วการปรับให้เหมาะสมซึ่งเป็นจุดที่ดีมากที่จะนำมาใช้ CTE นำข้อดีมาให้มากมาย แต่จริง ๆ แล้วถ้าใช้แบบสุ่มสี่สุ่มห้าสามารถนำไปสู่ประสิทธิภาพที่น่ากลัว
John Powell

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.