Showing posts with label Performance Tuning. Show all posts
Showing posts with label Performance Tuning. Show all posts

Saturday, 17 October 2020

Join Methods


Nested Loop Join

 

            /* Nested loop join example */

            SELECT * FROM employees e JOIN departments d

            ON d.department_id = e.department_id

            WHERE d.department_id = 60;

           

            /* Even if we change the join order and on clause order, the plan did not change */

            SELECT * FROM departments d JOIN employees e

            ON e.department_id = d.department_id

            WHERE d.department_id = 60;

             

            /* We can use leading hint to change the driving table */

            SELECT /*+ leading(e) */ * FROM employees e JOIN departments d

            ON d.department_id = e.department_id

            WHERE d.department_id = 60;

             

            /* Does not use nested loop without hint */

            SELECT * FROM employees e JOIN departments d

            ON d.department_id = e.department_id;

             

            /* Using nested loop hint */

            SELECT /*+ use_nl(d e) */ * FROM employees e JOIN departments d

            ON d.department_id = e.department_id;

             

            /* Nested loop prefetching and double nested loops example */

            SELECT e.employee_id,e.last_name,d.department_id,d.department_name

            FROM employees e JOIN departments d

            ON d.department_id = e.department_id

            WHERE d.department_name LIKE 'A%';

 

Sort Merge Join

 

            /* Sort Merge Join example */

            SELECT * FROM employees e JOIN departments d

            ON d.department_id = e.department_id

            WHERE e.last_name like 'K%';

           

            /* Force it to use Nested Loop Join */

            SELECT /*+ use_nl(e d) */* FROM employees e JOIN departments d

            ON d.department_id = e.department_id

            WHERE e.last_name like 'K%';

             

            /* Another Sort Merge Join example */

            SELECT * FROM employees e JOIN departments d

            ON d.department_id = e.department_id

            WHERE d.manager_id > 110;

             

            /* Equality Operator prevented Sort Merge Join */

            SELECT * FROM employees e JOIN departments d

            ON d.department_id = e.department_id

            WHERE d.manager_id = 110;

             

            /* Using Sort Merge Join Hint*/

            SELECT /*+ use_merge(e d) */* FROM employees e JOIN departments d

            ON d.department_id = e.department_id

            WHERE d.manager_id = 110;

 

Hash Joins

 

            GRANT select_catalog_role TO hr;

            GRANT SELECT ANY DICTIONARY TO hr;

           

            SELECT * FROM employees e, departments d

            WHERE d.department_id = e.department_id

            AND d.manager_id = 110;

           

            SELECT /*+ use_hash(d e) */ * FROM employees e, departments d

            WHERE d.department_id = e.department_id

            AND d.manager_id = 110;

 

 

Thursday, 15 October 2020

Access Path

 Full Table Scan:
-------------------------------------------------
select * from sales;
select * from sales where amount_sold > 1770;
select * from employees where employee_id > 100;
 
explain plan for
select * from employees e, departments d
where e.employee_id = d.manager_id;
select * from table (dbms_xplan.display());
 
explain plan for
select * from employees e, departments d
where e.department_id = d.department_id;
select * from table (dbms_xplan.display());
 
Table Access by ROWID
-------------------------------------------------
 
select * from sales where prod_id = 116 and cust_id = 100090;
select * from sales where rowid = 'your_row_id';
create index prod_cust_ix on sales (prod_id,cust_id);
select prod_id,cust_id from sales where prod_id = 116;
drop index prod_cust_ix;
 
Index Range Scan
-------------------------------
It can be applied to both b-tree and bitmap index types
 
-- One side bounded searched
SELECT * FROM SALES WHERE time_id > to_date('01-NOV-01','DD-MON-RR');
 
-- Bounded by both sides
SELECT * FROM SALES WHERE time_id between to_date('01-NOV-00','DD-MON-RR') and to_date('05-NOV-00','DD-MON-RR');
 
-- B-Tree index range scan
SELECT * FROM employees where employee_id > 190;
 
-- Index range scan on Non-Unique Index
SELECT * FROM employees where department_id > 80;
 
-- Order by with the indexed column -  sort is processed
SELECT * FROM employees where employee_id > 190 order by email;
 
-- Order by with the indexed column - no sort is processed
SELECT * FROM employees where employee_id > 190 order by employee_id;
 
-- Index range scan descending
SELECT * FROM employees where department_id > 80 order by department_id desc;
 
-- Index range scan with wildcard
SELECT * FROM PRODUCTS WHERE PROD_SUBCATEGORY LIKE 'Accessories%';
SELECT * FROM PRODUCTS WHERE PROD_SUBCATEGORY LIKE '%Accessories';
SELECT * FROM PRODUCTS WHERE PROD_SUBCATEGORY LIKE '%Accessories%';
 
