Thread: Query plan when using currval

Query plan when using currval

From
Steve Baldwin
Date:
Hi,

Can someone please help me understand why the first query doesn't use the primary key index whereas the second query does use it:

bcaas=> explain analyze select id from log_statement where id = currval('log_statement_id_seq');
                                               QUERY PLAN                                               
--------------------------------------------------------------------------------------------------------
 Seq Scan on log_statement  (cost=0.00..14.05 rows=1 width=8) (actual time=0.027..0.027 rows=0 loops=1)
   Filter: (id = currval('log_statement_id_seq'::regclass))
   Rows Removed by Filter: 9
 Planning time: 0.081 ms
 Execution time: 0.048 ms
(5 rows)

bcaas=> explain analyze select id from log_statement where id = (select currval('log_statement_id_seq'));
                                                              QUERY PLAN                                                               
---------------------------------------------------------------------------------------------------------------------------------------
 Index Only Scan using log_statement_pkey on log_statement  (cost=0.16..8.18 rows=1 width=8) (actual time=0.017..0.017 rows=0 loops=1)
   Index Cond: (id = $0)
   Heap Fetches: 0
   InitPlan 1 (returns $0)
     ->  Result  (cost=0.00..0.01 rows=1 width=8) (actual time=0.003..0.004 rows=1 loops=1)
 Planning time: 0.105 ms
 Execution time: 0.045 ms
(7 rows)

(This is with pg 10.3)

Thanks,

Steve

Re: Query plan when using currval

From
Tom Lane
Date:
Steve Baldwin <steve.baldwin@gmail.com> writes:
> Can someone please help me understand why the first query doesn't use the
> primary key index whereas the second query does use it:

> bcaas=> explain analyze select id from log_statement where id =
> currval('log_statement_id_seq');

currval is marked as a volatile function (because its value can change
during a statement), so it's not a candidate for use in an index
qualifier.  In principle (i.e. in a naive interpretation of SQL
semantics), the WHERE clause should be evaluated in full for each row
produced by the FROM clause --- and if a volatile function is involved
then we actually have to do it that way, because we don't know enough
about how the function will act.  So you get a seqscan.  But a clause
comparing an indexed column to a value that we know won't change for
the duration of the scan is OK to optimize into an indexscan.

> bcaas=> explain analyze select id from log_statement where id = (select
> currval('log_statement_id_seq'));

Use of a sub-select hides the function's volatility, making it possible to
use the clause as an index qual.  This is not quite entirely unprincipled,
but it relies on the fact that this is an uncorrelated sub-select, so it
needn't be re-executed for each row of the outer query.  You could argue
about how well that agrees with a naive view of SQL semantics ;-) ... but
Postgres has acted this way for as long as it's had sub-selects.

            regards, tom lane


Re: Query plan when using currval

From
David Rowley
Date:
On 9 May 2018 at 18:24, Steve Baldwin <steve.baldwin@gmail.com> wrote:
> bcaas=> explain analyze select id from log_statement where id = (select currval('log_statement_id_seq'));

I know Tom has answered your question, but you may be interested in
INSERT RETURNING as this will save you from having to do this to
lookup the ID of the last inserted sequence value.

https://www.postgresql.org/docs/10/static/sql-insert.html

-- 
 David Rowley                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services


Re: Query plan when using currval

From
Steve Baldwin
Date:
Thanks Tom and David. My current logic flow was to do an insert and then a bit later, and update the same row. The update was using 'where id = currval(...)' and was getting slower and slower as the table grew (surprise surprise). I realise I can solve my performance issue in a couple of ways, but I think I'm going to refactor it and try to avoid the update.

I really appreciate the insight.

Steve

On Thu, May 10, 2018 at 8:22 AM, David Rowley <david.rowley@2ndquadrant.com> wrote:
On 9 May 2018 at 18:24, Steve Baldwin <steve.baldwin@gmail.com> wrote:
> bcaas=> explain analyze select id from log_statement where id = (select currval('log_statement_id_seq'));

I know Tom has answered your question, but you may be interested in
INSERT RETURNING as this will save you from having to do this to
lookup the ID of the last inserted sequence value.

https://www.postgresql.org/docs/10/static/sql-insert.html

--
 David Rowley                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services