64

一連のネストされたサブクエリを介して、4 つのテーブルにまたがる PostgreSQL データベースにかなり複雑なクエリを実行しています。ただし、外観とセットアップが少しトリッキーに見えますが、最終的には、2 つの外部パラメーター (2 つの文字列が異なるテーブルのフィールドと一致する必要があります) の一致に基づいて (状況に役立つ場合は、同じテーブルから) 2 つの列が返されます。私は PostgreSQL でのデータベース設計にかなり慣れていないので、ビューと呼ばれるこの一見魔法のようなものが存在することを知っています。

複雑なクエリをビュー内に移動し、一致させる必要がある 2 つの値を渡す方法はありますか? これにより、フロントエンドのコードが大幅に簡素化されます (複雑さをデータベース構造にシフトすることにより)。静的サンプル クエリをラップするビューを作成できます。これは問題なく機能しますが、文字列値の 1 つのペアに対してのみ機能します。さまざまな異なる値で使用できるようにする必要があります。

したがって、私の質問は次のとおりです。それ以外の場合は静的なビューにパラメーターを渡して、それを「動的」にすることは可能ですか? または、ビューはそれにアプローチする正しい方法ではないかもしれません。他にもっとうまくいくものがあれば、私はすべて耳にします!

*編集:*コメントで要求されたように、現在の私のクエリは次のとおりです。

SELECT   param_label, param_graphics_label
  FROM   parameters
 WHERE   param_id IN 
         (SELECT param_id 
            FROM parameter_links
           WHERE region_id = 
                 (SELECT region_id
                    FROM regions
                   WHERE region_label = '%PARAMETER 1%' AND model_id =
                         (SELECT model_id FROM models WHERE model_label = '%PARAMETER 2%')
                 )
         ) AND active = 'TRUE'
ORDER BY param_graphics_label;

パラメータは、上記のパーセント記号によって設定されます。

4

5 に答える 5

80

セットを返す関数を使用できます。

create or replace function label_params(parm1 text, parm2 text)
  returns table (param_label text, param_graphics_label text)
as
$body$
  select ...
  WHERE region_label = $1 
     AND model_id = (SELECT model_id FROM models WHERE model_label = $2)
  ....
$body$
language sql;

次に、次のことができます。

select *
from label_params('foo', 'bar')

ところで:あなたは本当にしたいですか:

AND model_id = (SELECT model_id FROM models WHERE model_label = $2)

model_labelが一意 (または主キー) でない場合、最終的にエラーがスローされます。あなたはおそらく欲しい:

AND model_id IN (SELECT model_id FROM models WHERE model_label = $2)
于 2012-07-09T19:43:35.123 に答える
31

@a_horseがすでにクリアしたものに加えて、ネストされたサブクエリの代わりにJOIN構文を使用することで、SQLを単純化できます。パフォーマンスは似ていますが、構文ははるかに短く、管理が簡単です。

CREATE OR REPLACE FUNCTION param_labels(_region_label text, _model_label text)
  RETURNS TABLE (param_label text, param_graphics_label text) AS
$func$
    SELECT p.param_label, p.param_graphics_label
    FROM   parameters      p 
    JOIN   parameter_links l USING (param_id)
    JOIN   regions         r USING (region_id)
    JOIN   models          m USING (model_id)
    WHERE  p.active
    AND    r.region_label = $1 
    AND    m.model_label = $2
    ORDER  BY p.param_graphics_label;
$func$ LANGUAGE sql;
  • 一意でない場合model_label、またはクエリ内の他の何かが重複する行を生成する場合は、最高のパフォーマンスを得るためSELECT DISTINCT p.param_graphics_label, p.param_labelに、一致するORDER BY句を使用してそれを作成することをお勧めします。または、GROUP BY句を使用します。

  • Postgres 9.2以降では、SQL関数の代わりに、$1および$2SQL関数で宣言されたパラメーター名を使用できます。(PL / pgSQL関数では長い間可能でした)。

  • 名前の競合を避けるように注意する必要があります。そのため、宣言でパラメーター名のプレフィックスを付け(関数内のほとんどの場所で表示されます)、本文で列名をテーブル修飾することを習慣にしています。

  • 列 はおそらくタイプではなくタイプである必要があるため、に簡略化WHERE p.active = 'TRUE'しました。WHERE p.activeactivebooleantext

  • USING列名がJOINの左側にあるすべてのテーブルで明確である場合にのみ機能します。それ以外の場合は、より明示的な構文を使用する必要があります。
    ON l.param_id = p.param_id

于 2012-07-09T20:15:38.950 に答える
26

ほとんどの場合、セットを返す関数が適していますが、セットの読み取りと書き込みの両方が必要な場合は、ビューの方が適している場合があります。また、ビューがセッション パラメータを読み取ることも可能です。

CREATE VIEW widget_sb AS
  SELECT * FROM widget
  WHERE column = cast(current_setting('mydomain.myparam') as int)

SET mydomain.myparam = 0
select * from widget_sb
[results]

SET mydomain.myparam = 1
select * from widget_sb
[distinct results]
于 2014-08-12T04:32:42.037 に答える
3

あなたが述べたような「動的な」ビューは可能だとは思いません。

代わりに 2 つの引数を取るストアド プロシージャを作成してみませんか?

于 2012-07-09T19:40:55.880 に答える