Index Full Scan
----------------------------------
/* Index usage with order by */
SELECT * FROM departments ORDER BY department_id;
 
/* Index usage with order by, one column of an index - causes index full scan*/
SELECT last_name,first_name FROM employees ORDER BY last_name;
 
/* Index usage with order by, one column of an index - causes unnecessary sort operation*/
SELECT last_name,first_name FROM employees ORDER BY first_name;
 
/* Index usage with order by, but with wrong order - causes unnecessary sort operation */
SELECT last_name,first_name FROM employees ORDER BY first_name,last_name;
 
/* Index usage with order by, with right order of the index - there is no unncessary sort */
SELECT last_name,first_name FROM employees ORDER BY last_name,first_name;
 
/* Index usage with order by, wit unindexed column - there is no unncessary sort */
SELECT last_name,first_name FROM employees ORDER BY last_name,salary;
 
/* Index usage order by - when use * , it performed full table scan */
SELECT * FROM employees ORDER BY last_name,first_name;
 
/* Index usage with group by - using a column with no index leads a full table scan */
SELECT salary,count(*) FROM employees e
WHERE salary IS NOT NULL
GROUP BY salary;
 
/* Index usage with group by - using indexed columns may lead to a index full scan */
SELECT department_id,count(*) FROM employees e
WHERE department_id IS NOT NULL
GROUP BY department_id;
 
/* Index usage with group by - using more columns than ONE index has may prevent index full scan */
SELECT department_id,manager_id,count(*) FROM employees e
WHERE department_id IS NOT NULL
GROUP BY department_id, manager_id;
 
/* Index usage with merge join */
SELECT e.employee_id, e.last_name, e.first_name, e.department_id,
       d.department_name
FROM   employees e, departments d
WHERE  e.department_id = d.department_id;
 
Index Fast Full Scan
-----------------------------------
Since index fast full scan reads the data from the index directly,
and doesn't need to go to the memory to get data, it is the fastest index usage
 
/* Index Fast Full Scan Usage - Adding a different column
    than index has will prevent the Index Fast Full Scan */
SELECT e.employee_id, d.department_id, e.first_name,
       d.department_name
FROM   employees e, departments d
WHERE  e.department_id = d.department_id;
 
/* If all the columns are in the index, it may perform
   an Index Fast Full Scan */
SELECT e.employee_id, d.department_id,
       d.department_name
FROM   employees e, departments d
WHERE  e.department_id = d.department_id;
 
/*Index Fast Full Scan can be applied to b-tree indexes, too
  Even if there is an order by here, it used IFF Scan */
SELECT prod_id from sales order by prod_id;
 
/* Optimizer thinks Index Full Scan is better here*/
SELECT time_id from sales order by time_id;
 
/* Optimizer uses inded Fast Full Scan*/
SELECT time_id from sales;
 
Index Skip Scan
--------------------------
 
/*Index skip scan usage with equality operator*/
SELECT * FROM employees WHERE first_name = 'Alex';
 
/* Index range scan occurs if we use the first column of the index */
SELECT * FROM employees WHERE last_name = 'King';
 
/* Using index skip scan with adding a new index */
SELECT * FROM employees WHERE salary BETWEEN 6000 AND 7000;
CREATE INDEX dept_sal_ix ON employees (department_id,salary);
DROP INDEX dept_sal_ix;
 
/* Using index skip scan with adding a new index
   This time the cost increases significantly */
ALTER INDEX customers_yob_bix invisible;
SELECT * FROM customers WHERE cust_year_of_birth BETWEEN 1989 AND 1990;
CREATE INDEX customers_gen_dob_ix ON customers (cust_gender,cust_year_of_birth);
DROP INDEX customers_gen_dob_ix;
ALTER INDEX customers_yob_bix visible;
 
 
Index Join Scan
-----------------------------
/* Index join scan with two indexes */
SELECT employee_id,email FROM employees;
 
/* Index join scan with two indexes, but with range scan included*/
SELECT last_name,email FROM employees WHERE last_name LIKE 'B%';
 
/* Index join scan is not performed when we add rowid to the select clause */
SELECT rowid,employee_id,email FROM employees;

Hints


/* A query without a hint. It performs a range scan*/

 

SELECT employee_id, last_name

  FROM employees e

  WHERE last_name LIKE 'A%';

 

/* Using a hint to command the optimizer to use FULL TABLE SCAN*/ 

 

SELECT /*+ FULL(e) */ employee_id, last_name

  FROM employees e

  WHERE last_name LIKE 'A%';

 

/* Using the hint with the table name as the parameter*/

 

SELECT /*+ FULL(employees) */ employee_id, last_name

  FROM employees

  WHERE last_name LIKE 'A%';

 

/* Using the hint with the table name while we aliased it*/ 

 

