简体   繁体   中英

Improve Query performance in Oracle & Postgres

I need to do multiple joins like below query.Have around 200 CAT_CODE.

Primary Table(PRIM):

NUM     CAT1_CODE   CAT2_CODE   CAT3_CODE
A          1           y           q     
B          2           e           a     
C          3           s           z  

Secondary Table(LOV):

CATEGORY    COLUMN_LKP        EXT_CODE
CAT1_CODE       1                AB
CAT1_CODE       2                CD
CAT1_CODE       3                HI
CAT2_CODE       y                JL
CAT2_CODE       e                QD
CAT2_CODE       s                AH
CAT3_CODE       q                CD
CAT3_CODE       a                MS
CAT3_CODE       z                EJ

REQUIRED OUTPUT:

NUM CAT1    CAT2    CAT3
A    AB      JL      CD
B    CD      QD      MS
C    HI      AH      EJ

SQL:

I have written a simple query to accomplish this task. Do you think, this would be right approach? Any other ways, to improve this query? Right now, I'm using both Oracle and Postgres.

SELECT 
NUM,
(SELECT EXT_CODE FROM TEST_LOV 
WHERE CATEGRY='CAT1_CODE' AND COLUMN_LKP=A.CAT1_CODE) CAT1,
(SELECT EXT_CODE FROM TEST_LOV 
WHERE CATEGRY='CAT2_CODE' AND COLUMN_LKP=A.CAT2_CODE) CAT2,
(SELECT EXT_CODE FROM TEST_LOV 
WHERE CATEGRY='CAT3_CODE' AND COLUMN_LKP=A.CAT3_CODE) CAT3 
FROM 
TEST_PRIM A

TEST_LOV is scanned multiple times.

Query Plan:

-------------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           |     3 |    24 |    17   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST_LOV  |     1 |    15 |     3   (0)| 00:00:01 |
|*  2 |  TABLE ACCESS FULL| TEST_LOV  |     1 |    15 |     3   (0)| 00:00:01 |
|*  3 |  TABLE ACCESS FULL| TEST_LOV  |     1 |    15 |     3   (0)| 00:00:01 |
|   4 |  TABLE ACCESS FULL| TEST_PRIM |     3 |    24 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("CATEGRY"='CAT1_CODE' AND "COLUMN_LKP"=:B1)
   2 - filter("CATEGRY"='CAT2_CODE' AND "COLUMN_LKP"=:B1)
   3 - filter("CATEGRY"='CAT3_CODE' AND "COLUMN_LKP"=:B1)

In oracle, you can unpivot the prim table and use the result to join to lov table.

with primunpiv as 
(select * from test_prim
 unpivot
 (code for category in (CAT1_CODE,CAT2_CODE,CAT3_CODE))
)
select p.num
,max(case when p.category = 'CAT1_CODE' then l.ext_code end) cat1
,max(case when p.category = 'CAT2_CODE' then l.ext_code end) cat2
,max(case when p.category = 'CAT3_CODE' then l.ext_code end) cat3
from primunpiv p
join testlov l on p.code = l.column_lkp and p.category = l.category
group by p.num

You should never embed sql queries in the select component of your statement if at all possible.

You could rewrite it so that it goes against all the tables appropriately.

select num, ... from test_prim a , test_lov b , test_lov c , test_lov d where a.cat1_code = b.column_lkup and b.catgry = 'CAT1_CODE' and ...

The only problem with this approach is in the case that one of the categories in test_prim is null, in which case your joins against test_lov have to be outer joins.

This also has the benefit of using indexes if the test_lov.column_lkup column has an index on it.

LOL - this is turning into a run-on. Your selects in the select clause basically cause full table scans and then the resolution occurs in the PGA, which is slow. The other approach stops each time there is a success, so it's much faster.

My oracle isn't very good, but I've done a similar thing in the past in SQL Server. I think this should work, and might improve your performance. If there's always a corresponding record for each different CAT code, you can change these to INNER JOIN's.

SELECT 
NUM,
B.EXT_CODE CAT1, 
C.EXT_CODE CAT2, 
D.EXT_CODE CAT3
FROM TEST_PRIM A
LEFT JOIN TEST_LOV B ON B.CATEGRY='CAT1_CODE' AND B.COLUMN_LKP = A.CAT1_CODE
LEFT JOIN TEST_LOV C ON C.CATEGRY='CAT2_CODE' AND C.COLUMN_LKP = A.CAT2_CODE
LEFT JOIN TEST_LOV D ON D.CATEGRY='CAT3_CODE' AND C.COLUMN_LKP = A.CAT3_CODE

SQLFiddle: Postgres

WITH
cat1 AS ( SELECT EXT_CODE, COLUMN_LKP FROM LOV WHERE CATEGORY='CAT1_CODE' ),
cat2 AS ( SELECT EXT_CODE, COLUMN_LKP FROM LOV WHERE CATEGORY='CAT2_CODE' ),
cat3 AS ( SELECT EXT_CODE, COLUMN_LKP FROM LOV WHERE CATEGORY='CAT3_CODE' )

SELECT num, cat1.ext_code CAT1, cat2.ext_code CAT2, cat3.ext_code CAT3
FROM prim
LEFT JOIN cat1 ON prim.cat1_code::varchar(1) = cat1.column_lkp
LEFT JOIN cat2 ON prim.cat2_code = cat2.column_lkp
LEFT JOIN cat3 ON prim.cat3_code = cat3.column_lkp
;

Problems w/ Your Question

  1. Your query uses TEST_LOV and TEST_PRIM , but your question labels them as LOV and PRIM , respectively
  2. Your query uses CATEGRY , but your question defines the table with field, CATEGORY

在CATEGORY上具有索引,COLUMN_LKP将避免全表扫描,并在您拥有巨大数据集的情况下更快地获取结果。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM