Anda di halaman 1dari 10

Performance Implication Of Functions In SQL

Functions
It is common for any programming languages to abstract similar contents/code blocks into reusable functions, to make applications easy to maintain. Functions are also used extensively in Oracle, for example, built in functions like to_date, substr, etc. Some Oracle functions are deterministic: the same inputs always result in the same output. Some Oracle functions may depend on other factors, for example, a random number generator, or a table with content constantly updated. The results from a deterministic function will be cached, but the results from a non-deterministic function will not be cached, meaning Oracle has to evaluate the function every time it is used. A not well-written function in a large query could have significant performance impact.

Example
hlpat: hotlist-sp1.corp.sp1.yahoo.com SQL_ID: 4mjnbvjjsjy61
select SPACEID, get_apt_attrs_as_varchar2_q(spaceid) as APT_ATTR_STR from yan_resolved_class_node_query

function get_apt_attrs_as_varchar2_q (p_spaceid in number) return varchar2 IS l_data varchar2(32767); BEGIN select apt_attr_str into l_data from yan_resolved_class_node_query where spaceid = p_spaceid; return substr (l_data, 1, 32767); END get_apt_attrs_as_varchar2_q;

SQL Stats
4 executions, started at 2012-11-01 13:15:52, ended at 2012-11-05 09:04:23. Three aborted, one succeeded. Elapsed time: 325,260 seconds. CPU time: 317,488 seconds. IO Wait Time: 160,440 seconds. DISK_READS (PIO): 3,449,182,357 blocks. BUFFER_GETS (LIO): 3,534,546,432 blocks Rows: 493,657

The Execution Plan

1. A very simple plan. FTS to read every block and row. 2. Dynamic sampling level 2 is used, since the table has no statistics. 3. Since no additional predicate, so the Rows in the plan should be the accurate estimate of the table row count: 521K (note the query SQL stats returned 493,657 rows). 4. The table size is 50,897 blocks as of 2012-11-02, found from v$session_longops, not statistics. dba_segments can be used for estimation.

SQL and Plan Monitor Stats


v$sql_monitor did show large DISK_READS (1,582,626,608, requests). v$sql_plan_monitor only had 3179 read requests with 392MB. The table size can be found from v$session_longops (only one round of record left, others were purged).

Where Was The High PIO Spent?


We know there is a function involved. The main query didnt show high PIO. Both v$sql and v$sql_monitor recorded very high PIO. So we need find out the SQL stats for any statistics related to the function.
During real time session tracking, the recursive SQL_ID from the function sometime is visible from v$session for the session related to the main query. v$open_cursor can also be used to check if any open cursor has similar text as the SQLs inside the function definition. Querying v$sql_monitor based on the sid of the main query could expose multiple SQL_IDs. Check the SQL texts of those SQL_IDs and compare with the function definition. Using keyword form the SQL inside the function to search v$sql (or v$sqlstats, v$sqlarea) sql_text. After identify SQL_ID of a candidate, using v$sql, column program_id, as object_id to check dba_objects to see if the name is the concerned function name. All above methods are supported by DBA Performance Analyzer (Framework).

The SQL_ID of the recursive SQL related to the function is visible from v$session. V$sql_monitor for the given session has a huge number of entries for this recursive SQL based on the SID of the main query and it is hard to find the main query v$sql_monitor info because of the long list.

Recursive SQL Info


The follow info was taken at 2012-11-02, two days before the query completed. The recursive SQL uses full table scan on the same table because no index available on column SPACEID. So the total IO cost could be up to (table row count) * (table blocks), minus cache.

The Alternatives
Without rewriting the main query: add index on column spaceid to reduce IO (PIO and LIO) for the function. Rewriting the main query by using join (a lot of function usages can be eliminated using join). In some cases, if the function input has few distinct values (say less than subquery cache slot size), replace the function with a subquery.