You are on page 1of 57

Informix Performance Optimization

Table of Contents

Author: Kevin Fennimore

Informix Performance Optimization Overview: Steps for Optimizing Optimization Goal: Increase Performance Setting up a Test Environment Optimizing the Query: Understand the Requirements Optimizing the Query: Examine the Schema Optimizing the Query: Examine the Data Optimizing the Query: Run, Examine and Modify Set Explain Output Set Explain: Example 1 Set Explain: Example 2 Set Explain: Example 3 Set Explain: Example 4 Set Explain: Example 5 Set Explain: Example 6 Set Explain: Example 7 Set Explain: Example 8 Set Explain: Example 8 cont. Indexing Strategies Indexing Strategies: B+ Trees Indexing Strategies: Types of Indexes Indexing Strategies: Leading Portion of an Index Indexing Strategies: Guidelines Indexing Strategies: Benefits vs. Cost

Email: kfenn@uci-consulting.com

by

Kevin Fennimore

Overview:

Discuss steps for optimizing Discuss the output of the Set Explain command

Discuss Indexing Strategies New SQL in OnLine Dynamic Server (IDS 7.3) Table Scans & Table Joins Optimizer Directives (IDS 7.3) Discuss optimization techniques and examples XTREE command Correlated Sub-Queries (IDS 7.3)

Steps for Optimizing Optimization Goal: Increase Performance

Reduce I/O
o o

reduce I/O performed by the engine reduce I/O between the back-end and the front-end

Reduce processing time

Setting up a Test Environment


Identify Problem Queries Simplify Queries Test on a machine with minimal system activity

Use database that reflects production data Turn Set Explain on

Optimizing the Query: Understand the Requirements


What is the object of the query? What is the information required? What is the order criteria?

Optimizing the Query: Examine the Schema

Identify the the data types and indexes on the columns being:
o o o o

selected used as filters used in joins used for sorting

Be aware of constraints on the data( e.g. primary, check, etc. )

Optimizing the Query: Examine the Data

Consider the number of rows examined vs. the number of rows returned

Determine the distribution of filter columns Look at the relationship of joined tables:
o o o

one-to-one one-to-many many-to-many

Optimizing the Query: Run, Examine and Modify

Run the Query:


query.sql $ timex dbaccess db query.sql > try1.out 2>&1

Examine the Set Explain output Modify the query and/or schema (use directives to test various paths) Run the query again

Set Explain Output Set Explain: Example 1


QUERY: select * from stock order by description Estimated Cost: 6

Estimated # of Rows Returned: 15 Temporary Files Required For: Order By 1) informix.stock: SEQUENTIAL SCAN

Set Explain: Example 2


QUERY: select * from stock where unit_price order by stock_num Estimated Cost: 3 Estimated # of Rows Returned: 5 1) informix.stock: INDEX PATH Filters: informix.stock.unit_price > 20 (1) Index Keys: stock_num manu_code

Set Explain: Example 3


QUERY: select manu_code from stock Estimated Cost: 2 Estimated # of Rows Returned: 15

1) informix.stock: INDEX PATH (1) Index Keys: stock_num manu_code (Key-Only)

Set Explain: Example 4


QUERY: select * from stock where stock_num and stock_num Estimated Cost: 1 Estimated # of Rows Returned: 1 1) informix.stock: INDEX PATH (1) Index Keys: stock_num manu_code Lower Index Filter: informix.stock.stock_num > 10 Upper Index Filter: informix.stock.stock_num < 14

Set Explain: Example 5


QUERY: select * from stock, items where stock.stock_num = items.stock_num and items.quantity

Estimated Cost: 9 Estimated # of Rows Returned: 22 1) informix.stock: SEQUENTIAL SCAN 2) informix.items: INDEX PATH Filters: informix.items.quantity > 1 (1) Index Keys: stock_num manu_code Lower Index Filter: informix.items.stock_num = informix.stock.stock_num

