Oracle PLSQL stored procedure performance optimization method

Oracle PLSQL stored procedure performance optimization method

1. Common causes affecting performance

1. Try to use stored procedures and avoid using PL/SQL anonymous blocks

After the stored procedure is created, Oracle will perform syntactic analysis on it and store it in the database in compiled form. When the client calls it, it only needs to send a calling instruction, which avoids the anonymous block from transmitting a large amount of source code on the Internet and reduces the network cost. The communication burden is reduced, and the performance of the program is improved because it is only compiled once when it is created.

2. Write shared SQL statements

When Oracle executes a SQL statement, after the first parsing, it will place the SQL statement in the shared pool located in the system global area SGA. This memory area can be shared by all database users, so when executing a SQL statement For example, when a cursor in a PL/SQL statement executes a SQL statement, if Oracle detects that it is the same as a statement that has been run before, it will use the parsed statement and use the optimal execution path.

When Oracle executes a SQL statement, it will always first search for the same SQL statement in the shared memory area. However, because Oracle only caches simple tables, it is not suitable for multi-table connection queries.

SELECT * FROM EMP;

SELECT * from EMP;

Select * from Emp;

SELECT * FROM EMP;

In order to avoid this type of SQL statement, when writing SQL statements, you must pay attention to using consistent case conventions, keywords, reserved words are in uppercase, and user-declared identifiers are in lowercase. By designing your own writing conventions and abiding by these conventions, you can process The statements are consistent with those in the shared pool, which helps improve running performance.

3. Use BINARY_INTEGER and PLS_INTEGER to declare integers

When declaring variable types in PLSQL programming, you should always use BINARY_INTEGER and PLS_INTEGER to avoid relying too much on the number type, because the former provides faster performance.

4. Use NOCOPY compilation prompt when passing big data parameters in the process

When creating a procedure or function, IN mode always passes a pointer, while OUT and IN OUT pass a copy of the value, also known as pass by value. When large-capacity parameter passing is involved, performance will be seriously reduced. At this time, you should consider using the NOCOPY compilation hint to pass parameters by reference. The larger the size of the parameter, the more obvious the effect. For example, assume that a procedure has an IN OUT
type The parameters are passed by value by default. The following example demonstrates calling this process multiple times and passing a large index table parameter. If NOCOPY is not used, performance will be seriously reduced.

Use NOCOPY to improve performance

declare
type test_tb1 is table of pls_integer index by pls_integer;  --定义索引表类型
test_tb1 test_tb1_type;   --定义索引表类型的变量
--定义内嵌子程序,在IN OUT 参数中使用NOCOPY提示来按引用传递
procedure test (arg_cnt in pls_integer,arg_tb1 in out nocopy test_tb1_type)
is
begin
for cnt_test in test_tb1.first .. arg_tb1.last --依循环索引表
loop
arg_tb1 (cnt_test):=arg_tb1 (cnt_test) + arg_cnt;
end loop;	--为形式参数表赋值
end;
begin
for cnt in 0 .. 10000
loop
test_tb1 (cnt) := cnt;
end loop;
for cnt in 0 .. 10000
loop
test (cnt,test_tb1);
end loop;
end;

5. Use returning to get the return value

When using DML statements to process object row data, if you want to obtain the return value of the row, you should always use the returning clause to reduce the number of SQL executions and improve execution efficiency:

insert into … value (…) returning col1 into :col1;
update … set … returning col1 into :col1;
delete … returning col1 into :col1;
Using returning, you can not only return multi-column data, but also return data and save it to arrays and other data types. Medium:
returning col1, col2 into :col1, :col2;
returning col1 into :col1_array;

6. Avoid using dynamic SQL statements

Although dynamic SQL statements provide programming convenience, excessive use of dynamic SQL statements will seriously reduce the performance of PLSQL applications. Therefore, if necessary, you should always consider using static SQL statements. If you have to use dynamic sql statements, you should always choose to use local dynamic sql statements, that is, execute immediate or open for instead of using dbms_sql, because dbms_sql is not only more complicated to write code, but also has poorer performance than local dynamic sql statements.

