Question : SYBASE QUERY OPTIMIZATION

Hello,
I have a query such as below on which I ran a Show plan with statistics IO and Statistics time on:
The below Code is part of a Stored procedure and I am trying to Optimize the stored Procedure One query at a time. The Stored Procedure takes 17 minutes to run.
If the information below is insufficient to analyze then I can re-word the question. Thanks
-------------------------------------------------------------------------------------------------------------------------
Assume that the Temporary tables have Data.

create table #trans_ac_bal_tmp(
rpt_per_d    datetime    not null,
ext_src_sys_id_t  char(30)    not null,
ic_tran_id_c   int      not null,
ic_acct_typ_c   char(10)    not null,
adac_unit_id_c   char(4)     null,
gl_comp_id_c   char(5)     null,
adac_ctpy_unit_c  char(4)     null,
gl_comp_affil_id_c  char(5)     null,
hyp_acct_n    char(26)    null,
adac_acct_id_c   char(10)    null,
gl_acct_post_lvl_c  char(10)     null,
gl_acct_sub_lvl_c  char(8)     null,
t_con_m    float      null,
t_base_cur_id_c  char(3)     null,
t_base_m    float      null,
t_natv_cur_id_c  char(6)     null,
t_natv_m    float      null,
adac_unit_cur_id_c  char(3)     null,
adac_unit_cur_m  float      null,
user_id_add_c   char(8)     not null,
user_id_updt_c   char(8)     not null,
stamp_add_dz   datetime    not null,
stamp_updt_dz   datetime not null,
t_con2_m            float       null,
ic_linked_id     char(20) null)

create nonclustered index dnc_idx001 on #trans_ac_bal_tmp
    (rpt_per_d, ext_src_sys_id_t, ic_tran_id_c)
-----------------------------------------------------------------------------------------------
create table #temp_trans_feed_result (
ext_rec_grp_seq_c     int      null,
rpt_per_d       datetime     null,
product_type       char(25)     null,
ext_ic_t_id_c      char(50)     null,
ext_c_ac_id_c      char(50)     null,
ext_c_ac_t      char(50)     null,
ext_trd_agre_d     datetime     null,
settlement_d       datetime     null,
start_d      datetime     null,
maturity_d      datetime     null,
clearance_d     datetime     null,
p_isin_id_c     char(24)     null,
product_code       char(50)     null,
ext_trd_prod_t     char(50)     null,
underly_instr_desc_t     char(50)     null,
underly_instr_cur_id_c   char(6)   null,
underly_amt     float     null,
buy_sell_id_c      char(4)   null,
put_call_id_c      char(4)   null,
remium_paid_recv_m    float     null,
strike_price_m     float     null,
interest_rate_m    float     null,
float_fix_id_c     char(10)     null,
book_cost_center      char(30)     null,
ext_src_sys_id_t      char(30)     null,
adac_unit_id_c     char(4)   null,
gl_comp_id_c       char(5)   null,
adac_ctpy_unit_c      char(4)   null,
gl_comp_affil_id_c    char(5)   null,
adac_acct_id_c     char(10)     null,
gl_acct_post_lvl_c    char(10)   null,
gl_acct_sub_lvl_c     char(8)   null,
t_con_m         float     null,
t_base_cur_id_c    char(3)   null,
t_base_m        float     null,
t_natv_cur_id_c    char(6)   null,
t_natv_m        float     null,
adac_unit_cur_id_c    char(3)   null,
adac_unit_cur_m    float     null,
payable_adac_acct_id_c   char(10)     null,
payable_gl_acct_post_lvl_c  char(10)   null,
payable_t_con_m       float     null,
payable_t_base_cur_id_c  char(3)   null,
payable_t_base_m      float     null,
payable_t_natv_cur_id_c  char(6)   null,
payable_t_natv_m      float     null,
payable_adac_unit_cur_id_c  char(3)   null,
payable_adac_unit_cur_m  float     null,
recv_adac_acct_id_c   char(10)     null,
recv_gl_acct_post_lvl_c  char(10)   null,
recv_t_con_m       float     null,
recv_t_base_cur_id_c     char(3)   null,
recv_t_base_m      float     null,
recv_t_natv_cur_id_c     char(6)   null,
recv_t_natv_m      float     null,
recv_adac_unit_cur_id_c  char(3)   null,
recv_adac_unit_cur_m     float     null,
acrint_adac_acct_id_c    char(10)     null,
acrint_gl_acct_post_lvl_c   char(10)   null,
acrint_gl_acct_sub_lvl_c    char(8)   null,
acrint_t_con_m       float     null,
acrint_t_base_cur_id_c   char(3)   null,
acrint_t_base_m    float     null,
acrint_t_natv_cur_id_c   char(6)   null,
acrint_t_natv_m    float     null,
acrint_adac_unit_cur_id_c   char(3)   null,
acrint_adac_unit_cur_m   float     null,
cmpacct_adac_acct_id_c   char(10)     null,
cmpacct_gl_acct_post_lvl_c  char(10)   null,
cmpacct_t_con_m       float     null,
cmpacct_t_base_cur_id_c  char(3)   null,
cmpacct_t_base_m      float     null,
cmpacct_t_natv_cur_id_c  char(6)   null,
cmpacct_t_natv_m      float     null,
cmpacct_adac_unit_cur_id_c  char(3)   null,
cmpacct_adac_unit_cur_m  float     null,
regional_ref_t     char(20)     null,
counterparty_trade_num   char(50)     null,
user_id_add_c      char(8)   null,
user_id_updt_c     char(8)   null,
stamp_add_dz       datetime     null,
stamp_updt_dz      datetime     null,
ic_linked_id       char(20) null)