-- If a table has an alias, we cannot use the table name directly. We need to use the alias instead of table names. Otherwise it will not work. 

SELECT /*+ FULL(employees) */ employee_id, last_name

  FROM employees e

  WHERE last_name LIKE 'A%';

 

/* Using an unreasonable hint. The optimizer will not consider this hint */

 

SELECT /*+ INDEX(EMP_DEPARTMENT_IX) */ employee_id, last_name

  FROM employees e

  WHERE last_name LIKE 'A%';

 

/* Using multiple hints. But they aim for the same area. So unreasonable. Optimizer picked full table scan as the best choice */

 

SELECT /*+ INDEX(EMP_NAME_IX) FULL(e)  */ employee_id, last_name

  FROM employees e

  WHERE last_name LIKE 'A%';

 

/* When we change the order of the hints. But it did not change the Optimizer's decision*/

 

SELECT /*+ FULL(e) INDEX(EMP_NAME_IX)   */ employee_id, last_name

  FROM employees e

  WHERE last_name LIKE 'A%';

 

/* There is no hint. To see the execution plan to compare with the next one */ 

 

SELECT 

  e.department_id, d.department_name,

  MAX(salary), AVG(salary)

FROM employees e, departments d

WHERE e.department_id=e.department_id

GROUP BY e.department_id, d.department_name;

 

/* Using multiple hints to change the execution plan */

 

SELECT /*+ LEADING(e d)  INDEX(d DEPT_ID_PK) INDEX(e EMP_DEPARTMENT_IX)*/

  e.department_id, d.department_name,

  MAX(salary), AVG(salary)

FROM employees e, departments d

WHERE e.department_id=e.department_id

GROUP BY e.department_id, d.department_name;

 

/* Using hints when there are two access paths.*/ 

 

SELECT /*+ INDEX(EMP_DEPARTMENT_IX) */ employee_id, last_name

  FROM employees e

  WHERE last_name LIKE 'A%'

  and department_id > 120;

 

/* When we change the selectivity of last_name search, it did not consider our hint.*/

 

SELECT /*+ INDEX(EMP_DEPARTMENT_IX) */ employee_id, last_name

  FROM employees e

  WHERE last_name LIKE 'Al%'

  and department_id > 120;

 

/* Another example with multiple joins, groups etc. But with no hint*/

 

SELECT customers.cust_first_name, customers.cust_last_name,

  MAX(QUANTITY_SOLD), AVG(QUANTITY_SOLD)

FROM sales, customers

GROUP BY customers.cust_first_name, customers.cust_last_name;

WHERE sales.cust_id=customers.cust_id

GROUP BY customers.cust_first_name, customers.cust_last_name;

 

/* Performance increase when performing parallel execution hint*/

 

SELECT /*+ PARALLEL(4) */ customers.cust_first_name, customers.cust_last_name,

  MAX(QUANTITY_SOLD), AVG(QUANTITY_SOLD)

FROM sales, customers

WHERE sales.cust_id=customers.cust_id

Monday, 11 December 2017

SQL Query Result Cache


PL/SQL Function Cache here



The SQL query result cache enables explicit caching of query result sets and query fragments in database memory. A dedicated memory buffer stored in the shared pool can be used for storing and retrieving the cached results (SQL query result cache). The query results stored in this cache become invalid when data in the database objects being accessed by the query is modified.

Although the SQL query cache can be used for any query, good candidate statements are the ones that need to access a very high number of rows to return only a fraction of them. This is mostly the case for data warehousing applications.

In the graphic shown in the slide, if the first session executes a query, it retrieves the data from the database and then caches the result in the SQL query result cache. If a second session executes the exact same query, it retrieves the result directly from the cache instead of using the disks.

The query optimizer manages the result cache mechanism depending on the settings of the RESULT_CACHE_MODE parameter in the initialization parameter file.

You can use this parameter to determine whether or not the optimizer automatically sends the results of queries to the result cache. You can set the RESULT_CACHE_MODE parameter at the system, session, and table level. The possible parameter values are MANUAL, and FORCE:

When set to MANUAL (the default), you must specify, by using the RESULT_CACHE hint, that a particular result is to be stored in the cache. 

When set to FORCE, all results are stored in the cache




Managing the SQL Query Result Cache

You can alter various parameter settings in the initialization parameter file to manage the SQL query result cache of your database.

By default, the database allocates memory for the result cache in the shared pool inside the SGA. The memory size allocated to the result cache depends on the memory size of the SGA as well as the memory management system.

You can change the memory allocated to the result cache by setting the RESULT_CACHE_MAX_SIZE parameter. The result cache is disabled if you set its value to 0. The value of this parameter is rounded to the largest multiple of 32 KB that is not greater than the specified value. If the rounded value is 0, then the feature is disabled.

