0% found this document useful (0 votes)
5 views

sql profile tuning-auto and manual

The document outlines a comprehensive SQL script for creating tables, inserting data, and tuning SQL queries for performance. It includes steps for creating a SQL tuning task, gathering statistics, executing the tuning task, and accepting SQL profiles to optimize query performance. Additionally, it provides guidance on manual tuning techniques and verifying table statistics.

Uploaded by

D Pavan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

sql profile tuning-auto and manual

The document outlines a comprehensive SQL script for creating tables, inserting data, and tuning SQL queries for performance. It includes steps for creating a SQL tuning task, gathering statistics, executing the tuning task, and accepting SQL profiles to optimize query performance. Additionally, it provides guidance on manual tuning techniques and verifying table statistics.

Uploaded by

D Pavan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

Full SQL Script: From Setup to SQL Profile Tuning

- Create Tables and Insert Data

DROP TABLE t1 PURGE;


DROP TABLE t2 PURGE;

CREATE TABLE t1 (c1 NUMBER, c2 VARCHAR2(15), c3 VARCHAR2(15), c4


VARCHAR2(15), c5 VARCHAR2(15));
CREATE TABLE t2 (c1 NUMBER, c2 VARCHAR2(15), c3 VARCHAR2(15), c4
VARCHAR2(15), c5 VARCHAR2(15));

BEGIN
FOR i IN 1..70000 LOOP
INSERT INTO t1 VALUES(i, 'AAAAAAAAAA', 'BBBBBBBB', 'CCCCCCCCCC',
'DDDDDDDDDD');
INSERT INTO t2 VALUES(i, 'AAAAAAAAAA', 'BBBBBBBB', 'CCCCCCCCCC',
'DDDDDDDDDD');
END LOOP;
COMMIT;
END;
/

- Run a Bad Plan with a Hint (to simulate performance issue)

SET TIMING ON;

SELECT /*+ USE_NL(t1 t2) */ COUNT(*)

FROM t1

JOIN t2 ON t1.c1 = t2.c1;

- Create SQL Tuning Task for this Query


DECLARE
l_task_name VARCHAR2(30);
BEGIN
l_task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK(
sql_text => 'SELECT /*+ USE_NL(t1 t2) */ COUNT(*) FROM t1 JOIN t2 ON
t1.c1 = t2.c1',
user_name => USER,
scope => 'COMPREHENSIVE',
time_limit => 60,
task_name => 't1_t2_tuning_task',
description => 'Tuning JOIN of t1 and t2');
END;
/

- Execute Tuning Task

BEGIN
DBMS_SQLTUNE.EXECUTE_TUNING_TASK('t1_t2_tuning_task');
END;
/

- View Recommendations

SET LONG 10000

SET PAGESIZE 500

SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK('t1_t2_tuning_task') AS report


FROM DUAL;

- Note: if we do not get stats in above report then we need to gather the stats first for both table

BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'SYS',
tabname => 'T1',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE AUTO'
);
END;
/

BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'SYS',
tabname => 'T2',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE AUTO'
);
END;
/

- Execute Tuning Task again


BEGIN
DBMS_SQLTUNE.EXECUTE_TUNING_TASK('t1_t2_tuning_task');
END;
/

- Now, run again below query to generate profile report

SET LONG 10000

SET PAGESIZE 500

SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK('t1_t2_tuning_task') AS report


FROM DUAL;

- Still if not able to see stats then first check if stats is available or not

SELECT table_name, last_analyzed


FROM dba_tables
WHERE table_name IN ('T1', 'T2') AND owner = 'SYS';

- Then Delete old tuning task (optional but clean approach)

EXEC DBMS_SQLTUNE.DROP_TUNING_TASK('t1_t2_tuning_task');

- Create and run a new tuning task

