暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

客户端数据库复杂视图优化分析

bestpaydata 2021-04-18
700

本文概述(表名已做脱敏处理):

  1. 数据库视图常用于封装一些复杂冗长的sql,减少宽带传输,增加可读性

  2. 但视图如果写的较复杂时,特别是一些标量子查询存在优化点时,从视图外部无法调优。可考虑把视图写出来再优化

  3. 本文中的优化主要还是针对:分区表全分区扫描、标量子查询冗余、分区表中如何快速求最大日期等的一些优化手段

  4. 提醒下,优化后的语句一定要严格测试,确保数据与之前语句跑出的数据完全匹配后在上线。本文中用的是intersect匹配交集

今天在监视数据库时,发现一条长查询的语句可以优化。其主要原因是查询语句引用到的一张视图写的比较烂

原始的sql语句

select ORDER_TYPE_NAME,

ORDER_ID,

to_char(ACCT_DATE, 'YYYY-MM-DD HH24:MI:SS') as ACCT_DATE,

TERM_ID,

TERM_SEQ,

PRTN_ID,

CUST_TYPE,

CUST_TYPE_NAME,

CUST_CODE,

CUST_NAME,

ACTION_CODE,

ACTION_NAME,

OBJ_CODE,

CARD_NUMBER,

FACE_VALUE_NAME,

to_char(DUE_MONEY) as DUE_MONEY,

to_char(PAY_MONEY) as PAY_MONEY,

to_char(END_BAL) as END_BAL,

ORDER_STAT,

ORDER_STAT_NAME,

PAY_STAT,

PAY_STAT_NAME,

ACT_STAT,

ACT_STAT_NAME,

ERR_CODE,

AREA_CODE,

AREA_NAME,

ORDER_TYPE,

ACCT_CODE,

GOODS_NAME,

EVENT_SEQ

from VIEW_EXP_TMP

where 1 = 1

and ACCT_DATE >= to_date('2015-11-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')

and ACCT_DATE <= to_date('2015-11-26 23:59:59', 'YYYY-MM-DD HH24:MI:SS')

and AREA_CODE in (SELECT AREA_CODE

FROM T_AREACODE

START WITH AREA_CODE = '000000'

CONNECT BY PARENT_AREA = PRIOR AREA_CODE)

and ORDER_TYPE = 'OT501'

统计信息


我们可以看到大量的重复标量子查询引用,并有些range all.我的优化点也主要是优化VIEW_EXP_TMP这个视图

view VIEW_EXP_TMP code

CREATE OR REPLACE VIEW VIEW_EXP_TMP

(area_code, area_name, order_type, order_type_name, order_id, acct_date, acct_date_str, term_id, term_seq, prtn_id, cust_type, cust_type_name, cust_code, cust_name, action_code, action_name, obj_code, card_number, face_value_name, due_money, pay_money, end_bal, order_stat, order_stat_name, pay_stat, pay_stat_name, act_stat, act_stat_name, err_code, acct_code, goods_name, event_seq)

AS

SELECT ord.area_code,

(SELECT area_name FROM T_AREACODE WHERE area_code = ord.area_code) area_name,

ord.order_type,

(SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'SCS_ORDERTYPE'

AND dict_id = ord.order_type) order_type_name,

ord.order_id,

ord.acct_date,

TO_CHAR(ord.acct_date, 'yyyy-mm-dd hh24:mi:ss') acct_date_str,

ord.term_id,

ord.term_seq,

DECODE(tci.prtn_id, 0, 1, tci.prtn_id) prtn_id,

tci.cust_type,

(SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'CUM_CUSTTYPE'

AND dict_id =

(SELECT cust_type FROM T_CLIENT_INFO WHERE cust_id = ord.cust_id)) cust_type_name,

(SELECT cust_code FROM T_CLIENT_INFO WHERE cust_id = ord.cust_id) cust_code,

(SELECT cust_name FROM T_CLIENT_INFO WHERE cust_id = ord.cust_id) cust_name,

(SELECT action_code FROM T_ACTION WHERE action_id = tac.action_id) action_code,

(SELECT action_name FROM T_ACTION WHERE action_id = tac.action_id) action_name,

tac.obj_code,

(SELECT dtl_info

FROM T_QUERY_DORDER

WHERE dtl_id = '1531'

AND event_seq = tac.event_seq) card_number, -- 卡号

(SELECT dtl_info

FROM T_QUERY_DORDER

WHERE dtl_id = 'B525'

AND event_seq = tac.event_seq) face_value_name, -- 卡面值

(SELECT due_moeny FROM T_FEE WHERE order_id = ord.order_id) due_moeny,

(SELECT pay_money FROM T_FEE WHERE order_id = ord.order_id) pay_money,

((SELECT MAX(end_bal)

FROM T_BANK_DEALLOG

WHERE acct_code NOT IN

('440000000000000001', '440000000000000002')

AND event_seq = tac.event_seq) -

(SELECT NVL((SELECT th

FROM t_cum_custom

WHERE custom_type = 'T10'

AND action_id = tac.action_id

AND th_type = 'TH001'

AND cust_id = ord.cust_id),

0)

FROM dual)) end_bal, -- 交易后余额

ord.stat order_stat,

(SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'SCS_ORDERSTAT'

AND dict_id = ord.stat) order_stat_name,

ord.pay_stat,

(SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'SCS_PAYSTAT'

AND dict_id = ord.pay_stat) pay_stat_name,

ord.act_stat,

(SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'SCS_ACTSTAT'

AND dict_id = ord.act_stat) act_stat_name,