Set Explain: Example 6


QUERY: select * from items,stock where items.total_price=stock.unit_price Estimated Cost: 19 Estimated # of Rows Returned: 59 1) informix.items: SEQUENTIAL SCAN SORT SCAN: informix.items.total_price 2) informix.stock: SEQUENTIAL SCAN SORT SCAN: informix.stock.unit_price

MERGE JOIN Merge Filters: informix.stock.unit_price = informix.items.total_price


Prior to version 5.0 this would have caused an auto-index on the second table.

Set Explain: Example 7


QUERY: -----select * from items, stock where items.total_price = stock.unit_price Estimated Cost: 35 Estimated # of Rows Returned: 496 1) informix.items: SEQUENTIAL SCAN 2) informix.stock: SEQUENTIAL SCAN DYNAMIC HASH JOIN Dynamic Hash Filters: informix.items.total_price = informix.stock.unit_price

Set Explain: Example 8


Table ps_ledger has the following index: create index psaledger on ps_ledger ( account, fiscal_year, accounting_period, business_unit, ledger, currency_cd, statistics_code, deptid, product, posted_total_amt ) fragment by expression ( fiscal_year = 1999 ) in dbspace1, ( fiscal_year = 2000 ) in dbspace1, remainder in dbspace1

Set Explain: Example 8 cont.


QUERY: -----select fiscal_year, account, posted_total_amt from ps_ledger where fiscal_year = 1999 and accounting_period = 10 and account between '1234' and '9999' 1) sysadm.ps_ledger: INDEX PATH Filters: (ps_ledger.fiscal_year = 1999 AND ps_ledger.accounting_period = 10 ) (1) Index Keys: account fiscal_year accounting_period business_unit ledger currency_cd statistics_code deptid product posted_total_amt (Key-Only) (Serial, fragments: 0) Lower Index Filter: ps_ledger.account >= '1234' Upper Index Filter: ps_ledger.account <= '9999'

Indexing Strategies Indexing Strategies: B+ Trees Indexing Strategies: Types of Indexes


Unique Duplicate Composite Clustered Attached Detached

Indexing Strategies: Leading Portion of an Index


Consider an index on columns a, b and c on table xyz.
Index is used for: SELECT * FROM XYZ WHERE a = 1 AND b = 2 AND c = 3 SELECT * FROM XYZ WHERE a = 1 AND b = 2 SELECT * FROM XYZ

WHERE a = 1 ORDER BY a, b, c Index is not used for: SELECT * FROM XYZ WHERE b = 2 AND c = 3 SELECT * FROM XYZ WHERE b = 2 SELECT * FROM XYZ WHERE c = 3 ORDER BY b, c

Indexing Strategies: Guidelines


Columns used in joining tables Columns used as filters Columns used in ORDER BYs and GROUP BYs Avoid highly duplicate columns Keep key size small Limit indexes on highly volatile tables Use the FILL FACTOR option (version 7)

Indexing Strategies: Benefits vs. Cost


Benefits Guarantee Uniqueness Speed up Queries

Cost Maintenance of indexes on Inserts, Updates & Deletes Extra Disk Space

New SQL for Informix Dynamic Server 7.3 New SQL in IDS 7.3

Select first N rows Directives String Manipulation


o o

UPPPER, lower, InitCap replace, substr, lpad, rpad

CASE, NVL, DECODE TO_CHAR, TO_DATE DBINFO Views with UNIONs

New SQL in IDS 7.3: First N rows


SELECT FIRST 10 * from customer where state = MD

order by any_column;
This causes the first 10 rows from the result set to be returned.

New SQL in IDS 7.3: CASE


SELECT name, case when status = A then Active when status = I then Inactive else Invalid Status end, state from customer order by name
SELECT name, case status when A then Active when I then Inactive else Invalid Status end, state

from customer order by name OR

New SQL in IDS 7.3: DECODE


