|
|
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.
|
|
|
|
|