製品の特定の順序を返すPostgreSQL関数を作成しました。ここで、表示するだけでなく、最初のSELECT
クエリの結果も配列に入れて、IDを別のselectクエリ内で再利用できるようにします。最初にSELECT * FROM (SELECT id FROM products) as pr
のようなエイリアスをselectクエリに追加し、2番目のクエリのNOT IN(pr)
ステートメント内でpr
を使用しようとしましたが、機能しません。 。
例を使ってより明確に説明します。これは関数の簡略版です。
_CREATE OR REPLACE FUNCTION featured_products(
valid_to_in timestamp without time zone,
taxonomy_id_in integer,
product_limit_in integer)
RETURNS SETOF integer AS
$BODY$
BEGIN
RETURN QUERY
(
-- #1
SELECT * FROM (
SELECT "product"."supplier_id" FROM products AS "product"
) AS "featured"
LIMIT 2
)
UNION ALL
SELECT *
FROM (
SELECT "product"."supplier_id" FROM products AS "product"
) AS "featured"
WHERE id NOT IN (
-- #2
SELECT * FROM (
SELECT "product"."supplier_id" FROM products AS "product"
) AS "featured"
LIMIT 2
)
LIMIT product_limit_in;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
_
いくつかの結合と_GROUP BY
_および_ORDER BY
_ステートメントを削除したので、関数はもう少し読みやすくなりました。そして、上記のコード内に_#1
_と_#2
_を追加したので、クエリ1と2を選択することの意味がわかります。
ご覧のとおり、クエリ#2はクエリ#1と同じ結果を返すはずです。実際には、これらのクエリははるかに大きくなります。したがって、2番目の同一のクエリをIDの配列だけに置き換えたいだけです。コードが少なく、おそらく高速です。
最初のクエリから返されたIDを配列に追加し、それを2番目のクエリの代わりにNOT IN(<id's>)
ステートメントに配置する方法がわかりません。
これを修正する方法を知っている人はいますか?
[〜#〜] cte [〜#〜]の教科書ケースです。 @ Danielコメント のように。
例はもう少し簡略化できます。また、LIMIT
クエリでUNION
がどのように機能するかを知っておく必要があります。
_CREATE OR REPLACE FUNCTION featured_products(valid_to_in timestamp
, taxonomy_id_in integer
, product_limit_in integer)
RETURNS SETOF integer AS
$func$
BEGIN
RETURN QUERY
WITH featured AS (SELECT supplier_id FROM products LIMIT 2)
SELECT supplier_id
FROM featured
UNION ALL
(
SELECT p.supplier_id
FROM products p
LEFT JOIN featured f USING (supplier_id)
WHERE f.supplier_id IS NULL
LIMIT product_limit_in
) -- parens required - or not?
END
$func$ LANGUAGE plpgsql VOLATILE;
_
LIMIT
は、クエリの脚を括弧で囲まない限り、UNION
(ALL
)クエリで1回だけ適用できます。かっこを追加する場合と追加しない場合があります。
product_limit_in
_行がさらに CTEから「注目の」行に返されます。product_limit_in
_行 total が表示されます。これは、「注目の」製品でさえ破棄される可能性があることを意味します。いずれにせよ、LIMIT
の前に、 _ORDER BY
_ outer(結合)の結果を表示しないでください。あなたはそれを避けることができます。 Postgresはクエリを非常に効率的に最適化し、十分な行が返されると評価を停止します(一致するインデックスの先頭からタプルをフェッチする可能性があります)。それはもはや不可能であり、巨大なパフォーマンスの違いを生む可能性があります。
_LEFT JOIN / NOT NULL
_を使用して2番目のSELECTから注目の行を除外します。これは、おそらく_NOT IN
_よりも高速であり、NULL値または空の結果を処理するときに「驚き」をもたらしません。
Postgresでは(他のRDBMSとは対照的に)、USING (supplier_id)
で結合した後、_p.supplier_id
_および_f.supplier_id
_を参照できます。
はい、CTEは1回だけ評価されます:
WITH
クエリの便利なプロパティは、親クエリによって複数回参照されている場合でも、親クエリの実行ごとに一度だけ評価されることです。兄弟のWITH
クエリ。
太字強調鉱山。