SELECT name, DECODE( status, A, Active, I, Inactive, Invalid Status ), state from customer order by name

New SQL in IDS 7.3: NVL


SELECT name, address1, NVL( address2, ), NVL( status, No status ), city, state, zipcode from customer

order by name
This could also be: CASE WHEN address2 is NULL then else address2

New SQL in IDS 7.3: DBINFO


SELECT DBINFO ( DBSPACE, partnum ), tabid, tabname from systables order by 1

Table Scans Types of Table Scans


Sequential Auto Index Index

Index Scans: Upper and Lower Index Filters

QUERY: select * from stock where stock_num>=99 and stock_num<=190 Estimated Cost: 1 Estimated # of Rows Returned: 1 1) informix.stock: INDEX PATH (1) Index Keys: stock_num manu_code Lower Index Filter: informix.stock.stock_num >= 99 Upper Index Filter: informix.stock.stock_num <= 190

Index Scans: Upper and Lower Index Filters Index Scans: Upper and Lower Index Filters
Create indexes on columns that are the most selective. For example: SELECT * FROM CUSTOMER WHERE ACCOUNT BETWEEN 100 and 1000 AND STATUS = A AND STATE = MD

Which column is the most selective? Account, status or state?

Index Scans: Key-Only


QUERY: select manu_code from stock where stock_num = 190 Estimated Cost: 2 Estimated # of Rows Returned: 15 1) informix.stock: INDEX PATH (1) Index Keys: stock_num manu_code (Key-Only) Lower Index Filter: informix.stock.stock_num = 190

Index Scans: Key-First


QUERY: select count(e)from mytable where a=1 and b=1 and d="Y"

Estimated Cost: 4 Estimated # of Rows Returned: 1 1) informix.mytable: INDEX PATH Filters: informix.mytable.d = 'Y' (1) Index Keys: a b c d (Key-First) (Serial, fragments: ALL) Lower Index Filter: (informix.mytable.a = 1 AND informix.mytable.b = 1 )

Table Joins Joining Tables


Consider the following query:
select * from stock, items where stock.stock_num = items.stock_num and items.quantity

What were looking for is:


All of the items records with a quantity greater than 10 and their associated stock records.

Joining Tables: Join Methods

Sort Merge Join Dynamic Hash Join Nested Loop Join

Join Methods: Nested Loop Join


QUERY: select * from stock, items where stock.stock_num = items.stock_num and items.quantity Estimated Cost: 9 Estimated # of Rows Returned: 22 1) informix.stock: SEQUENTIAL SCAN 2) informix.items: INDEX PATH Filters: informix.items.quantity > 10 (1) Index Keys: stock_num manu_code Lower Index Filter:items.stock_num = stock.stock_num NESTED LOOP JOIN
Notice the index on the joined column New Output in version 7.3

Joining Tables: Table Order


Consider the select:
Select * from A, B where A.join_col = B.join_col

How can the database satisfy this join?


o o

Read from A then find matching rows in B Read from B then find matching rows in A

Joining Tables: Table Order Who Cares?


A then B 1,000 reads from A For each A row do an index scan into B (4 reads) Total reads: 5,000 (1,000 for A + 1,000*4 for B) B then A 50,000 reads from B For each B row do an index

scan into A (3 reads) Total reads: 200,000 (50,000 for B + 50,000*3 for A)
Table A - 1000 rows Table B - 50,000 rows This is a difference of 195,000 reads!!!

Joining Tables: Table Order What is the best order?


A then B 1,000 reads from A For each A row do an index scan into B (4 reads) Total reads: 5,000 (1,000 for A + 1,000*4 for B) Total Rows Returned: 10 B then A Index scan of B (3 reads), then the data (10 reads) for a toal of 13 For each B row do an index

