web-dev-qa-db-ja.com

PostgreSQLでVALUESを使用して一時テーブルを作成する方法

私は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での使用にどのように適応できますか?

前もって感謝します。

43
tinlyx

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で指摘されているように、 the docs で_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によるコメントから、およびOPの元の質問では、値リスト内の正しいデータ型へのキャストが含まれ、CTE(WITH)ステートメントが使用されています。

また、Evan Carrolの回答で指摘されているように、CTEクエリは最適化フェンスです。つまり、CTEは常に具体化されます。 CTEを使用することには多くの理由がありますが、慎重に使用しないと、パフォーマンスに大きな影響を与える可能性があります。ただし、最適化フェンスが実際にパフォーマンスを向上させることができる多くのインスタンスがあるため、これは盲目的に回避するのではなく、認識しておくべきことです。

53
John Powell

create table asには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);

CTEを使用するようにこれを書き換えることもできます。

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

問題はデータ型です。それらを削除すると、ステートメントは機能します。

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) ;
12
ypercubeᵀᴹ

クエリでいくつかの値を使用するだけの場合は、テーブルを作成したり、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          |
3
isapir

最初に常に標準化されたCREATE TABLE ASSELECT INTOを使用してください。他の回答で提案されているように、10年以上にわたって非推奨の構文でした。 CTEで CREATE TABLE ASを使用できます

ここでの多くの回答は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);

PostgreSQLのCTEは強制的に実体化します。それは最適化フェンスです。そのため、コストを理解し、パフォーマンスを向上させるためにそれを知っている場合を除いて、どこでもそれらを使用することは一般的にはお勧めできません。たとえば、ここでスローダウンを確認できます

\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
2
Evan Carroll