create nonclustered index dnc_idx001 on #temp_trans_feed_result
    (rpt_per_d, ext_src_sys_id_t, ext_rec_grp_seq_c)
-----------------------------------------------------------------------------------------------
Tables #temp_trans_feed_result and #trans_ac_bal_tmp have the same value for columns ext_src_sys_id_t and rpt_per_d
-----------------------------------------------------------------------------------------------
update  #temp_trans_feed_result
set adac_acct_id_c   = b.adac_acct_id_c,
 gl_acct_post_lvl_c  = b.gl_acct_post_lvl_c,
 gl_acct_sub_lvl_c  = b.gl_acct_sub_lvl_c,
 t_con_m     = b.t_con_m,
 t_base_cur_id_c   = b.t_base_cur_id_c,
 t_base_m    = b.t_base_m,
 t_natv_cur_id_c   = b.t_natv_cur_id_c,
 t_natv_m    = b.t_natv_m,
 adac_unit_cur_id_c  = b.adac_unit_cur_id_c,
 adac_unit_cur_m   = b.adac_unit_cur_m
from    #temp_trans_feed_result a, #trans_ac_bal_tmp b
where   a.ext_rec_grp_seq_c =  b.ic_tran_id_c
and  a.ext_src_sys_id_t  =   b.ext_src_sys_id_t
and  a.rpt_per_d   = b.rpt_per_d
----------------------------------------------------------------------------------------------------------------
Query Plan Output:
QUERY PLAN FOR STATEMENT 27 (at line 345).


    STEP 1
        The type of query is UPDATE.
        The update mode is deferred_varcol.

        FROM TABLE
            #temp_trans_feed_result
            a
        Nested iteration.
        Table Scan.
        Forward scan.
        Positioning at start of table.
        Using I/O Size 16 Kbytes for data pages.
        With LRU Buffer Replacement Strategy for data pages.

        FROM TABLE
            #trans_ac_bal_tmp
            b
        EXISTS TABLE : nested iteration.
        Table Scan.
        Forward scan.
        Positioning at start of table.
        Using I/O Size 16 Kbytes for data pages.
        With LRU Buffer Replacement Strategy for data pages.
        TO TABLE
            #temp_trans_feed_result
        Using I/O Size 2 Kbytes for data pages.
---------------------------------------------------------------------------------------------------

Answer : SYBASE QUERY OPTIMIZATION

You have run up against a classic issue with creating indexes on temp tables in a stored procedure.  The say Sybase works, is that the query optimization that happens when you create/compile the procedure does not find or use the index.  You can see that from the by looking at the plan; neither of the indexes is used.  The result is an n X m join done with table scans; never a good thing.

Another thing.  You only need to index one of the tables.  The driving table will always be a table scan since you need someplace to start.  The index on the driving table will never be used UNLESS it contains ALL of the columns required by the DML.  Normally, you want to make the smaller table the driving table since that will reduce the number of index lookups and therefore the total effort required to satisfy the query (in this case the update).

Depending on your version of Sybase, there are one or two ways to get around this.  The first method works regardless of version.  That involves taking your update statement and putting it in it's own stored procedure which you then call from the first one.  When you compile the update proc, you have to have a script that creates the temp tables, preferably with representative data volumes, and the indexes; they have to exist for the proc to compile.  Then you can compile the main procedure that calls the update one and sysdepends will by happy.

The second method is a bit of a cheat and might not perform quite as well, but it works at least in 12.5.1 and above.  If you create your temp tables with explicit CREATE TABLE statements, make sure you have a clustered primary key constraint in the definition, then populate them with INSERT/SELECTs intead of SELECT/INTO, and finally HINT the index in the update.  Hints are in the FROM clause and are of the form (INDEX = IndexName) placed immediately after the associated table name.  You may get a runtime warning message that the index could not be found and that it will look for another, however, under the covers it will actually use it.  The actual query engine is smarter than the SP runtime engine thinks it is.

An entirely different approach is to join against the base table from which #temp_trans_feed_result is create from and stuff the results into the temp table.  Assuming trans_feed_result has a good index to join to #trans_ac_bal_temp, you could do something like

SELECT
INTO #temp_trans_feed_result
FROM #trans_ac_bal_temp
JOIN trans_feed_result
  on
WHERE

You wind up in the same spot as if you did it the other way but you potentially save your self an index build, a bunch of logging, and all of the update overhead.

Just a thought.

Bill

PS, some folks might find 50 points to be a bit slim for a non-trivial performance and tuning question.
Random Solutions  
 
programming4us programming4us