scan into A (3 reads) Total reads: 43 (13 for B+10*3 for A) Total Rows Returned: 10
Table A - 1,000 rows Table B - 50,000 rows Select * from A, B where A.join_col = B.join_col and B.filter_col = 1 General Rule: The table which returns the fewest rows, either through a filter or the row count, should be first. Assume 10 rows in B meet this condition

Joining Tables: Table Order What affects the join order?


Number of rows in the tables Indexes available for:


o o

Filters Join Columns

Data Distribution UPDATE STATISTICS is very important

Optimizer Directives

Optimizer Directives

Changes the generated query plan by removing paths from consideration Similar to Oracles HINTs Better than HINTs
o o o o

More options Cannot be ignored Negative directives Set Explain output

Optimizer Directives
A then B Seq A, Seq B Cost:100 Seq A, Idx B Cost:50 Idx A, Idx B Cost:20 etc. B then A Seq B, Seq A Cost:100 Seq B, Idx A Cost:50 Idx B, Idx A Cost:10 etc.

Select --ORDERED * from A, B where A.join_col = B.join_col With the directive, ORDERED, the optimizer only considers paths that read from A then B. The lowest cost is then chosen from those paths. Normally, this path would be chosen With the directive, this path would be chosen

Optimizer Directives: Syntax


SELECT --+ directive text SELECT {+ directive text } UPDATE --+ directive text UPDATE {+ directive text } DELETE --+ directive text DELETE {+ directive text }
C-style comments are also valid as in: SELECT /*+directive*/

Types of Directives

Access Methods Join Methods Join Order

Optimization Goal

Types of Directives: Access Methods


index - forces use of a subset of specified indexes index_one - forces use of one of the specified indexes index_all - forces use of all of the specified indexes avoid_index - avoids use of specified indexes full - forces sequential scan of specified table avoid_full - avoids sequential scan of specified table

Types of Directives: Join Order

ordered - forces table order to follow the from clause

Types of Directives: Optimization Goal

first_rows (N) - tells the optimizer to choose a plan optimized to return the first N rows of the result set all_rows - tells the optimizer to choose a plan optimized to all tupelos

Types of Directives: Join Methods


use_nl - forces nested loop join on specified tables use_merge - forces sort merge join on specified tables use_hash - forces hash join on specified tables avoid_nl - avoids nested loop join on specified tables avoid_merge - avoids sort merge join on specified tables avoid_hash - avoids hash join on specified tables

Directives Examples: ORDERED


QUERY: select --+ ordered customer.lname, orders.order_num, items.total_price from customer, orders, items where customer.customer_num = orders.customer_num and orders.order_num = items.order_num and items.stock_num = 6 and items.manu_code = "SMT" DIRECTIVES FOLLOWED: ORDERED

DIRECTIVES NOT FOLLOWED: 1) customer: SEQUENTIAL SCAN 2) orders: INDEX PATH (1) Index Keys: customer_num Lower Index Filter: orders.customer_num = customer.customer_num NESTED LOOP JOIN 3) items: INDEX PATH Filters: items.order_num = orders.order_num (1) Index Keys: stock_num manu_code Lower Index Filter: (items.stock_num = 6 AND items.manu_code = 'SMT' ) NESTED LOOP JOIN

Directives Examples : INDEX


QUERY: -----select --+ ordered index(customer, zip_ix) avoid_index(orders," 101_4")

customer.lname, orders.order_num, items.total_price from customer c, orders o, items i where c.customer_num = o.customer_num and o.order_num = i.order_num and stock_num = 6 and manu_code = "SMT"

Directives Examples : INDEX (cont.)


DIRECTIVES FOLLOWED: ORDERED INDEX ( customer zip_ix ) AVOID_INDEX ( orders 101_4 ) DIRECTIVES NOT FOLLOWED: 1)customer: INDEX PATH (1) Index Keys: zipcode 2)orders: SEQUENTIAL SCAN DYNAMIC HASH JOIN (Build Outer) Dynamic Hash Filters:c.customer_num =o.customer_num 3)items: INDEX PATH

