Tôi đang cố gắng để hiểu rõ hơn về cách trình lập kế hoạch truy vấn hoạt động trong postgresql.
Tôi có truy vấn này:
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
Nó chạy trong chưa đầy 10ms trên cơ sở dữ liệu của tôi với khoảng 500 nghìn mục trong bảng người dùng.
Sau đó, tôi nghĩ rằng để tránh các mục con trùng lặp, tôi có thể viết lại truy vấn dưới dạng CTE, như thế này:
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;
Tuy nhiên, truy vấn viết lại này chạy trong khoảng 1 giây! Lý do tại sao điều này xảy ra? Tôi thấy trong phần giải thích rằng nó không sử dụng chỉ số hình học, nhưng có thể làm gì cho điều đó không? Cảm ơn!
Một cách khác để viết truy vấn là:
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;
Tuy nhiên, điều này cũng sẽ chậm như CTE.
Mặt khác, nếu tôi trích xuất ra các tham số cho tôi và chèn tĩnh chúng thì truy vấn lại nhanh chóng:
select u.id, u.popularity from users u
where u.gender = 'male'
order by u.latest_location::geometry <-> '0101000000A49DE61DA71C5A403D0AD7A370F54340'::geometry ASC
limit 50;
Giải thích về truy vấn đầu tiên (nhanh)
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)
Giải thích về truy vấn thứ hai (chậm)
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)
như là cte? Có lẽ đó là * nguyên nhân gây ra vấn đề này
FROM
thay vì thuật ngữ CTE để có kết quả tốt nhất.