Quantcast
Viewing all articles
Browse latest Browse all 207

How can a cheap function in a SELECT make the whole query slow?

I'm using Postgres 13.3 with inner and outer queries that both only produce a single row (just some stats about row counts).

I can't figure out why Query2 below is so much slower than Query1. They should basically be almost exactly the same, maybe a few ms difference at most ...

Query1: takes 49 seconds

WITH t1 AS (        SELECT            (SELECT COUNT(*) FROM racing.all_computable_xformula_bday_combos) AS all_count,            (SELECT COUNT(*) FROM racing.xday_todo_all) AS todo_count,            (SELECT COUNT(*) FROM racing.xday) AS xday_row_count        OFFSET 0 -- this is to prevent inlining)SELECT            t1.all_count,            t1.all_count-t1.todo_count AS done_count,            t1.todo_count,            t1.xday_row_countFROM t1;

Query2: takes 4 minutes and 30 seconds

And I only added one line:

WITH t1 AS (        SELECT            (SELECT COUNT(*) FROM racing.all_computable_xformula_bday_combos) AS all_count,            (SELECT COUNT(*) FROM racing.xday_todo_all) AS todo_count,            (SELECT COUNT(*) FROM racing.xday) AS xday_row_count        OFFSET 0 -- this is to prevent inlining)SELECT            t1.all_count,            t1.all_count-t1.todo_count AS done_count,            t1.todo_count,            t1.xday_row_count,            -- the line below is the only difference to Query1:            util.divide_ints_and_get_percentage_string(todo_count, all_count) AS todo_percentageFROM t1;

Before this point, and with some extra columns in the outer query (which should have made almost zero difference), the whole query was insanely slow, like 25 minutes, which I think was due to inlining maybe? Hence the OFFSET 0 being added into both queries (which does help a lot).

I've also been swapping between using the above CTEs vs subqueries, but with the OFFSET 0 included it doesn't seem to make any difference.

Definitions of the functions being called in Query2:

CREATE OR REPLACE FUNCTION util.ratio_to_percentage_string(FLOAT, INTEGER) RETURNS TEXT AS $$ BEGIN    RETURN ROUND($1::NUMERIC * 100, $2)::TEXT || '%';END; $$ LANGUAGE plpgsql IMMUTABLE;CREATE OR REPLACE FUNCTION util.divide_ints_and_get_percentage_string(BIGINT, BIGINT) RETURNS TEXT AS $$ BEGIN    RETURN CASE         WHEN $2 > 0 THEN util.ratio_to_percentage_string($1::FLOAT / $2::FLOAT, 2)        ELSE 'divide_by_zero'        END        ;END; $$ LANGUAGE plpgsql IMMUTABLE;

As you can see it's a very simple function, which is only being called once, from the single row the whole thing produces. How can this cause such a massive slowdown? And why is it affecting whether Postgres inlines the initial subquery / CTE? (Or whatever else might be going on here?)

Also, it doesn't matter what the function does at all, simply replacing it with a function that does nothing but return a TEXT string hello causes the exact same slow down of the initial inner query. So it's not about anything the function "does", but more like some kind of "Schrödinger's cat" effect, where stuff in the outer query is affecting how the inner query is initially performed. Why does a simple tiny change in the outer query (which basically has zero effect on performance) affect the initial inner query?

EXPLAIN ANALYZE outputs:


Viewing all articles
Browse latest Browse all 207

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>