クエリプランナーがpostgresqlでどのように機能するかをよりよく理解しようとしています。
私はこのクエリを持っています:
select id from users
where id <> 2
and gender = (select gender from users where id = 2)
order by latest_location::geometry <-> (select latest_location from users where id = 2) ASC
limit 50
私のデータベースでは10ms未満で実行され、usersテーブルには約500kのエントリがあります。
次に、副選択の重複を避けるために、次のようにクエリをCTEとして書き直すことができると考えました。
with me as (
select * from users where id = 2
)
select u.id, u.popularity from users u, me
where u.gender = me.gender
order by u.latest_location::geometry <-> me.latest_location::geometry ASC
limit 50;
ただし、この書き換えられたクエリは約1秒で実行されます。なぜこれが起こるのですか?説明でジオメトリインデックスを使用していないことを確認しましたが、そのために何ができますか?ありがとう!
クエリを作成する別の方法は次のとおりです。
select u.id, u.popularity from users u, (select gender, latest_location from users where id = 2) as me
where u.gender = me.gender
order by u.latest_location::geometry <-> me.latest_location::geometry ASC
limit 50;
ただし、これはCTEと同じくらい遅くなります。
一方、meパラメーターを抽出して静的に挿入すると、クエリは再び高速になります。
select u.id, u.popularity from users u
where u.gender = 'male'
order by u.latest_location::geometry <-> '0101000000A49DE61DA71C5A403D0AD7A370F54340'::geometry ASC
limit 50;
最初の(高速)クエリについて説明する
Limit (cost=5.69..20.11 rows=50 width=36) (actual time=0.512..8.114 rows=50 loops=1)
InitPlan 1 (returns $0)
-> Index Scan using users_pkey on users users_1 (cost=0.42..2.64 rows=1 width=32) (actual time=0.032..0.033 rows=1 loops=1)
Index Cond: (id = 2)
InitPlan 2 (returns $1)
-> Index Scan using users_pkey on users users_2 (cost=0.42..2.64 rows=1 width=4) (actual time=0.009..0.010 rows=1 loops=1)
Index Cond: (id = 2)
-> Index Scan using users_latest_location_gix on users (cost=0.41..70796.51 rows=245470 width=36) (actual time=0.509..8.100 rows=50 loops=1)
Order By: (latest_location <-> $0)
Filter: (gender = $1)
Rows Removed by Filter: 20
Total runtime: 8.211 ms
(12 rows)
2番目の(遅い)クエリの説明
Limit (cost=62419.82..62419.95 rows=50 width=76) (actual time=1024.963..1024.970 rows=50 loops=1)
CTE me
-> Index Scan using users_pkey on users (cost=0.42..2.64 rows=1 width=221) (actual time=0.037..0.038 rows=1 loops=1)
Index Cond: (id = 2)
-> Sort (cost=62417.18..63030.86 rows=245470 width=76) (actual time=1024.959..1024.963 rows=50 loops=1)
Sort Key: ((u.latest_location <-> me.latest_location))
Sort Method: top-N heapsort Memory: 28kB
-> Hash Join (cost=0.03..54262.85 rows=245470 width=76) (actual time=0.122..938.131 rows=288646 loops=1)
Hash Cond: (u.gender = me.gender)
-> Seq Scan on users u (cost=0.00..49353.41 rows=490941 width=48) (actual time=0.021..465.025 rows=490994 loops=1)
-> Hash (cost=0.02..0.02 rows=1 width=36) (actual time=0.054..0.054 rows=1 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 1kB
-> CTE Scan on me (cost=0.00..0.02 rows=1 width=36) (actual time=0.047..0.049 rows=1 loops=1)
Total runtime: 1025.096 ms
(select id, latest_location from users where id = 2)
cteとして使用するとどうなりますか?多分それは、この問題を引き起こしている*です
私はあなたが反対の性別の最も近いユーザーを探すだろうと思っただろう:)
—
cha
@chaは速度の違いはなく、CTEで性別と場所を選択するだけです。(私の場合、私は質問のクエリを単純化しただけで、類似したユーザーの平均を取りたいです)
—
viblo
@CraigRinger最適化のフェンスだとは思いません。私もあなたの提案を試みました、そしてそれはまた遅かったです。一方、手動でパラメーターを抽出した場合、それは迅速です(そして私の場合、それが実際のオプションであり、最終的に結果は関数です)。
—
viblo
FROM
最良の結果を得るには、CTE用語の代わりにサブクエリを試してください。