7. Try to use bulk batch processing

If the operation involves a large amount of data, you can improve performance by processing a large amount of data at one time. For example, you can put the data in index tables, nested tables, and variable-length arrays, and use batch statements such as forall or bulk collect into , processing large amounts of data at one time and improving performance.

Use the bulk collect into statement to insert all the data in the emp table into the index table variable at one time, which can significantly improve performance when the amount of data is particularly large

Get all data at once using batch processing

declare
type emp_tb1 is table of emp%ROWTYPE index by pls_integer; --定义索引表类型
        emp_tb1	emp_tb1_type;
cursor	emp_cur
is
select * from emp;
begin
open emp_cur;
fetch emp_cur
bulk collect into emp_tb1;
close emp_cur;
end;

By using the bulk collect into clause, all cursor data is extracted into index table variables at one time, which improves the execution performance of the program and saves the amount of code writing. Therefore, whenever possible, batch processing should be used to complete data processing.

2. Use the DBMS_PROFILER package

1.Install the DBMS_PROFILER package

Before using DBMS_PROFILER, you must enter the database system as an administrator to install it.
conn system/manager as sysdba;
desc dbms_profiler;
If the desc command prompts that the dbms_profiler package does not exist, you need to use the following command to install it:
sql>@?/rdbms/admin/profload.sql
Run desc dbms_profiler again, you can see the package Contains subroutine information, there are two main functions used:
start_profiler starts the profiler
stop_profiler stops the profile

2. Configure the profiler solution

Create a user to store tracking information, and synonyms for profiler-related tables:

create user profiler indentified by 123456;
grant connect,resource to profiler;
create public synonym plsql_profiler_runs for profiler.plsql_profiler_runs;
create public synonym plsql_profiler_units for profiler.plsql_profiler_units;
create public synonym plsql_profiler_data for profiler.plsql_profiler_data;
create public synonym plsql_profiler_runnumber for profiler.plsql_profiler_runnumber;

3. Configure the profiler table

conn profiler/123456

@?/rdbms/admin/proftab.sql

grant select on plsql_profile_runnumber to public;
grant select, insert, update, delete on plsql_profiler_data to public;
grant select, insert, update, delete on plsql_profiler_units to public; grant select, insert, update, delete on plsql_profiler_runs to public_profiler_runs
to public; plsqlber
The running information
plsql_profiler_data saves the profiler information of each unit
plsql_profiler_units saves the detailed data of each unit
plsql_profiler_runs is used to generate the sequence of profiler unique running numbers

4. Execute profiler to obtain configuration information

After creating the process, you can use profiler to instrument the program code.
Create the procedure to be tested

create table pro_tst_table (a int);
create or replace procedure sp_test
as
begin
for i in 1 .. 10000
loop
insert into pro_tst_table values(i);
end loop;
commit;
end;

Use dbms_profiler to test packages

declare
v_run_number	integer;
v_temp1	integer;
begin
--启动profiler
sys.DBMS_PROFILER.start_profiler(run_number => v_run_number);
--显示当前跟踪的运行序号(后面查询要用)
dbms_output.put_line('run_number:'||v_run_number);
--运行要跟踪的PLSQL
sp_test;
--停止profiler
sys.DBMS_PROFILER.stop_profiler;
end;

5. Query profiler to get results

To use SQL statements to query the information of this execution, you can first query plsql_profiler_runs to obtain the basic information of this execution:
select runid, run_owner, run_date, run_total_time from plsql_profiler_runs;
The author ran the code twice, so there are two runid records, ID The value is generated through the serial number, and the maximum ID value indicates the most recent execution.
RUN_TOTAL_TIME indicates the execution time. You can see that the time of the two executions is significantly different. The
unit information of this profile can be obtained by querying the plsql_profiler_units table.
By querying the plsql_profiler_data table, you can obtain statistical information for each row of the executed stored procedure based on the row number and unit number.