(SELECT q.dtl_info

FROM T_QUERY_DORDER q, T_ACCT aa --T_ACCT没有必须在这在关联次,因为下面的from有这张表,可直接引用

WHERE q.dtl_id = '0001'

AND q.event_seq = tac.event_seq

and aa.order_id = ord.order_id

and aa.END_DATE = (select max(x.end_date) from T_ACCT x)) err_code, -- 错误编码

(SELECT acct_code

FROM T_INFOPAYMENT --交易支付表是张大表,可指定分区谓词

WHERE order_id = ord.order_id

and rownum = 1) acct_code, --商户账号

(SELECT VALUE1

FROM t_scs_act_attr

WHERE ACTLIST_ID = tac.actlist_id

AND attr_id = '1016') goods_name, --商品名称

tac.event_seq -- 流水号

FROM T_CLIENT_INFO tci, T_ACCT tac, t_scs_order ord

WHERE tac.order_id = ord.order_id

and tci.cust_id = ord.cust_id --order by ord.acct_date desc;

;

T_CLIENT_INFO这张表被重复关联了4次,其实只要一次即可

T_ACTION这张表被重复关联了2次,其实只要一次即可

标题查询重复关联的还有:T_FEE







优化后的语句我建议视图单独拿出来跑,因为这样可以加一些分区列避免全分区扫描。放在一个视图中灵活性不强

SELECT (SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'SCS_ORDERTYPE'

AND dict_id = ord.order_type) order_type_name,

ord.order_id,

to_char(ACCT_DATE, 'YYYY-MM-DD HH24:MI:SS') as acct_date,

ord.term_id,

ord.term_seq,

DECODE(tci.prtn_id, 0, 1, tci.prtn_id) prtn_id,

tci.cust_type,

(SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'CUM_CUSTTYPE'

AND dict_id = tci.cust_type) cust_type_name,

tci.cust_code cust_code,

tci.cust_name cust_name,

action.action_code,

action.action_name,

tac.obj_code,

(SELECT dtl_info

FROM T_QUERY_DORDER

WHERE dtl_id = '1531'

AND event_seq = tac.event_seq) card_number, -- 卡号

(SELECT dtl_info

FROM T_QUERY_DORDER

WHERE dtl_id = 'B525'

AND event_seq = tac.event_seq) face_value_name, -- 卡面值

to_char(fee.due_moeny) as due_money,

to_char(fee.pay_money) as pay_money,

to_char(((SELECT MAX(end_bal)

FROM T_BANK_DEALLOG

WHERE acct_code NOT IN

('440000000000000001', '440000000000000002')

AND event_seq = tac.event_seq) -

(SELECT NVL((SELECT th

FROM t_cum_custom

WHERE custom_type = 'T10'

AND action_id = tac.action_id

AND th_type = 'TH001'

AND cust_id = ord.cust_id),

0)

FROM dual))) end_bal, -- 交易后余额

ord.stat order_stat,

(SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'SCS_ORDERSTAT'

AND dict_id = ord.stat) order_stat_name,

ord.pay_stat,

(SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'SCS_PAYSTAT'

AND dict_id = ord.pay_stat) pay_stat_name,

ord.act_stat,

(SELECT dict_name

FROM T_DICTIONARY

WHERE dict_typeid = 'SCS_ACTSTAT'

AND dict_id = ord.act_stat) act_stat_name,

(SELECT q.dtl_info

FROM T_QUERY_DORDER q

WHERE q.dtl_id = '0001'

AND q.event_seq = tac.event_seq

and tac.END_DATE =

(select max(x.end_date)

from T_ACCT x

where CREATE_DATE >=

(select max(create_date) - 1 from T_ACCT))) err_code, -- 错误编码

*max(x.end_date)这玩艺应该是要标记下是否是最新结束的订单,看业务只是需要得到最新的处理日期,

可详细指定只扫描最大的那个分区*/

ord.area_code,

area.area_name,

ord.order_type,

(SELECT acct_code

FROM T_INFOPAYMENT

WHERE order_id = ord.order_id

and rownum = 1

and CREATE_DATE between

to_date('2015-11-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS') and

to_date('2015-11-26 23:59:59', 'YYYY-MM-DD HH24:MI:SS') + 1) acct_code, --商户账号

(SELECT VALUE1

FROM t_scs_act_attr

WHERE ACTLIST_ID = tac.actlist_id

AND attr_id = '1016') goods_name, --商品名称

tac.event_seq -- 流水号

FROM T_CLIENT_INFO tci,

T_ACCT tac,

t_scs_order ord,

T_ACTION action,

T_FEE fee,

(SELECT AREA_CODE, area_name

FROM T_AREACODE

START WITH AREA_CODE = '000000'

CONNECT BY PARENT_AREA = PRIOR AREA_CODE) area

WHERE tac.order_id = ord.order_id

and tci.cust_id = ord.cust_id

and tac.action_id = action.action_id(+)

and ord.order_id = fee.order_id

and ord.AREA_CODE = area.AREA_CODE

and ord.ACCT_DATE >=

to_date('2015-11-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')

and ord.ACCT_DATE <=

to_date('2015-11-26 23:59:59', 'YYYY-MM-DD HH24:MI:SS')

and tac.CREATE_DATE >=

to_date('2015-11-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')

and tac.CREATE_DATE <=

to_date('2015-11-26 23:59:59', 'YYYY-MM-DD HH24:MI:SS') + 1

and ord.ORDER_TYPE = 'OT501'

优化后的统计信息


主要是优化了些标量子查询、全分区扫描等。大家可以用beyone compare对比看区别

我们来看下执行后的时间对比

优化后

执行只需7秒

优化前需要34秒

用instersect对比结果数据也是吻合的

文章转载自bestpaydata,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论