1

I am working in an Oracle to PostgreSQL migration project. Today I found what I think is a PostgreSQL bug. I wonder whether I missed something in my experimentation.

The following is my test code.

create table t2 as
select i as c1
     , '12000103' as c2
 from generate_series(1,1000000) a(i);

    explain (analyze, verbose)
    select *
  from t2
 where c2 = to_char(current_date, 'yyyymmdd');

After running the above SQL statement I got this execution plan.

 Gather  (cost=1000.00..14739.43 rows=1 width=13) (actual time=950.342..954.845 rows=0 loops=1)
   Output: c1, c2
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on postgres_air.t2  (cost=0.00..13739.33 rows=1 width=13) (actual time=929.924..929.925 rows=0
loops=3)
         Output: c1, c2
         Filter: (t2.c2 = to_char((CURRENT_DATE)::timestamp with time zone, 'yyyymmdd'::text))
         Rows Removed by Filter: 333333
         Worker 0:  actual time=917.776..917.776 rows=0 loops=1
         Worker 1:  actual time=925.052..925.053 rows=0 loops=1
 Planning Time: 0.482 ms
 Execution Time: 954.978 ms
(12 rows)

Compared with Oracle's elapsed time, the execution time in PostgreSQL was too long. I couldn't find out the reason why PostgreSQL was so slow in this simple query. Coincidently I changed where clause.

 explain (analyze, verbose)
select *
  from t2
 where c2 = (select to_char(current_date, 'yyyymmdd'));

 Seq Scan on postgres_air.t2  (cost=0.02..17906.02 rows=1000000 width=13) (actual time=236.536..236.538 rows=0 loops=1)
   Output: t2.c1, t2.c2
   Filter: (t2.c2 = $0)
   Rows Removed by Filter: 1000000
   InitPlan 1 (returns $0)
     ->  Result  (cost=0.00..0.02 rows=1 width=32) (actual time=0.025..0.027 rows=1 loops=1)
           Output: to_char((CURRENT_DATE)::timestamp with time zone, 'yyyymmdd'::text)
 Planning Time: 0.460 ms
 Execution Time: 236.892 ms

The elapsed time dropped from 954 ms to 236 ms. My question is :

  1. Why is the revised query more performant?
  2. Is this just a PostgreSQL bug?
  3. If this is a PostgreSQL bug, should I use a subquery whenever I use a PostgreSQL internal function?
3
  • 1
    If you had a sensible index, the first one would be so fast you would have trouble measuring it reliably. Who knows, maybe in Oracle you do have sensible index.
    – jjanes
    Commented Oct 4, 2021 at 15:15
  • 1
    Why are you storing date values in varchar columns to begin with? That's a really bad idea. Do you have a chance to fix that broken data model?
    – user1822
    Commented Oct 4, 2021 at 15:20
  • In Oracle the table id modeled to store a data value in varchar columns. We have decided not to change the type of the column. In Oracle we don't have an index on the column either.
    – JAEGEUN YU
    Commented Oct 4, 2021 at 21:36

1 Answer 1

1

The question is why the subquery in the second example is executed only once, instead of once for each row, which accounts for the difference in execution time.

The reason is that the subquery is not “correlated” with the surrounding query, that is, it does not reference anything from the outer query. The optimizer then executes the query only once (InitPlan) and uses the result for each row.

One can argue (and it has been argued before) that this behavior is wrong, but with equal right you could say that if you execute it as a subquery, you want it executed independently. At any rate, this is known and accepted behavior, and I don't think you will be able to convince PostgreSQL that this is a bug.

If you want the subquery to be executed for each row, reference the outer query:

SELECT *
FROM t2
WHERE c2 = (SELECT CASE WHEN c2 IS NOT NULL
                        THEN to_char(current_date, 'yyyymmdd')
                   END);
5
  • The experimentation of my question was done on a virtual machine in my laptop computer. I did the experimentation on a different server again and I got the same result. My wild guess is that when I use c2=to_char(current_date,'yyyymmdd'), the to_char internal function is executed 1000000 times and when I use c2 = (select to_char(current_date,'yyyymmdd')), the to_char internal function is executed only once. But I am not sure. When I look at pg_proc view, to_char is stable function, not immutable function.
    – JAEGEUN YU
    Commented Oct 4, 2021 at 21:44
  • Ah, I see now. I misunderstood the question. See my revised answer. Commented Oct 5, 2021 at 4:18
  • Now I am clear. Thank you very much.
    – JAEGEUN YU
    Commented Oct 7, 2021 at 5:11
  • @LaurenzAlbe any idea why in the first example the function gets invoked for every row? current_date is "stable" (remains the same for the duration of the transaction); so is to_char(). The function invocation is not "correlated" to the table. One would expect the optimiser to make the same choice in both cases.
    – mustaccio
    Commented Dec 23, 2022 at 20:47
  • @mustaccio I guess this possible optimization is just not implemented. Commented Dec 24, 2022 at 5:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.