3. Use the DBMS_TRACE package

For example, if you see the execution order of subroutines, you can use the DBMS_TRACE package. The use of this package is similar to using DBMS_PROFILER. One major difference is that dbms_trace can set the events that need to be tracked: calls, exceptions, SQL and even the possible events of each PLSQL code. run. With the assistance of this information, abnormalities in the background program process can be located very quickly.

There are two functions in the package:

set_plsql_trace: Enables the collection of tracing statistics.

clear_plsql_trace: Stops the collection of trace statistics

1. Configure and use DBMS_TRACE

Before using it, you need to configure the data tables used by dbms_trace and enable all users to write data to these tables.

conn system/manager as sysdba

@?/rdbms/admin/tracetab.sql

The script creates two tables and a sequence

plsql_trace_runs table: used to record each trace information.

plsql_trace_events table: used to record detailed data of all traces

plsql_trace_runnumber sequence: Sequence used to generate unique run numbers.

2. After successfully creating the required tables, you need to create synonyms for the corresponding tables and assign accessible permissions to them so that users with the public role can operate on the corresponding tables.

create or replace public synonym plsql_trace_runs for sys.plsql_trace_runs;

create or replace public synonym plsql_trace_events for sys.plsql_trace_events;

create or replace public synonym plsql_trace_runnumber for sys.plsql_trace_runnumber;

grant select,insert,update,delete on plsql_trace_events to public;

grant select,insert,update,delete on plsql_trace_runs to public;

grant select on plsql_trace_runnumber to public;

Create dbms_trace test program

create or replace procedure do_something (p_times in number)
as
1_dummy	number;
begin
for i in 1 .. p_times
loop
select 1_dummy + 1 into 1_dummy from dual;
end loop;
end;

Using dbms_trace tracing program

declare
1_result binary_integer;
begin
--跟踪所有的调用
dbms_trace.set_plsql_trace(dbms_trace.trace_all_calls);
do_something(100);
--停止PLSQL跟踪
dbms_trace.clear_plsql_trace;
--跟踪所有的sql语句
dbms_trace.set_plsql_trace(dbms_trace.trace_all_sql);
do_something(100);
--停止跟踪
dbms_trace.clear_plsql_trace;
--跟踪所有行数据
dbms_trace.set_plsql_trace(dbms_trace.trace_all_lines);
do_something(100);
dbms_trace.clear_plsql_trace;
end;

For each call, set_plsql_trace is first used to start the tracing process. The parameter DBMS_TRACE_trace_all_calls of this process is a constant specified to trace the call or return value.

The package specification of the DBMS_TRACE package contains a list of constants available for set_plsql_trace and a detailed explanation.

Obtain each trace information by querying the plsql_trace_runs table.

4. PLSQL performance optimization skills

When the Oracle database executes a SQL statement, Oracle's optimizer will determine the execution path of the SQL statement according to certain rules to ensure that the SQL statement can be executed with optimal performance. In order to execute the SQL statement in the Oracle database system, Oracle may need to implement multiple Each of these steps may be physically retrieving rows of data from the database, or preparing the rows of data in some way for use by the user writing the SQL statement. The combination of these steps used by Oracle to execute the statement is called execution. plan.

Oracle goes through 4 steps when executing a SQL statement:

Parse SQL statements: mainly query the same SQL statements in the shared pool to check security and SQL syntax and semantics.

Creation of execution plans and execution: including creation of execution plans for SQL statements and actual acquisition of table data.

Display the result set: Performs all necessary sorting, conversion, and reformatting of field data.

Convert field data: Reformat and convert fields that have been converted through built-in functions.

Check the execution plan of the sql statement. For example, some third-party tools need to execute the utlxplan.sql script first to create the explain_plan table.

@?/rdbms/admin/utlxplan.sql

set autotrace on explain: execute sql and only display the execution plan

