web-dev-qa-db-ja.com

LIMITを追加すると、PostgreSQLプランが良いものから悪いものに変わります

典型的なEAVスキーマに関するクエリがあります。

SELECT contacts.id
FROM contacts
         LEFT OUTER JOIN (SELECT DISTINCT contacts_values.company_id AS company_id, contacts_values.id AS id
                          FROM contacts_values
                          WHERE contacts_values.field_id = '\x000000000000000000000000'
                            AND contacts_values.field_name = 'facebook'
                            AND nimble_contact_value_normalize(nimble_skip_long_values(contacts_values.value)) =
                                nimble_contact_value_normalize('http://www.facebook.com/jon.ferrara')) AS anon_1
                         ON anon_1.company_id = contacts.company_id AND anon_1.id = contacts.id
         LEFT OUTER JOIN (SELECT DISTINCT contacts_values.company_id AS company_id, contacts_values.id AS id
                          FROM contacts_values
                          WHERE contacts_values.field_id = '\x000000000000000000000000'
                            AND contacts_values.field_name = 'last_name'
                            AND nimble_contact_value_normalize(nimble_skip_long_values(contacts_values.value)) =
                                nimble_contact_value_normalize('Ferrara')) AS anon_2
                         ON anon_2.company_id = contacts.company_id AND anon_2.id = contacts.id
         JOIN contacts__aggregated AS contacts__aggregated_1
              ON contacts__aggregated_1.company_id = contacts.company_id AND
                 contacts__aggregated_1.contact_id = contacts.id AND contacts__aggregated_1.field_name = 'names'
WHERE contacts.company_id = '\x4c2118ad54397f271b000000'
  AND (anon_1.id IS NOT NULL OR anon_2.id IS NOT NULL)
ORDER BY contacts__aggregated_1.value ASC
LIMIT 30 OFFSET 0;

私の問題は、このクエリのLIMIT句により、プランナーがネストされたループを持つ不適切なプランを選択するようになることです: https://explain.depesz.com/s/MuteSET ENABLE_NESTLOOP TO OFFの後に同じクエリを実行すると、はるかに効率的な計画が得られます: https://explain.depesz.com/s/b5kn 。クエリからLIMITを削除すると、同様の計画になります: https://explain.depesz.com/s/wDqE

私が懸念していることの1つは、ネストされていないループプランのLIMITノードのコストがそのサブノードのコストのほんの一部であるということです。ただし、マージ結合プランを改善するために、LIMITノードのコストはサブノードと同じです。どうしてこのようになるのでしょうか?そして、プランナーがより良いプランを選択できるようにするにはどうすればよいですか?

PostgreSQL10.10を実行しています。

1
rmihael

を使用してインデックスを使用しないようにPostgreSQLに指示します

_ORDER BY contacts__aggregated_1.value + 0 ASC
_

問題は、PostgreSQLがインデックスを使用して_contacts__aggregated_の順序で_ORDER BY_をスキャンし、ネストされたループを使用して他のテーブルから行を追加し、満たさない値を除外すると、最速になると考えていることです。状況、契約条項。

多くの値が除外されない場合、これは多くの場合、優れた戦略です。

しかし、それは悪い計画を生み出します

  • postgreSQLの見積もりよりもフィルター条件を満たす行が少ない
  • フィルタ条件を満たす行はすべて高いvalueを持っています

これらのことの1つが起こったに違いありません。 EXPLAIN (ANALYZE, BUFFERS)出力で高い_Rows removed by filter_を探します。

3
Laurenz Albe