Can a force a bind variable to work as a literal variable???
A reader, March 27, 2021 - 5:43 pm UTC
select * from v$version;
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production 0
PL/SQL Release 12.1.0.2.0 - Production 0
CORE 12.1.0.2.0 Production 0
TNS for 64-bit Windows: Version 12.1.0.2.0 - Production 0
NLSRTL Version 12.1.0.2.0 - Production 0
So my query is something like this -
SELECT ...
, ...
FROM (
SELECT RANK ()
OVER (PARTITION BY col1
ORDER BY col2 DESC) AS RANK
, ...
, colN
FROM <<table_name>>
WHERE date < :x + 1
)
WHERE RANK = 1
AND col = :bindNewState
AND ( ( :5 = 'NO'
AND col5 || col6 IN
(SELECT col1 || col2
FROM table2
WHERE col3 = :bindCol3
UNION ALL
SELECT col1 || 0
FROM table2
WHERE col3 = :bindCol3))
OR (:5 = 'YES'))
And the plan it generates is as follows -
Plan
SELECT STATEMENT ALL_ROWSCost: 5,970 Bytes: 4,781,140 Cardinality: 13,135
5 NESTED LOOPS Cost: 2 Bytes: 41 Cardinality: 1
2 TABLE ACCESS BY INDEX ROWID TABLE <<TableNameHidden>> Cost: 1 Bytes: 21 Cardinality: 1
1 INDEX UNIQUE SCAN INDEX (UNIQUE) <<IndexNameHIdden>> Cost: 0 Cardinality: 1
4 TABLE ACCESS BY INDEX ROWID TABLE <<TableNameHidden>> Cost: 1 Bytes: 20 Cardinality: 1
3 INDEX UNIQUE SCAN INDEX (UNIQUE) <<IndexNameHIdden>> Cost: 0 Cardinality: 1
10 NESTED LOOPS Cost: 2 Bytes: 41 Cardinality: 1
7 TABLE ACCESS BY INDEX ROWID TABLE <<TableNameHidden>> Cost: 1 Bytes: 21 Cardinality: 1
6 INDEX UNIQUE SCAN INDEX (UNIQUE) <<IndexNameHIdden>> Cost: 0 Cardinality: 1
9 TABLE ACCESS BY INDEX ROWID TABLE <<TableNameHidden>> Cost: 1 Bytes: 20 Cardinality: 1
8 INDEX UNIQUE SCAN INDEX (UNIQUE) <<IndexNameHIdden>>Cost: 0 Cardinality: 1
26 FILTER <<<<<<---------------------------------
20 VIEW REPORTING. Cost: 5,970 Bytes: 4,781,140 Cardinality: 13,135
19 WINDOW SORT PUSHED RANK Cost: 5,970 Bytes: 1,169,015 Cardinality: 13,135
18 HASH JOIN Cost: 5,666 Bytes: 1,169,015 Cardinality: 13,135
16 NESTED LOOPS Cost: 5,666 Bytes: 1,169,015 Cardinality: 13,135
14 NESTED LOOPS
12 STATISTICS COLLECTOR
11 TABLE ACCESS FULL TABLE viewTable2 Cost: 1,266 Bytes: 932,585 Cardinality: 13,135
13 INDEX UNIQUE SCAN INDEX (UNIQUE) viewTable1_PK
15 TABLE ACCESS BY INDEX ROWID TABLE viewTable1 T Cost: 4,400 Bytes: 18 Cardinality: 1
17 TABLE ACCESS FULL TABLE viewTable1 Cost: 4,400 Bytes: 950,256 Cardinality: 52,792
25 UNION-ALL
22 TABLE ACCESS BY INDEX ROWID BATCHED TABLE table1 Cost: 9 Bytes: 85 Cardinality: 5
21 INDEX RANGE SCAN INDEX table1index1 Cost: 4 Cardinality: 461
24 TABLE ACCESS BY INDEX ROWID BATCHED TABLE table1 Cost: 9 Bytes: 55 Cardinality: 5
23 INDEX RANGE SCAN INDEX table1index1Cost: 4 Cardinality: 461
Now I modify the query to -
SELECT ...
, ...
FROM (
SELECT RANK ()
OVER (PARTITION BY col1
ORDER BY col2 DESC) AS RANK
, ...
, colN
FROM <<table_name>>
WHERE date < :x + 1
)
WHERE RANK = 1
AND col = :bindNewState
AND ( ( 'NO' = 'NO'
AND col5 || col6 IN
(SELECT col1 || col2
FROM table2
WHERE col3 = :bindCol3
UNION ALL
SELECT col1 || 0
FROM table2
WHERE col3 = :bindCol3))
OR ('NO' = 'YES'))
And the plan is as follows -
Plan
SELECT STATEMENT ALL_ROWSCost: 5,988 Bytes: 49,168,630 Cardinality: 121,105
5 NESTED LOOPS Cost: 2 Bytes: 41 Cardinality: 1
2 TABLE ACCESS BY INDEX ROWID TABLE <<TableNameHidden>> Cost: 1 Bytes: 21 Cardinality: 1
1 INDEX UNIQUE SCAN INDEX (UNIQUE) <<IndexNameHIdden>> Cost: 0 Cardinality: 1
4 TABLE ACCESS BY INDEX ROWID TABLE <<TableNameHidden>> Cost: 1 Bytes: 20 Cardinality: 1
3 INDEX UNIQUE SCAN INDEX (UNIQUE) <<IndexNameHIdden>> Cost: 0 Cardinality: 1
10 NESTED LOOPS Cost: 2 Bytes: 41 Cardinality: 1
7 TABLE ACCESS BY INDEX ROWID TABLE <<TableNameHidden>> Cost: 1 Bytes: 21 Cardinality: 1
6 INDEX UNIQUE SCAN INDEX (UNIQUE) <<IndexNameHIdden>> Cost: 0 Cardinality: 1
9 TABLE ACCESS BY INDEX ROWID TABLE <<TableNameHidden>> Cost: 1 Bytes: 20 Cardinality: 1
8 INDEX UNIQUE SCAN INDEX (UNIQUE) <<IndexNameHIdden>> Cost: 0 Cardinality: 1
28 HASH JOIN Cost: 5,988 Bytes: 49,168,630 Cardinality: 121,105 <<<<<<---------------------------------
17 VIEW VIEW SYS.VW_NSO_3 Cost: 18 Bytes: 38,724 Cardinality: 922
16 HASH UNIQUE Cost: 18 Bytes: 25,816 Cardinality: 922
15 UNION-ALL
12 TABLE ACCESS BY INDEX ROWID BATCHED TABLE table1 Cost: 9 Bytes: 7,837 Cardinality: 461
11 INDEX RANGE SCAN INDEX table1index1 Cost: 4 Cardinality: 461
14 TABLE ACCESS BY INDEX ROWID BATCHED TABLE table1 Cost: 9 Bytes: 5,071 Cardinality: 461
13 INDEX RANGE SCAN INDEX table1index1 Cost: 4 Cardinality: 461
27 VIEW REPORTING. Cost: 5,970 Bytes: 4,781,140 Cardinality: 13,135
26 WINDOW SORT PUSHED RANK Cost: 5,970 Bytes: 1,169,015 Cardinality: 13,135
25 HASH JOIN Cost: 5,666 Bytes: 1,169,015 Cardinality: 13,135
23 NESTED LOOPS Cost: 5,666 Bytes: 1,169,015 Cardinality: 13,135
21 NESTED LOOPS
19 STATISTICS COLLECTOR
18 TABLE ACCESS FULL TABLE viewTable2 Cost: 1,266 Bytes: 932,585 Cardinality: 13,135
20 INDEX UNIQUE SCAN INDEX (UNIQUE) viewTable1_PK
22 TABLE ACCESS BY INDEX ROWID TABLE viewTable1 Cost: 4,400 Bytes: 18 Cardinality: 1
24 TABLE ACCESS FULL TABLE viewTable1 Cost: 4,400 Bytes: 950,256 Cardinality: 52,792
In the second plan the query is using a "HASH JOIN", which leads to a turn around time of less than 3 secs. In the original query with bind variable the query uses the filter clause which keeps running for minutes.
What can I do so that I need not change the query but it still takes the 2nd plan (with hash join)?
March 30, 2021 - 12:31 pm UTC
Not that I'm aware of. Look into SQL plan management (baselines) and SQL profiles to help you get the plan you want.