web-dev-qa-db-ja.com

「WHERE」パラメーターをPostgreSQLビューに渡しますか?

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

ビュー内で複雑なクエリを移動し、何らかの方法で一致する必要がある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;

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

39
Devin

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

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)

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

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

@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および$2の代わりに宣言されたパラメーター名を使用できます。 (PL/pgSQL関数では長い間可能です)。

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

  • activeはおそらくbooleanではなくtext型でなければならないため、WHERE p.active = 'TRUE'WHERE p.activeに簡略化しました。

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

26

ほとんどの場合、セットを返す関数を使用する方法がありますが、セットの読み取りと書き込みの両方を行う場合は、ビューの方が適切な場合があります。そして、それはisビューがセッションパラメータを読み取ることができる:

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]
22
Drew

あなたが述べたような「動的な」見解は可能ではないと思います。

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

2
Jin Kim

クエリを次のように言い換えます。

SELECT   p.param_label, p.param_graphics_label
  FROM   parameters p
where exists (
    select 1
    from parameter_links pl
    where pl.parameter_id = p.id
    and exists (select 1 from regions r where r.region_id = pl.region_id
) and p.active = 'TRUE'
order by p.param_graphics_label;

さまざまなid列にインデックスがあると仮定すると、このクエリはIN演算子を使用するよりも大幅に高速になります。ここに存在するパラメーターは、パラメーターテーブルから最終データを取得する場合を除き、データテーブルに触れることなく、インデックス値のみを使用します。

1
Monty