Use the RESULT_CACHE_MAX_RESULT parameter to specify the maximum amount of cache memory that can be used by any single result.
The default value is 5%, but you can specify any percentage value between 1 and 100. This parameter can be implemented at the system and session level.

Use the RESULT_CACHE_REMOTE_EXPIRATION parameter to specify the time (in number of minutes) for which a result that depends on remote database objects remains valid. The default value is 0, which implies that results using remote objects should not be cached. Setting this parameter to a nonzero value can produce stale answers. For example, if the remote table used by a result is modified at the remote database.

Using the RESULT_CACHE Hint

If you want to use the query result cache and the RESULT_CACHE_MODE initialization parameter is set to MANUAL, you must explicitly specify the RESULT_CACHE hint in your query.

This introduces the ResultCache operator into the execution plan for the query. When you execute the query, the ResultCache operator looks up the result cache memory to check whether the result for the query already exists in the cache. If it exists, the result is retrieved directly out of the cache. If it does not yet exist in the cache, the query is executed, the result is returned as output, and is also stored in the result cache memory.

If the RESULT_CACHE_MODE initialization parameter is set to FORCE, and you do not want to store the result of a query in the result cache, you must then use the NO_RESULT_CACHE hint in your query.

For example, when the RESULT_CACHE_MODE value equals FORCE in the initialization parameter file, and you do not want to use the result cache for the EMPLOYEES table, then use the NO_RESULT_CACHE hint.

Note: Use of the [NO_] RESULT_CACHE hint takes precedence over the parameter settings.

Using the DBMS_RESULT_CACHE Package

You can use the DBMS_RESULT_CACHE package to perform various operations such as viewing the status of the cache (OPEN or CLOSED), retrieving statistics on the cache memory usage, and flushing the cache. For example, to view the memory allocation statistics, use the following SQL procedure:

SQL> set serveroutput on
SQL> execute dbms_result_cache.memory_report
R e s u l t   C a c h e   M e m o r y   R e p o r t
[Parameters]
Block Size          = 1024 bytes
Maximum Cache Size  = 720896 bytes (704 blocks)
Maximum Result Size = 35840 bytes (35 blocks)
[Memory]
Total Memory = 46284 bytes [0.036% of the Shared Pool]
... Fixed Memory = 10640 bytes [0.008% of the Shared Pool]
... State Object Pool = 2852 bytes [0.002% of the Shared Pool]
... Cache Memory = 32792 bytes (32 blocks) [0.025% of the Shared Pool]
....... Unused Memory = 30 blocks
....... Used Memory = 2 blocks
........... Dependencies = 1 blocks
........... Results = 1 blocks
............... SQL = 1 blocks



Viewing SQL Query Result Cache Information


SQL Query Result Cache: Considerations

Any user-written function used in a function-based index must have been declared with the DETERMINISTIC keyword to indicate that the function always returns the same output value for any given set of input argument values.

·       The purge works only if the cache is not in use; disable (close) the cache for flush to succeed.
·       With bind variables, cached result is parameterized with variable values. Cached results can be found only for the same variable values. That is, different values or bind variable names cause cache miss.

 






OCI Client Query Cache

You can enable caching of query result sets in client memory with Oracle Call Interface (OCI) Client Query Cache in Oracle Database 11g.

The cached result set data is transparently kept consistent with any changes done on the server side. Applications leveraging this feature see improved performance for queries that have a cache hit.

Additionally, a query serviced by the cache avoids round trips to the server for sending the query and fetching the results. Server CPU, which would have been consumed for processing the query, is reduced thus improving server scalability.

Client-side caching is useful when you have applications that produce repeatable result sets, small result sets, static result sets, or frequently executed queries on database objects that do not change often.

Client and server result caches are autonomous; each can be enabled/disabled independently.
Note: You can monitor the client query cache using the client_result_cache_stats$ view.

The following two parameters can be set in your initialization parameter file:
·       CLIENT_RESULT_CACHE_SIZE: A nonzero value enables the client result cache. This is the maximum size of the client per-process result set cache in bytes. All OCI client processes get this maximum size and can be overridden by the OCI_RESULT_CACHE_MAX_SIZEparameter.
·       CLIENT_RESULT_CACHE_LAG: Maximum time (in milliseconds) since the last round-trip to the server, before which the OCI client query executes a round-trip to get any database changes related to the queries cached on client.
A client configuration file is optional and overrides the cache parameters set in the server initialization parameter file. Parameter values can be part of a sqlnet.ora file. When parameter values shown in the slide are specified, OCI client caching is enabled for OCI client processes using the configuration file.
OCI_RESULT_CACHE_MAX_RSET_SIZE/ROWS denotes the maximum size of any result set in bytes/rows in the per-process query cache.
OCI applications can use application hints to force result cache storage. The application hints can be SQL hints:
·       /*+ result_cache */
·       /*+ no_result_cache */