Filters:i.order_num =o.order_num (1) Index Keys: stock_num manu_code Lower Index Filter: (i.stock_num = 6 AND i.manu_code = 'SMT' ) NESTED LOOP JOIN

Directives Examples : Errors


QUERY: select --+ ordered index(customer, zip_ix) avoid_index(orders," 222_4") customer.lname, orders.order_num, items.total_price from customer, orders, items where customer.customer_num = orders.customer_num and orders.order_num = items.order_num and stock_num = 6 and manu_code = "SMT" DIRECTIVES FOLLOWED: ORDERED INDEX ( customer zip_ix ) DIRECTIVES NOT FOLLOWED:

AVOID_INDEX( orders 222_4 ) Invalid Index Name Specified.

Optimization Techniques Optimization Techniques


Use Composite Indexes Use Index Filters Create indexes for Key-Only scans Perform indexed reads for sorting Use temporary tables Simplify queries by using Unions Avoid sequential scans of large tables

Optimization Techniques (Cont.)


Drop and recreate indexes for large modifications Avoid Correlated Sub-queries (pre IDS 7.3) Select needed columns vs. Select * Use OUTER JOINS

Prepare and Execute statements

Optimization Techniques: Use Composite Indexes

Composite indexes are ones built on more than one column The optimizer uses the leading portions of a composite index for filters, join conditions and sorts A composite index on columns a, b and c will be used for selects involving:
o o o

column a columns a and b columns a, b and c

It will not be used for selects involving only columns b and/or c since those columns are not at the beginning of the index( i.e. the leading portion )

Optimization Techniques: Use Index Filters


Create indexes on columns that are the most selective. For example: SELECT * FROM CUSTOMER WHERE ACCOUNT BETWEEN 100 and 1000 AND STATUS = A

AND STATE = MD Which column is the most selective? Account, status or state?

Optimization Techniques: Use Index Filters


Assume table xyz has an index on begin_idx & end_idx With the following select:
SELECT * FROM xyz WHERE begin_idx >= 99 AND end_idx <= 150

The leading portion of the index, column begin_idx, will be used.

Optimization Techniques: Use Index Filters Optimization Techniques: Use Index Filters
If we can change the query to include an upper bound on begin_idx as follows:
SELECT * FROM xyz WHERE begin_idx >= 99

AND begin_idx <= 150 AND end_idx <= 150

Optimization Techniques: Use Index Filters Optimization Techniques: Key-Only Scans

Data for the select list is read from the index key -- No read of the data page is needed Useful for inner tables of nested-loop joins Useful for creating a sub-table for very wide tables

Optimization Techniques: Indexed Reads for Sorting

Indexed reads cause rows to be read in the order of the indexed columns Higher priority is given to indexes on columns used as filters Reasons why an index will not be used to perform a sort:
o o o

Columns in the sort criteria are not in the index Columns in the sort criteria are in a different order than the index Columns in the sort criteria are from different tables

Optimization Techniques: Indexed Reads for Sorting


select * from some_table where x = ? and y = ? order by z Assume the table some_table has a composite index on columns x, y and z. Note: With Informix Dynamic Server v7.31 this is done automatically by the optimizer. select * from some_table where x = ? and y = ? order by x, y, z

Optimization Techniques: Indexed Reads for Sorting


select a.invt, a.location, a.sku from inv_sum a, loc_master b where a.location = b.location and b.code in ( A, B, C ) order by a.location, a.sku
Table inv_sum has 300,000 records and a composite index on the columns location and sku. Table loc_master has an index on code. There is a 1-to-1 relationship between the tables. The filter condition on code excludes about 20,000 records, leaving 280,000 to be sorted. The optimizer gives preference to the index on the filter column code and a temporary file is used for the sorting instead of doing an indexed read on the composite index.

