sql >> データベース >  >> RDS >> PostgreSQL

WHERE句で同じ列を複数回使用する

    これは、関係分割の場合です。タグを追加しました。

    インデックス

    USER_PROPERTY_MAP(property_value_id, user_id)にPKまたはUNIQUE制約があると仮定します -クエリを高速化するためのこの順序の列。関連:

    • 複合インデックスは、最初のフィールドのクエリにも適していますか?

    PROPERTY_VALUE(value, property_name_id, id)にもインデックスが必要です 。繰り返しますが、この順序で列を作成します。最後の列を追加しますid インデックスのみのスキャンを取得した場合のみ。

    指定された数のプロパティに対して

    それを解決する方法はたくさんあります。これは、正確に2つの場合、最も単純で最速の1つである必要があります。 プロパティ:

    SELECT u.*
    FROM   users             u
    JOIN   user_property_map up1 ON up1.user_id = u.id
    JOIN   user_property_map up2 USING (user_id)
    WHERE  up1.property_value_id =
          (SELECT id FROM property_value WHERE property_name_id = 1 AND value = '101')
    AND    up2.property_value_id =
          (SELECT id FROM property_value WHERE property_name_id = 2 AND value = '102')
    -- AND    u.user_name = 'user1'  -- more filters?
    -- AND    u.city = 'city1'
    

    テーブルPROPERTY_NAMEにアクセスしていません 、クエリの例によると、プロパティ名はすでにIDに解決されているようです。それ以外の場合は、PROPERTY_NAMEに結合を追加できます 各サブクエリで。

    この関連する質問の下で、技術の武器を集めました:

    • SQLの結果をフィルタリングしてhas-many-through関係にする方法

    不明な数の物件の場合

    @Mikeと@Valeraには、それぞれの回答に非常に役立つクエリがあります。これをさらに動的にするため :

    WITH input(property_name_id, value) AS (
          VALUES  -- provide n rows with input parameters here
            (1, '101')
          , (2, '102')
          -- more?
          ) 
    SELECT *
    FROM   users u
    JOIN  (
       SELECT up.user_id AS id
       FROM   input
       JOIN   property_value    pv USING (property_name_id, value)
       JOIN   user_property_map up ON up.property_value_id = pv.id
       GROUP  BY 1
       HAVING count(*) = (SELECT count(*) FROM input)
       ) sub USING (id);
    

    VALUESからのみ行を追加/削除します 表現。または、WITHを削除します 句とJOIN プロパティフィルタなし まったく。

    問題 このクラスのクエリ(すべての部分一致をカウント)では、パフォーマンスです。 。私の最初のクエリはそれほど動的ではありませんが、通常はかなり高速です。 (EXPLAIN ANALYZEでテストするだけです 。)特に大きなテーブルと増え続けるプロパティの場合。

    両方の長所は?

    再帰CTEを使用するこのソリューションは、適切な妥協点となるはずです。高速および 動的:

    WITH RECURSIVE input AS (
       SELECT count(*)     OVER () AS ct
            , row_number() OVER () AS rn
            , *
       FROM  (
          VALUES  -- provide n rows with input parameters here
            (1, '101')
          , (2, '102')
          -- more?
          ) i (property_name_id, value)
       )
     , rcte AS (
       SELECT i.ct, i.rn, up.user_id AS id
       FROM   input             i
       JOIN   property_value    pv USING (property_name_id, value)
       JOIN   user_property_map up ON up.property_value_id = pv.id
       WHERE  i.rn = 1
    
       UNION ALL
       SELECT i.ct, i.rn, up.user_id
       FROM   rcte              r
       JOIN   input             i ON i.rn = r.rn + 1
       JOIN   property_value    pv USING (property_name_id, value)
       JOIN   user_property_map up ON up.property_value_id = pv.id
                                  AND up.user_id = r.id
       )
    SELECT u.*
    FROM   rcte  r
    JOIN   users u USING (id)
    WHERE  r.ct = r.rn;          -- has all matches
    

    ここにdbfiddle

    再帰的CTEに関するマニュアル。

    追加された複雑さは、追加のオーバーヘッドが利点を上回っている、または最初から違いが無視できる小さなテーブルには影響しません。ただし、拡張性ははるかに高く、テーブルが増え、プロパティフィルターの数が増える「カウント」手法よりもますます優れています。

    カウントテクニックはすべてにアクセスする必要があります user_property_mapの行 指定されたすべてのプロパティフィルターに対して、このクエリ(および最初のクエリ)は無関係なユーザーを早期に排除できます。

    パフォーマンスの最適化

    現在のテーブル統計(合理的な設定、autovacuum 実行中)、Postgresは「最も一般的な値」に関する知識を持っています 各列で、最初のクエリの結合を並べ替えます 最も選択的なプロパティフィルターを最初に評価します(または少なくとも最も選択性の低いフィルターではありません)。特定の制限まで:join_collapse_limit 。関連:

    • Postgresqljoin_collapse_limitとクエリプランニングの時間
    • 検索語を少し変更すると、クエリが大幅に遅くなるのはなぜですか?

    この「deus-ex-machina」介入は、3番目のクエリでは不可能です。 (再帰CTE)。パフォーマンスを向上させるには(おそらく多くの場合)、最初に自分でより選択的なフィルターを配置する必要があります。ただし、最悪の場合の順序でも、クエリのカウントを上回ります。

    関連:

    • PostgreSQLで統計ターゲットを確認する

    非常に厄介な詳細:

    • 既存のデータを含むテーブルで作成された場合、PostgreSQLの部分インデックスは使用されません

    マニュアルの詳細説明:

    • プランナーが使用する統計


    1. カーソルオプションのフォローアップ

    2. AndroidのListViewでクエリを削除して更新する(sqlite)

    3. SQLテーブル結合のON句とWHERE句の違い

    4. SQLServerのIN論理演算子とは-SQLServer/TSQLチュートリアルパート122