Update statement on PostgreSQL not using primary key index to update

Viewed 66

I have a stored procedure on PostgreSQL like this:

create or replace procedure new_emp_sp (f_name varchar, l_name varchar, age integer, threshold integer, dept varchar)
language plpgsql
as $$
declare
    new_emp_count integer;
begin
    INSERT INTO employees (id, first_name, last_name, age)
        VALUES (nextval('emp_id_seq'), 
                random_string(10),
                random_string(20),
                age);
    select count(*) into new_emp_count from employees where age > threshold;
    update dept_employees set emp_count = new_emp_count where id = dept;
end; $$

I have enabled auto_explain module and set log_min_duration to 0 so that it logs everything.

I have an issue with the update statement in the procedure. From the auto_explain logs I see that it is not using the primary key index to update the table:

->  Seq Scan on dept_employees  (cost=0.00..1.05 rows=1 width=14) (actual time=0.005..0.006 rows=1 loops=1)
        Filter: ((id)::text = 'ABC'::text)
        Rows Removed by Filter: 3

This worked as expected until a couple of hours ago and I used to get a log like this:

->  Index Scan using dept_employees_pkey on dept_employees  (cost=0.15..8.17 rows=1 width=48) (actual time=0.010..0.011 rows=1 loops=1)
        Index Cond: ((id)::text = 'ABC'::text)

Without the procedure, if I run the statement standalone like this:

explain analyze update dept_employees set emp_count = 123 where id = 'ABC';

The statement correctly uses the primary key index:

Update on dept_employees  (cost=0.15..8.17 rows=1 width=128) (actual time=0.049..0.049 rows=0 loops=1)
->  Index Scan using dept_employees_pkey on dept_employees  (cost=0.15..8.17 rows=1 width=128) (actual time=0.035..0.036 rows=1 loops=1)
        Index Cond: ((id)::text = 'ABC'::text)

I can't figure out what has gone wrong especially because it worked perfectly just a couple of hours ago.

1 Answers

It is faster to scan N rows sequentially than to scan N rows using an index. So for small tables Postgres may decide that a sequence scan is faster than an index scan.

PL/pgSQL can cache prepared statements and execution plans, so you're probably getting a cached execution plan from when the table was smaller.

Related