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


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

編集:私は元の受け入れられた答えをそのまま残していますが、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 ...が、前者は後者のスーパーセットであり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を使用するのには多くの理由がありますが、慎重に使用しないと、かなりのパフォーマンスヒットが発生する可能性があります。ただし、最適化フェンスが実際にパフォーマンスを向上させることができる場合が多いため、盲目的に回避するのではなく、注意する必要があります。


12
ドキュメントから:「CREATE TABLE ASは機能的にSELECT INTOに似ています
。CREATETABLE

最適化フェンスは必ずしも悪いことではありません。そのため、大幅に高速に実行するように調整できる多くのステートメントを見てきました。
a_horse_with_no_name

確かに、私もそれを明らかにしました。私は常に空間コンテキストでCTEを使用しています。WHERE ST_Intersects(geom, (SELECT geom FROM sometable)またはなどのようなwhere句がある場合WHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)、geom列はsargableではないため、クエリプランナーは空間インデックスを使用しません。最初のCTEで関心領域を作成すると、この問題はなくなります。同じクエリ内の複数の式で同じaoiを使用したい場合にも便利です。これは、GISコンテキストでは珍しいことではありません。
ジョンパウエル

25

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;

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

はい、ただし、別のリレーションシップで結合しない場合に、いくつかの固定ルックアップ値を持つ一時テーブルが必要なのはなぜですか?このソリューションは、質問の表現方法に関係なく、問題自体を解決します。
isapir

1
たぶん、OPがたまたま質問を投稿しやすいものに例を絞り込んでいるのかもしれませんが、実際のデータには何千もの値がありますか?
スタニウス

具体的に述べOP 値を使用して、私の答えはまだそのよう適用されるので、それが何をするかを正確にある
isapir

2

他の回答で示唆されているようにCREATE TABLE AS、最初は常に標準化SELECT INTOされたを使用します。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

標準を反映するように回答を更新し、受け入れられた回答が常にCREATE TABLE ASと同等ではないことを指摘し、最適化フェンスにコメントを追加しました。これは非常に良いポイントです。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;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.