Як створити тимчасову таблицю за допомогою 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?

Заздалегідь спасибі.


Я спробував би відповісти на це запитання дещо сучаснішими порадами (оскільки обрана відповідь використовує устарений
Еван Керролл

Відповіді:


46

EDIT: Я залишаю оригінальну прийняту відповідь такою, якою вона є, але зауважте, що подана нижче редакція, запропонована 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;

EDIT: Як вказував a_horse_with_no_name, у документах він зазначає, що CREATE TABLE AS...він функціонально схожий SELECT INTO ..., але що перший є надмножиною останнього і 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 та в оригінальному питанні ОП це стосується переліку правильних типів даних усередині списку значень і використовує оператор CTE (WITH).

Крім того, як вказувалося у відповіді Евана Керрола, запит на CTE - це оптимізація , тобто CTE завжди матеріалізується. Існує багато вагомих причин для використання CTE, але це може бути досить вагомим результатом, якщо його не використовувати обережно. Однак є чимало випадків, коли огорожа для оптимізації може насправді підвищити продуктивність, тому це потрібно пам’ятати, а не сліпо уникати.


12
з Документів : " 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 вже не є sragable. Якщо створити зону інтересів у початковому CTE, ця проблема зникає. Це також дуже зручно, якщо ви хочете використовувати один і той же aoi в декількох подальших виразах в одному запиті, що не є рідкістю в контексті GIS.
Джон Пауелл

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 років.
Джон Пауелл

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ᵀᴹ

Так, але навіщо вам потрібна тимчасова таблиця з кількома фіксованими значеннями пошуку, якщо не для того, щоб приєднати її до іншого відносини? Це рішення вирішує саму проблему, незалежно від того, як формулюється питання.
ісапір

1
Можливо, ОП просто трапилось навести приклад на щось, що було б легко розмістити як питання, але реальні дані мають тисячі значень?
stannius

ОП спеціально вказано, використовуючи значення, тому моя відповідь все ще застосовується, оскільки саме це і робиться
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);

Якщо вам потрібно написати заяву про вибір, ви можете це зробити і (і вам не потрібен 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

Я оновив відповідь, щоб вона відображала стандарт, і зазначила, як прийнята відповідь не завжди еквівалентна СТВОРИТИ ТАБЛИЦЮ ТА додала коментар до огорожі оптимізації, що є дуже хорошим моментом. CTE приносять стільки переваг, але це правда, якщо використовувати їх наосліп, може призвести до жахливих показників.
Джон Пауелл

-2
WITH u AS (
    SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS account (id,name)
)
SELECT id, name, length(name) from u;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.