Optimization Techniques: Indexed Reads for Sorting


select a.invt, a.location, a.sku from inv_sum a, loc_master b where a.location = b.location and a.location >= order by a.location, a.sku
By moving the filter from the select into the program and adding a dummy filter on location, the optimizer chooses to use the composite index for the filter and the sorting. Note: location >= will return all values for location since all character strings are greater than the empty string.

Optimization Techniques: Indexed Reads for Sorting


select --+ INDEX( a i_inv_sum1 ) a.invt, a.location, a.sku from inv_sum a, loc_master b where a.location = b.location and b.code in ( A, B, C ) order by a.location, a.sku

With IDS v7.3, an optimizer directive could be used. Assuming the index name on inv_sum was i_inv_sum1, the select would be: The ORDERED directive might have done the same thing.

Optimization Techniques: Temporary Tables


Useful for batch reporting Avoid selecting a subset of data repetitively from a larger table Create summary information that can be joined to other tables
Disadvantage The data in the temporary table is a copy of the real data and therefore is not changed if the original data is modified.

Optimization Techniques: Temporary Tables


select b.sku, sum(b.sz_qty) tot_qty from ctn a, ctn_detail b where a.carton_stat = "Q" and a.ctn_id = b.ctn_id group by b.sku into temp tmp1 with no log;

create index i1 on tmp1( sku ) select tot_qty from tmp1 where sku = ?
select sum(b.sz_qty) from ctn a, ctn_detail b where a.carton_stat = "Q" and a.ctn_id = b.ctn_id and b.sku = ? The ctn table contains 300,000 records and very few records have a status of Q.

Optimization Techniques: Using UNIONs


OR's can cause the optimizer to not use indexes Complex where conditions can cause the optimizer to use the wrong index Note: Informix Dynamic Server v7.3 allows UNIONs in views

Optimization Techniques: Using UNIONs


select sum(qty) from log where trans_id = 1 and sku = ?

and date_time > ? UNION ... select sum(qty) from log where trans_id = 8 and sku = ? and date_time > ?
select sum(qty) from log where sku = ? and trans_id in ( 1, 2, 3, 4, 5, 6, 7, 8 ) and date_time > ? The log table has an index on date_time and a composite index on trans_id, sku and date_time.

Optimization Techniques: Avoid Sequential Scans and Auto Indexes


Sequential scans of large tables are resource intensive Sequential scans of small tables are not harmful Consider using permanent indexes to avoid sequential scans when possible Create temporary indexes for batch reporting

Replace Auto Indexes with real indexes

Optimization Techniques: Drop and Recreate Indexes

Useful for modifications to > 25% of the rows


o o

Eliminates overhead of maintaining indexes during modification Indexes are recreated more efficiently

Indexes can deteriorate over time


Disadvantage The table must be locked in exclusive mode while the indexes are dropped and recreated.

Optimization Techniques: Avoid Correlated Sub-queries


select c.* from customers c, orders o where c.custid = o.custid and o.ord_date = TODAY
select c.* from customers c where c.custid in ( select custid from orders where ord_date = TODAY ) This is an example of a non-correlated sub-query. The performance of these two should be the same.

Optimization Techniques: Avoid Correlated Sub-queries


select c.* from customers c, orders o where c.custid = o.custid and o.stat = OPEN
select c.* from customers c where exists ( select custid from orders o where o.custid = c.custid and o.stat = OPEN )

Optimization Techniques: Select needed columns vs. Select *


select * from customers
select cust_num, last_name from customers If the table customer had many columns, selecting needed columns is more efficient than selecting all of the columns.

Optimization Techniques: Use Outer Joins


SELECT cnum FROM customer WHERE status = A FOREACH SELECT onum FROM ORDERS o WHERE o.cnum = cnum IF ( STATUS = NOTFOUND ) THEN ... END IF END FOREACH
SELECT cnum, onum FROM customer c OUTER order o WHERE status = A AND c.cnum = o.cnum FOREACH IF ( onum IS NULL ) THEN ... END IF END FOREACH