set autotrace on statistics: execute sql and only display execution statistics

set autotrace on: execute sql, and display execution plan and statistical information, no execution results

set autotrace traceonly: only displays the execution plan and statistical information, no execution results

set autotrace off: Turn off trace display plan and statistics

set autotrace on explain

col ename format a20;

select empno,ename from emp where empno=7369;

5. Understand the execution plan

1. Full table scan: This method will read every record in the table and read each data block sequentially until the end mark. For a large data table, using a full table scan will reduce performance, but some Sometimes, for example, when the proportion of query results to the data volume of the entire table is relatively high, full table scanning is a better method than index selection.

2. Obtain through ROWID value: The rowid of the row points out the data file where the row is located, the data block and the position of the row in the block. Therefore, accessing data through rowid can quickly locate the target data, which is a single row accessed by Oracle. The fastest way to access data.

3. Index scan: First find the rowid value of the object through the index, and then find the specific data directly from the table through the rowid value, which can greatly improve the search efficiency.

6. Table order for join query

By default, the optimizer will use the all_rows optimization method, which is the cost-based optimizer CBO to generate an execution plan. The CBO method will generate an execution plan based on statistical information.

Statistical information gives information such as the size of the table, how many rows, and the length of each row. These statistical information are not available in the library at first, but are discovered after doing analyee. Many times, expired statistical information will cause the optimizer to make an error. execution plan, so this information should be updated in a timely manner.

In CBO mode, when performing a join query on multiple tables, the Oracle analyzer will process the table names in the from clause in order from right to left. For example:

select a.empno,a.ename,c.deptno,c.dname,a.log_action from emp_log a,emp b,dept c

During execution, oracle will first query the dept table, and use the rows queried in the dept table as the data source to serially connect the emp table to continue execution, so the dept table is also called the basic table or the driving table. Because the order of connections has a great impact on query efficiency. Therefore, when processing multi-table connections, you must choose a table with fewer records as the base table, and Oracle will use sorting and merging to connect. For example, first scan the dept table, then sort the dept table, then scan the emp table, and finally merge all the retrieved records with the records in the first table.

If there are more than 3 tables connected to the query, you need to select the crosstab as the base table. The crosstab refers to the table that is referenced by other tables. Since emp_log is a crosstab between the dept and emp tables, it contains the contents of both dept and emp.

select a.empno,a.ename,c.deptno,c.dname,a.log_action from emp b,dept c,emp_log a;

7. Specify the sequence of where conditions

When querying a table, the order of conditions in the where clause often affects execution performance. By default, oracle parses where clauses in a bottom-up order, so when processing multi-table queries, the connection between tables must be written before other where conditions, but the conditions for filtering data records must be written in where at the end of the clause so that the connection can be processed after filtering the data, which can improve the performance of the SQL statement.

Avoid using * symbol

8. Use the decode function

For example, to count the number and salary summary of employees with department number 20 and department number 30 in the emp table, if you do not use decode, you must use two SQL statements.

select count(),SUM(sal) from emp where deptno=20;
union
select count(
),SUM(sal) from emp where deptno=30;

Two full table scans were performed above

Through the decode statement, the same results can be obtained in one SQL query, and the two rows of results are displayed as a single row.

select count (decode (deptno,20,‘X’,NULL)) dept20_count,
count (decode (deptno,30,‘X’,NULL)) dept30_count,
SUM (decode (deptno,20,sal,NULL)) dept20_sal,
sum (decode (deptno,30,sal,NULL)) dept30_sal from emp;

Only one full table scan was performed above

By using the decode function flexibly, you can get many unexpected results, such as using the decode function in the group by or order by clause, or nesting another decode block in the decode block.

9. Use where instead of having

Both the where clause and the having clause can filter data, but the where clause cannot use aggregate functions, such as count max min avg sum and other functions.

example:

select empno,deptno,sum(sal) from emp group by empno,deptno
having sum(sal) > 1000 and deptno in (20,30);