DECLARE
l_task_name VARCHAR2(30);
BEGIN
l_task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK(
sql_text => 'SELECT /*+ USE_NL(t1 t2) */ COUNT(*) FROM t1 JOIN t2 ON
t1.c1 = t2.c1',
user_name => 'SYS',
scope => DBMS_SQLTUNE.SCOPE_COMPREHENSIVE,
time_limit => 60,
task_name => 't1_t2_tuning_task',
description => 'Tuning task after stats gather for T1 and T2'
);

BEGIN
DBMS_SQLTUNE.EXECUTE_TUNING_TASK(task_name => 't1_t2_tuning_task');
END;
/

- Get the new report

SET LONG 10000


SET PAGESIZE 500
SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK('t1_t2_tuning_task') AS report
FROM DUAL;

- Now we able to see report with updated stats as below


- Accept the Recommended SQL Profile
- Identify the rec_id of the SQL Profile
▪ This will return one or more rec_ids and their benefit %. You'll use this in the
next step.

SELECT rec_id, benefit, type, message


FROM dba_advisor_recommendations
WHERE task_name = 't1_t2_tuning_task'
AND task_owner = 'SYS'
AND type = 'SQL Profile';
- Accept SQL Profile Using the rec_id
▪ Once you know the correct rec_id (pick the one with the highest benefit), run
this:

BEGIN
DBMS_SQLTUNE.ACCEPT_SQL_PROFILE(
task_name => 't1_t2_tuning_task',
task_owner => 'SYS',
rec_id => <put_rec_id_here>,
replace => TRUE
);
END;
/

- After successfully accepting it, verify it’s now active:


▪ You should see your new SQL profile here.

SELECT name, status, created, category


FROM dba_sql_profiles
WHERE sql_text LIKE '%COUNT(*) FROM t1 JOIN t2%';

- Run your original query without the hint to confirm the SQL profile is kicking in:
▪ It should now be very fast, using a hash join, thanks to the SQL profile.

SELECT COUNT(*)
FROM t1
JOIN t2 ON t1.c1 = t2.c1;

Note- if you are a normal user and want to do tuning task at your end then you should have ADVISOR and
SELECT_CATALOG_ROLE to a particular user

- Command
GRANT ADVISOR TO your_user;
GRANT SELECT_CATALOG_ROLE TO your_user;

- You can auto tuning only when you have sql tuning is installled in your oracle version
- Command to check

SELECT parameter, value FROM v$option ORDER BY parameter;

Manually tuning

Gather fresh optimizer statistics on your tables:


BEGIN

DBMS_STATS.GATHER_TABLE_STATS('SYS', 'T1', cascade => TRUE);

DBMS_STATS.GATHER_TABLE_STATS('SYS', 'T2', cascade => TRUE);

END;

Analyze explain plans manually:

EXPLAIN PLAN FOR

SELECT /*+ USE_NL(t1 t2) */ COUNT(*)

FROM t1 JOIN t2 ON t1.c1 = t2.c1;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

- What to do?

CREATE INDEX idx_t1_c1 ON t1(c1);


CREATE INDEX idx_t2_c1 ON t2(c1);
COMMIT;

- Try forcing a hash join, which is generally better for large table joins:

SELECT /*+ USE_HASH(t1 t2) */ COUNT(*)

FROM t1
JOIN t2 ON t1.c1 = t2.c1;

- Explain plan of the hash join query:

EXPLAIN PLAN FOR

SELECT /*+ USE_HASH(t1 t2) */ COUNT(*)

FROM t1

JOIN t2 ON t1.c1 = t2.c1;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

Verify stats on tables

SET PAGESIZE 50

SET LINESIZE 100

COLUMN table_name FORMAT A15 HEADING 'TABLE NAME'

COLUMN num_rows FORMAT 9999999 HEADING 'NUM ROWS'


COLUMN blocks FORMAT 999999 HEADING 'BLOCKS'

COLUMN empty_blocks FORMAT 999999 HEADING 'EMPTY BLOCKS'

SELECT table_name, num_rows, blocks, empty_blocks

FROM user_tables

WHERE table_name IN ('T1', 'T2');

You might also like