Optimization Techniques: Use Outer Joins


SELECT cnum, NVL( onum, 0 ) FROM customer c OUTER order o WHERE status = A AND c.cnum = o.cnum FOREACH IF ( onum = 0 ) THEN ... END IF END FOREACH

Optimization Techniques: Prepare and Execute


What happens when a statement is sent to the engine?
o o o o

Syntax Check Permission Check Optimization Statement is executed

Optimization Techniques: Prepare and Execute


FOR x = 1 to 1000 INSERT INTO some_table VALUES ( x, 10 )

END FOR
PREPARE p1 FROM INSERT INTO some_table VALUES ( ?, 10 ) FOR x = 1 to 1000 EXECUTE p1 USING x END FOR

Correlated Sub-Queries Correlated Sub-Queries What are they?


select c.* from customers c, orders o where c.custid = o.custid and o.ord_date = TODAY
select c.* from customers c where c.custid in ( select custid from orders where ord_date = TODAY ) This is an example of a non-correlated sub-query. The performance of these two should be the same.

Correlated Sub-Queries What are they?


select c.*

from customers c, orders o where c.custid = o.custid and o.stat = OPEN


select c.* from customers c where exists ( select X from orders o where o.custid = c.custid and o.stat = OPEN ) select c.* from customers c where custid in ( select custid from orders o where o.stat = OPEN )

Correlated Sub-Queries Whats wrong with them?


Consider the statement:
update customers set stat = A where exists ( select X from orders o where o.custid = customer.custid and o.cmpny = customers.cmpny and o.stat = OPEN )

The statement is executed by executing the sub-query, on orders, for every row retrieved from customers.

If customers table had 100,000 rows, the sub-query would get executed 100,000 times. However, if orders only had 20 rows with stat=OPEN the database would be doing a lot of extra work.

Correlated Sub-queries
update customers set stat = A where exists ( select X from orders o where o.custid = customer.custid and o.cmpny = customers.cmpny and o.stat = OPEN ) and custid in ( select custid from orders o where o.stat = OPEN )
update customers set stat = A where exists ( select X from orders o where o.custid = customers.custid and o.cmpny = customers.cmpny

and o.stat = OPEN ) If orders has only 20 rows meeting the filter, the second version of the update runs much faster, assuming that customers has an index on the column custid. The original CSQ is left since it was joining on more than one column

Correlated Sub-queries: Normal CSQ


