web-dev-qa-db-ja.com

配列に結果を追加してSELECTクエリを再利用しますか?

製品の特定の順序を返す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>)ステートメントに配置する方法がわかりません。

これを修正する方法を知っている人はいますか?

1
Erik van de Ven

[〜#〜] 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は、クエリの脚を括弧で囲まない限り、UNIONALL)クエリで1回だけ適用できます。かっこを追加する場合と追加しない場合があります。

    • 私のやり方では、最大_product_limit_in_行がさらに CTEから「注目の」行に返されます。
    • 括弧を削除すると、最大_product_limit_in_行 total が表示されます。これは、「注目の」製品でさえ破棄される可能性があることを意味します。
      関連: 2つの大きなテーブルでクエリを最適化する
  • いずれにせよ、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クエリ。

太字強調鉱山。

0