Hello!

I'm using Postgres 7.4.5, sort_mem is 8192. Tables analyzed / vacuumed.

Here's a function I'm using to get an age from the user's birthday:

agey(date) -> SELECT date_part('year', age($1::timestamp))


The problem is, why do the plans differ so much between Q1 & Q3 below? Something with 
age() being a non-IMMUTABLE function?


Q1: explain analyze SELECT al.pid, al.owner, al.title, al.front, al.created_at, 
al.n_images, u.username as owner_str, u.image as owner_image, u.puid as owner_puid 
FROM albums al  , users u WHERE  u.uid = al.owner AND  al.security='a' AND al.n_images 
> 0 AND date_part('year', age(u.born)) > 17 AND date_part('year', age(u.born)) < 20 
AND city = 1 ORDER BY al.id DESC LIMIT 9;
                                                                                       
                                                                      QUERY PLAN       
                                                                            
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=5700.61..5700.63 rows=9 width=183) (actual time=564.291..564.299 rows=9 
loops=1)
   ->  Sort  (cost=5700.61..5700.82 rows=83 width=183) (actual time=564.289..564.291 
rows=9 loops=1)
         Sort Key: al.id
         ->  Nested Loop  (cost=0.00..5697.97 rows=83 width=183) (actual 
time=30.029..526.211 rows=4510 loops=1)
               ->  Seq Scan on users u  (cost=0.00..5311.05 rows=86 width=86) (actual 
time=5.416..421.264 rows=3021 loops=1)
                     Filter: ((date_part('year'::text, 
age((('now'::text)::date)::timestamp with time zone, (born)::timestamp with time 
zone)) > 17::double precision) AND (date_part('year'::text, 
age((('now'::text)::date)::timestamp with time zone, (born)::timestamp with time 
zone)) < 20::double precision) AND (city = 1))
               ->  Index Scan using albums_owner_key on albums al  (cost=0.00..4.47 
rows=2 width=101) (actual time=0.014..0.025 rows=1 loops=3021)
                     Index Cond: ("outer".uid = al."owner")
                     Filter: (("security" = 'a'::bpchar) AND (n_images > 0))
 Total runtime: 565.120 ms
(10 rows)


Result when removing the second age-check (AND date_part('year', age(u.born)) < 20):

Q2: explain analyze SELECT al.pid, al.owner, al.title, al.front, al.created_at, 
al.n_images, u.username as owner_str, u.image as owner_image, u.puid as owner_puid 
FROM albums al, users u WHERE  u.uid = al.owner AND  al.security='a' AND al.n_images > 
0 AND date_part('year', age(u.born)) > 17 AND city = 1 ORDER BY al.id DESC LIMIT 9;
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.00..140.95 rows=9 width=183) (actual time=0.217..2.474 rows=9 loops=1)
   ->  Nested Loop  (cost=0.00..86200.99 rows=5504 width=183) (actual 
time=0.216..2.464 rows=9 loops=1)
         ->  Index Scan Backward using albums_id_key on albums al  (cost=0.00..2173.32 
rows=27610 width=101) (actual time=0.086..1.080 rows=40 loops=1)
               Filter: (("security" = 'a'::bpchar) AND (n_images > 0))
         ->  Index Scan using users_pkey on users u  (cost=0.00..3.03 rows=1 width=86) 
(actual time=0.031..0.031 rows=0 loops=40)
               Index Cond: (u.uid = "outer"."owner")
               Filter: ((date_part('year'::text, age((('now'::text)::date)::timestamp 
with time zone, (born)::timestamp with time zone)) > 17::double precision) AND (city = 
1))
 Total runtime: 2.611 ms
(8 rows)

Trying another approach: adding a separate "stale" age-column to the users-table:

alter table users add column age smallint;
update users set age=date_part('year'::text, age((('now'::text)::date)::timestamp with 
time zone, (born)::timestamp with time zone));
analyze users;

Result with separate column:
Q3: explain analyze SELECT al.pid, al.owner, al.title, al.front, al.created_at, 
al.n_images, u.username as owner_str, u.image as owner_image, u.puid as owner_puid 
FROM albums al  , users u WHERE  u.uid = al.owner AND  al.security='a' AND al.n_images 
> 0 AND age > 17 AND age < 20 AND city = 1 ORDER BY al.id DESC LIMIT 9;
--------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.00..263.40 rows=9 width=183) (actual time=0.165..2.832 rows=9 loops=1)
   ->  Nested Loop  (cost=0.00..85925.69 rows=2936 width=183) (actual 
time=0.163..2.825 rows=9 loops=1)
         ->  Index Scan Backward using albums_id_key on albums al  (cost=0.00..2173.32 
rows=27610 width=101) (actual time=0.043..1.528 rows=56 loops=1)
               Filter: (("security" = 'a'::bpchar) AND (n_images > 0))
         ->  Index Scan using users_pkey on users u  (cost=0.00..3.02 rows=1 width=86) 
(actual time=0.020..0.020 rows=0 loops=56)
               Index Cond: (u.uid = "outer"."owner")
               Filter: ((age > 17) AND (age < 20) AND (city = 1))
 Total runtime: 2.973 ms
(8 rows)

My question is, why doesn't the planner pick the same plan for Q1 & Q3?

/Nichlas

---------------------------(end of broadcast)---------------------------
TIP 8: explain analyze is your friend

Reply via email to