QUERY: update orders set ship_charge = 0 where exists ( select "X" from customer c where c.customer_num = orders.customer_num and c.state = "MD ) 1) informix.orders: SEQUENTIAL SCAN Filters: EXISTS <subquery> Subquery: --------Estimated Cost: 1 Estimated # of Rows Returned: 1 1) informix.c: INDEX PATH Filters: informix.c.state = 'MD' (1) Index Keys: customer_num

Lower Index Filter: c.customer_num = orders.customer_num

Correlated Sub-queries: Rewritten CSQ


QUERY: update orders set ship_charge = 0 where customer_num in ( select customer_num from customer c where c.state = "MD ) 1) informix.orders: INDEX PATH (1) Index Keys: customer_num Lower Index Filter:orders.customer_num = ANY <subquery> Subquery: --------1) informix.c: SEQUENTIAL SCAN Filters: informix.c.state = 'MD'

Correlated Sub-queries: CSQ Flattening

QUERY: update orders set ship_charge = 0 where exists ( select "X" from customer c where c.customer_num = orders.customer_num and c.state = "MD ) 1) informix.c: SEQUENTIAL SCAN Filters: informix.c.state = 'MD' 2) informix.orders: INDEX PATH (1) Index Keys: customer_num Lower Index Filter: orders.customer_num = c.customer_num NESTED LOOP JOIN
An index could be created on state to avoid the sequential scan.

Correlated Sub-queries: Predicate Promotion in CSQs


Correlated Version select * from ps_jrnl_ln

where business_unit = 'ABC and process_instance = 5960 and not exists ( select "X" from PS_SP_BU_GL_NONVW P where P.business_unit = ps_jrnl_ln.business_unit ) Non-Correlated Version select * from ps_jrnl_ln where business_unit = 'ABC and process_instance = 5960 and not exists ( select "X" from PS_SP_BU_GL_NONVW P where P.business_unit = ABC )

Correlated Sub-queries: Predicate Promotion in CSQs


QUERY: select * from ps_jrnl_ln where business_unit = 'ABC and process_instance = 5960 and not exists ( select "X" from PS_SP_BU_GL_NONVW P where P.business_unit = ps_jrnl_ln.business_unit)

Correlated Sub-queries: Predicate Promotion in CSQs


1) ps_jrnl_ln: INDEX PATH Filters: NOT EXISTS <subquery> (1) Index Keys: process_instance business_unit Lower Index Filter: (ps_jrnl_ln.business_unit = 'ABC' AND ps_jrnl_ln.process_instance = 5960 ) Subquery:

--------1) ps_bus_unit_tbl_gl: INDEX PATH (1) Index Keys: business_unit (Key-Only) Lower Index Filter: ps_bus_unit_tbl_gl.business_unit = 'ABC' 2) ps_bus_unit_tbl_fs: INDEX PATH (1) Index Keys: business_unit descr (Key-Only) Lower Index Filter: ps_bus_unit_tbl_fs.business_unit = ps_bus_unit_tbl_gl.business_unit NESTED LOOP JOIN

Constant Subquery Optimization When this filter is checked for the first row, the query can stop immediately, if: its a NOT EXISTS and a row is found its an EXISTS and no rows are found

Correlated Sub-Queries: First Row/SemiJoin


QUERY: UPDATE PS_JRNL_LN SET jrnl_line_status = 3 WHERE BUSINESS_UNIT='ABC' AND PROCESS_INSTANCE=5960

AND EXISTS ( SELECT 'X' FROM PS_COMBO_SEL_06 A WHERE A.SETID='ABC' AND A.COMBINATION='OVERHEAD' AND A.CHARTFIELD='ACCOUNT' AND PS_JRNL_LN.ACCOUNT BETWEEN A.RANGE_FROM_06 AND A.RANGE_TO_06) Estimated Cost: 79 Estimated # of Rows Returned: 1

Correlated Sub-Queries: First Row/SemiJoin


1) sysadm.ps_jrnl_ln: INDEX PATH (1) Index Keys: process_instance business_unit Lower Index Filter: (ps_jrnl_ln.business_unit = 'ABC' AND ps_jrnl_ln.process_instance = 5960 ) 2) informix.a: INDEX PATH (First Row)

Filters: (informix.a.range_to_06 >= ps_jrnl_ln.account AND a.tree_effdt = <subquery> ) (1) Index Keys: setid chartfield combination range_from_06 range_to_06 Lower Index Filter: (a.setid = 'ABC' AND (a.combination = 'OVERHEAD' AND a.chartfield = 'ACCOUNT' ) ) Upper Index Filter: a.range_from_06 <= ps_jrnl_ln.account NESTED LOOP JOIN (Semi Join)

Correlated Sub-Queries: Skip Duplicate


QUERY: update orders set backlog = "Y" where exists ( select "X from items where orders.order_num = items.order_num and stock_num = 6 and manu_code = "SMT ) 1) informix.items: INDEX PATH (Skip Duplicate) Filters: (items.stock_num=6

AND items.manu_code='SMT' ) (1) Index Keys: order_num 2) informix.orders: INDEX PATH (1) Index Keys: order_num Lower Index Filter: orders.order_num = items.order_num NESTED LOOP JOIN

You might also like