Correlated Subquery Collapse |
글쓴이 : 모델광
날짜 : 2022-05-28 (토) 21:47
조회 : 4921
|
|
Last year, I worked on a
project where I had to verify that ported SQL statements from Oracle are
compatible with PostgreSQL. There were a lot of "mysterious"
performance problems in the ported queries. And Most of those problems
turned out to be a badly chosen query execution plan. After
working on this project, I realized that if we do not understand how
PostgreSQL planner works we can be in a sticky situation after migrating
data from Oracle. I have run up some test scripts to examine how PostgreSQL handles a correlated subquery. CREATE TABLE employee (
The
following query reports all employees who earned more than the average
for their department, where earnings is calculated as the sum of salary
and commission. SELECT EMPNO, SAL, COMM, DEPTNO In
Oracle I could get the result in 3 or 4 seconds. But in PostgreSQL with
the same amount of data I could get the result in about 13 seconds. In
order to solve this problem you should check the execution plan. The
following is the execution plan in PostgreSQL. Sort (actual time=13661.291..13661.432 rows=4979 loops=1) We can observe that PostgreSQL did not collapse the subquery. So it had to perform a sequential scan on the table EMPLOYEE 10,000 times which led to poor performance. It took just 1.363 ms to run the correlated subquery, but repetitive execution of the subquery took 13630 ms (13630=1.363 * 10,000). I do not know any technique to force PostgreSQL to collapse the subquery. If we run the same query in Oracle, the logical optimizer of Oracle transforms the SQL statement like this: --I got this query after running a 10053 trace event and examining the contents of the trace file. FROM ( SELECT A.EMPNO, A.SAL, A.COMM, A.DEPTNO, AVG(B.COMM +B.SAL) AS AVG_VAL FROM EMPLOYEE A, EMPLOYEE B WHERE A.DEPTNO = B.DEPTNO GROUP BY A.DEPTNO, A.EMPNO, A.SAL, A.COMM ) X WHERE SAL + COMM > AVG_VAL ORDER BY DEPTNO, EMPNO; Firstly the logical optimizer collapses the corellated subquery, so the subquery becomes an inline view. Secondly the resulting inline view merges with the main query. What a beautiful optimizer! It is like an artificial intelligence is working in Oracle. If we run the transformed query in PostgreSQL, we get the following execution plan. Sort (actual time=3315.641..3316.060 rows=4979 loops=1) We can notice that the number of block IOs dropped from 1100113 to 220. So under PostgreSQL 14 we have to be able to transform the query on our own.
If your query is relatively slow in PostgreSQL after migration, you
should check the execution plan and pinpoint the problematic operation
in the plan. In some cases you should tweak the query to remove the
problematic operation in the executioin plan. For your reference PostgreSQL does not provide SORT GROUP BY operation. It has to perform HashAggregate and then perform Sort. Let the query visit a table only once If
we take a closer look at the transformed SQL statement, we can notice
that the query is touching the table EMPLOYEE twice to get the resulting
dataset. You had better touch a table once to imrove performance. So as a quick "what if" test I tried using an analytic functioin as follows: SELECT * Sort (actual time=25.412..25.581 rows=4979 loops=1) Note that the number of block IOs has fallen from 220 to 110 and we have gotten up to 100 times performance increase (3316 -> 25 ms) in terms of execution time. I woud not argue that analytic functions do always help performance, but in this particular case it worked. Conclusion It is easy to overlook the fact that the execution plan of a query can matter for query performance. When you move your database from Oracle to PostgreSQL, you should check queries involving correlated subquries. One of the more prominent limitations of the PostgreSQL optimizer is the fact that PostgreSQL is not so good at collapsing a correlated subquery, which may have a significant impact on performance and it may not be possible to bypass the problem unless you amend the query to collapse the subquery. Added on JULY 18, 2023 When I wrote this note, I believed that the last query was the optimal choice. However, I regret to say that the query I presented in this note was not the most efficient. Recently I have discovered a different strategy that achieves better performance. You are kindly advised to read this updated note. |










