一連のネストされたサブクエリを介して、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;
パラメータは、上記のパーセント記号によって区別されます。
関数を返すセットを使用できます:
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)
@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
ほとんどの場合、セットを返す関数を使用する方法がありますが、セットの読み取りと書き込みの両方を行う場合は、ビューの方が適切な場合があります。そして、それは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]
あなたが述べたような「動的な」見解は可能ではないと思います。
代わりに2つの引数を取るストアドプロシージャを作成してみませんか?
クエリを次のように言い換えます。
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演算子を使用するよりも大幅に高速になります。ここに存在するパラメーターは、パラメーターテーブルから最終データを取得する場合を除き、データテーブルに触れることなく、インデックス値のみを使用します。