In the having clause, the records with department numbers 20 or 30 are filtered out. In fact, this will cause the query to retrieve the employee records of all departments, perform grouping calculations, and finally filter out the records of departments 20 and 30 based on the grouping results. This is very inefficient. A good algorithm is to first use the where clause to retrieve the records with department numbers 20 and 30, and then filter them.

select empno,deptno,sum(sal) from emp where deptno in (20,30)
group by empno,deptno having sum (sal) > 1000;

10. Use UNION instead of OR

If the two columns to be ORed are both index columns, you can consider using union to improve performance.

Example: For example, in the emp table, both empno and ename have created index columns. When you need to perform an OR operation query between empno and ename, you can consider changing these two queries to union to improve performance.

select empno,ename,job,sal from emp where empno > 7500 OR ename LIKE ‘S%’;

11. Use UNION

select empno,ename,job,sal from emp where empno > 7500
UNION
select empno,ename,job,sal from emp where ename LIKE ‘S%’;

But this method ensures that both columns are index columns.

If you insist on using the OR statement, you need to remember to write the index column with the fewest returned records at the front as much as possible, so as to achieve better performance. For example, empno > 7500 returns fewer records than the query for ename, so in the OR statement Putting it in the front will give you better performance. Another suggestion is to consider using IN instead when performing OR calculations on single field values.

For example the following

select empno,ename,job,sal from emp where deptno=20 OR deptno=30;

12. Use exists instead of IN

For example, to query the list of all employees located in Chicago, you can consider using IN

select * from emp where deptno IN (
select deptno from dept where loc=‘CHICAGO’);

Replace with

select * from emp where exists (
select deptno from dept where loc=‘CHICAGO’);

The same replacement page occurs between not in and not exists. The not in clause will perform an internal sort and merge. In fact, it performs a full table scan of the table in the subquery, so the efficiency is low. When you need to use In the case of NOT IN, Yingai always considers changing it to an external connection or NOT EXISTS.

select * from emp where deptno NOT IN (
select deptno from dept where loc=‘CHICAGO’);

In order to improve better performance, you can use join query

select a.* from emp a,dept b where a.deptno=b.deptno and b.loc <> ‘CHICAGO’;

most efficient

select a.* from emp a where NOT EXISTS (
select 1 from dept b where a.deptno =b.deptno and loc=‘CHICAGO’);

13. Avoid inefficient PL/SQL process control statements

PLSQL uses the short path calculation method when processing logical expression values.

declare
v_sal	number:=&sal;
v_job	varchar2(20):=&job;
begin
if (v_sal > 5000 ) OR (v_job = '销售')
then
dbms_output.put_line('符合匹配的OR条件');
end if;
end;

First judge the first condition. If v_sal is greater than 5000, the v_job condition will not be judged again. Flexible use of this short-circuit calculation method can improve performance. The judgment statement with lower overhead should always be placed first, so that when the previous judgment fails, the later statement with higher overhead will not be executed, which can improve the performance of PL/SQL applications.

For example, for the and logical operator, the result is true only if the operations on the left and right sides are true. If the first operation of the previous result is false, the second operation will not be performed.

declare
v_sal	number:=&sal;
v_job	varchar2(20):=&job;
begin
if (check_sal(v_sal) > 5000) AND (v_job = '销售') --判断执行条件
then
dbms_output.put_line('符合匹配的and条件');
end if;
end;

This code has a performance hazard. check_sal involves some business logic checks. If the check_sal function is called first, this function will always be called. Therefore, for performance considerations, the judgment of v_job should always be placed in and before the statement.

declare
v_sal	number:=&sal;
v_job	varchar2(20):=&job;
begin
if (v_job='销售') and (check_sal(v_sal) > 5000)
then
dbms_output.put_line('符合匹配的and条件');
end if;
end;

Avoid implicit type conversions

Guess you like

Origin blog.csdn.net/qq_38696286/article/details/119213244