web-dev-qa-db-ja.com

一般的なクエリを列として保存しますか?

PostgreSQLを使用して、次のようなクエリがいくつかあります。

SELECT <col 1>, <col 2>
     , (SELECT sum(<col x>)
        FROM   <otherTable> 
        WHERE  <other table foreignkeyCol>=<this table keycol>) AS <col 3>
FROM   <tbl>

副選択がすべての場合で同一であることを考えると、その副選択をテーブルの疑似列として格納する方法はありますか?基本的に、レコードが関連付けられているテーブルBの特定の列の合計であるテーブルAの列を選択できるようにしたいのですが。これは可能ですか?

37
ibrewster

その副選択をテーブルの疑似列として保存する方法はありますか?

VIEW 推奨されているような方法は、完全に有効なソリューションです。しかし、あなたの質問によりぴったり合う別の方法があります。テーブルタイプをパラメーターとして取る関数をemulate a "computed field"またはに書き込むことができます。 「生成された列」

説明から導き出されたこのテストケースを考えてみます。

CREATE TABLE tbl_a (a_id int, col1 int, col2 int);
INSERT INTO tbl_a VALUES (1,1,1), (2,2,2), (3,3,3), (4,4,4);

CREATE TABLE tbl_b (b_id int, a_id int, colx int);
INSERT INTO tbl_b VALUES
 (1,1,5),  (2,1,5),  (3,1,1)
,(4,2,8),  (5,2,8),  (6,2,6)
,(7,3,11), (8,3,11), (9,3,11);

col3をエミュレートする関数を作成します。

CREATE FUNCTION col3(tbl_a)
  RETURNS int8 AS
$func$
    SELECT sum(colx)
    FROM   tbl_b b
    WHERE  b.a_id = $1.a_id
$func$ LANGUAGE SQL STABLE;

これでクエリできます:

SELECT a_id, col1, col2, tbl_a.col3
FROM   tbl_a;

あるいは:

SELECT *, a.col3 FROM tbl_a a;

tbl_a.col3だけでなく、a.col3/col3の記述方法に注意してください。これはessentialです。

"Oracleの仮想列" とは異なり、それはnotSELECT * FROM tbl_aに自動的に含まれます。そのためにVIEWを使用できます。

なぜこれが機能するのですか?

テーブルカラムを参照する一般的な方法は、属性表記を使用することです。

選択する tbl_a.col1 FROM tbl_a;

関数を呼び出す一般的な方法はfunction notationです。

選択する col3(tbl_a);

一般に、SQL標準に同意するこれらの標準的な方法に固執するが最適です。

ただし、PostgreSQLでは、関数表記と属性表記は同等です。したがって、これらも機能します:

選択する col1(tbl_a) FROM tbl_a; 
 SELECT tbl_a.col3;

詳細についてはマニュアルをご覧ください。
おそらく、これがどこに向かっているのか、今ご存じでしょう。これは、テーブルtbl_aに列を追加するようなのように見えます一方で、col3()は、実際にはtbl_a(またはそのエイリアス)の現在の行を行として受け取る関数です引数を入力して値を計算します。

SELECT *, a.col3
FROM   tbl_a AS a;

実際の列col3がある場合、それが優先され、システムは、行tbl_aをパラメーターとして使用して、その名前の関数を検索しません。

その美しさ:tbl_aから列を追加または削除でき、最後のクエリは現在のすべての列を動的に返します。ビューは、作成時に存在した列のみを返します(アーリーバインディングと*のレイトバインディング)。
もちろん、ここでテーブルを削除する前に、依存する関数を削除する必要があります。また、テーブルを変更するときは、関数を無効にしないように注意する必要があります。

73

これまでに3つの答えがありますが、すべてうまくいきます。状況に応じて、それらのいずれかが「最良の解決策」になる可能性があります。小さなテーブルの場合、パフォーマンスはかなり近いはずですが、数百万行のテーブルにうまく対応できるものはありません。大きなデータセットで望ましい結果を得る最も速い方法は、おそらく(Erwinのセットアップを使用)です。

SELECT a_id, col1, col2, sum(colx)
FROM tbl_a LEFT JOIN tbl_b b using(a_id)
GROUP BY a_id, col1, col2;

a_idは主キーとして宣言されており、これは9.1以降で実行され、GROUP BY句は、col1およびcol2 are 機能的に依存 on a_id

SELECT a_id, col1, col2, sum(colx)
FROM tbl_a LEFT JOIN tbl_b b using(a_id)
GROUP BY a_id;

ビューはこのように定義でき、拡大縮小されますが、私はthinkを使用しません。関数を使用するアプローチではすべて同じ実行パスが考慮されるため、最速の実行パスは使用されない可能性があります。

2
kgrittn

どうやらこれは、ライオンのコメントに従って、ビューで処理されます。したがって、私の場合、次のコマンドを使用しました。

CREATE VIEW <viewname> AS
SELECT *, (SELECT sum(<col x>)
FROM   <otherTable
WHERE  <otherTable foreignkeyCol>=<thisTable keycol>) AS <col 3>
FROM   <tablename>

これにより、目的の列を含む別のテーブルが本質的に得られます。

2
ibrewster

ビューとは別に、合計の関数を作成できます。

CREATE FUNCTION sum_other_table( key type_of_key ) RETURNS bigint
AS $$ SELECT sum( col_x ) FROM table_1 where table_1.key = key $$ LANGUAGE SQL;

次に、それをアグリゲーターとして使用します。

SELECT col_1, col_2, sum_other_table( key ) AS col_3
FROM table_2 WHERE table_2.key = key;

Sum_other_table()の戻り値の型は、合計する列の型に依存することに注意してください。

2