暂无图片
用表用,和表的别名,执行计划有什么区别,麻烦大佬给看看
我来答
分享
NIU
2023-02-07
用表用,和表的别名,执行计划有什么区别,麻烦大佬给看看

观察到监控软件上有一个sql,等待事件很长,哪来看看,废话不多说,直接看sql_text:

软件捕获到的

INSERT
INTO KEEPER.SALE_CARD_BALANCE
(
ACCOUNT_ID,
CARD_NO,
CARD_SORT_ID,
CARD_TYPE,
CARD_TYPE_NAME,
SUMMONS_NO,
MANAGEDBRANCHNO,
MANAGEDBRANCHNAME,
SALE_DATE,
UNIT_NO,
NOW_BALANCE,
CREATE_DATE,
RUN_DATE,
CARD_STATE,
INIT_AMOUNT
)
SELECT
/*+index(b IDX_ACCOUNT_ID)*/
A.ACCOUNT_ID,
A.CARD_NO,
A.CARD_SOTR_ID,
A.CARD_TYPE,
A.CARD_TYPE_NAME,
A.SUMMONS_NO,
A.MANAGEDBRANCHNO,
A.MANAGEDBRANCHNAME,
A.OPT_DATE AS SALE_DATE,
A.UNIT_NO,
SUM(B.AMOUNT) AS NOW_BALANCE,
SYSDATE,
:B1 AS RUN_DATE,
B.STATUS,
A.INIT_AMOUNT
FROM KEEPER.SJL_CARD_BANK A
JOIN UNITED.XSH_VALID_CARD B
ON A.ACCOUNT_ID = B.ACCOUNT_ID
AND B.AMOUNT>0
WHERE A.SALE_SYS = 'new'
AND A.DETAIL_TYPE != '废卡'
GROUP BY A.ACCOUNT_ID,
A.CARD_NO,
A.CARD_SOTR_ID,
A.CARD_TYPE,
A.CARD_TYPE_NAME,
A.SUMMONS_NO,
A.MANAGEDBRANCHNO,
A.MANAGEDBRANCHNAME,
A.OPT_DATE,
A.UNIT_NO,
A.DETAIL_TYPE,
B.STATUS,
A.ZERO_FLAG,
A.INIT_AMOUNT;


对应的执行计划:

Plan Hash Value : 1065546509

------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14904088 | 1907723264 | 1676835 | 00:01:11 |
| 1 | HASH GROUP BY | | 14904088 | 1907723264 | 1676835 | 00:01:11 |
| * 2 | HASH JOIN | | 14904088 | 1907723264 | 1250043 | 00:00:53 |
| 3 | JOIN FILTER CREATE | :BF0000 | 13980272 | 1467928560 | 132686 | 00:00:06 |
| * 4 | TABLE ACCESS FULL | SJL_CARD_BANK | 13980272 | 1467928560 | 132686 | 00:00:06 |
| 5 | VIEW | VW_GBC_5 | 46903023 | 1078769529 | 961334 | 00:00:41 |
| 6 | HASH GROUP BY | | 46903023 | 609739299 | 961334 | 00:00:41 |
| 7 | JOIN FILTER USE | :BF0000 | 46903023 | 609739299 | 734937 | 00:00:32 |
| * 8 | TABLE ACCESS FULL | XSH_VALID_CARD | 46903023 | 609739299 | 734937 | 00:00:32 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("A"."ACCOUNT_ID"="ITEM_1")
* 4 - filter("A"."SALE_SYS"='new' AND "A"."DETAIL_TYPE"<>'废卡')
* 8 - filter("B"."AMOUNT">0 AND SYS_OP_BLOOM_FILTER(:BF0000,"B"."ACCOUNT_ID"))


自己能力有限,一般遇到执行比较慢的sql,首先看看执行计划,是否最优,然后看是否走索引,走索引是否是最佳索引组合,不走索引(可能是新执行的sql,没有创建索引,此时查询当前表的大小,然后再根据创建索引列的数据是否重复,再判断是否要创建索引,创建时一定要加online,否则会让你栓Q。),然后看表,索引的统计信息是否最新。还有有索引不走,可以加hiti 强制走再看看执行计划是否改变,下面我把select中的b别名改成了真实的表名,如下sql:

INSERT
INTO KEEPER.SALE_CARD_BALANCE
(
ACCOUNT_ID,
CARD_NO,
CARD_SORT_ID,
CARD_TYPE,
CARD_TYPE_NAME,
SUMMONS_NO,
MANAGEDBRANCHNO,
MANAGEDBRANCHNAME,
SALE_DATE,
UNIT_NO,
NOW_BALANCE,
CREATE_DATE,
RUN_DATE,
CARD_STATE,
INIT_AMOUNT
)
SELECT
/*+index(XSH_VALID_CARD IDX_ACCOUNT_ID)*/ 

A.ACCOUNT_ID,
A.CARD_NO,
A.CARD_SOTR_ID,
A.CARD_TYPE,
A.CARD_TYPE_NAME,
A.SUMMONS_NO,
A.MANAGEDBRANCHNO,
A.MANAGEDBRANCHNAME,
A.OPT_DATE AS SALE_DATE,
A.UNIT_NO,
SUM(B.AMOUNT) AS NOW_BALANCE,
SYSDATE,
:B1 AS RUN_DATE,
B.STATUS,
A.INIT_AMOUNT
FROM KEEPER.SJL_CARD_BANK A
JOIN UNITED.XSH_VALID_CARD B
ON A.ACCOUNT_ID = B.ACCOUNT_ID
AND B.AMOUNT>0
WHERE A.SALE_SYS = 'new'
AND A.DETAIL_TYPE != '废卡'
GROUP BY A.ACCOUNT_ID,
A.CARD_NO,
A.CARD_SOTR_ID,
A.CARD_TYPE,
A.CARD_TYPE_NAME,
A.SUMMONS_NO,
A.MANAGEDBRANCHNO,
A.MANAGEDBRANCHNAME,
A.OPT_DATE,
A.UNIT_NO,
A.DETAIL_TYPE,
B.STATUS,
A.ZERO_FLAG,
A.INIT_AMOUNT;


执行计划:

Plan Hash Value : 401238233

---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14904087 | 1758682266 | 1397129 | 00:01:00 |
| 1 | HASH GROUP BY | | 14904087 | 1758682266 | 1397129 | 00:01:00 |
| * 2 | HASH JOIN | | 14904087 | 1758682266 | 1001325 | 00:00:43 |
| * 3 | TABLE ACCESS FULL | XSH_VALID_CARD | 46903023 | 609739299 | 734937 | 00:00:32 |
| * 4 | TABLE ACCESS FULL | SJL_CARD_BANK | 13980272 | 1467928560 | 132686 | 00:00:06 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("A"."ACCOUNT_ID"="B"."ACCOUNT_ID")
* 3 - filter("B"."AMOUNT">0)
* 4 - filter("A"."SALE_SYS"='new' AND "A"."DETAIL_TYPE"<>'废卡')


疑点:hiti中,用别名和原表名效果不一样吗?
还有,是否还有可以优化的地方,请各位大佬给看看

我来答
添加附件
收藏
分享
问题补充
11条回答
默认
最新
Thomas

执行计划部分,最好是显示为缩进带层次的,比如:

0

   1

      2

      3

         4

你可以把两个执行计划截图发上来,不过 SJL_CARD_BANK 全表扫描肯定是不大好的   

暂无图片 评论
暂无图片 有用 0
NIU
题主
2023-02-07
截图发上去了 您看看
NIU


暂无图片 评论
暂无图片 有用 1
游湖

hint中当然要用别名来代替表名

暂无图片 评论
暂无图片 有用 0
NIU
题主
2023-02-07
图片中执行计划多的是别名,执行计划少的是表名,两者有什么区别?
Thomas

SJL_CARD_BANK有多少条记录,DETAIL_TYPE字段的取值有几种?

暂无图片 评论
暂无图片 有用 0
NIU
题主
2023-02-07
信息已更新,您看看
NIU

SJL_CARD_BANK表返回行数,--19956577

detail_type --19582251  


索引信息:




暂无图片 评论
暂无图片 有用 0
Thomas

account_id在两个表中都是UNIQUE或者是PRIMARY KEY么?

暂无图片 评论
暂无图片 有用 0
Thomas

我看了,似乎account_id在SJL_CARD_BANK里还不唯一是吧,有NON-UNIQUE 索引account_id_index。那么两个表的主键各自是什么

暂无图片 评论
暂无图片 有用 0
Thomas

即使在SJL_CARD_BANK里account_id不唯一,但起码account_id和cardo_no的组合索引应该唯一吧。XSH_VALID_CARD有多少条记录,把它的索引也列一下。另外。这个查询where里也没有拿ope_date做限制,等于是把整个表都拿出来查,好像不大对头。难道每次这个任务是清空SALE_CARD_BALANCE再重新插入么?

暂无图片 评论
暂无图片 有用 0
Thomas

推测XSH_VALID_CARD里应该也有CARDO_NO字段,那么on条件里写:ON A.ACCOUNT_ID = B.ACCOUNT_ID and A.CARD_NO=B.CARD_NO可否?如果一个人在该行先后办了两张卡,那么两次的account_id一样吗

暂无图片 评论
暂无图片 有用 0
NIU


问:account_id在两个表中都是UNIQUE或者是PRIMARY KEY么?

答:都不是,就是普通的列

问:我看了,似乎account_id在SJL_CARD_BANK里还不唯一是吧,有NON-UNIQUE 索引account_id_index。那么两个表的主键各自是什么

答:SJL_CARD_BANK没有设置主键,XSH_VALID_CARD主键是ID列


问:即使在SJL_CARD_BANK里account_id不唯一,但起码account_id和cardo_no的组合索引应该唯一吧。XSH_VALID_CARD有多少条记录,把它的索引也列一下。另外。这个查询where里也没有拿opt_date做限制,等于是把整个表都拿出来查,好像不大对头。难道每次这个任务是清空SALE_CARD_BALANCE再重新插入么?

答:XSH_VALID_CARD 记录 202865428 具体业务逻辑还要询问应用,

问:推测XSH_VALID_CARD里应该也有CARD_NO字段,那么on条件里写:ON A.ACCOUNT_ID = B.ACCOUNT_ID and A.CARD_NO=B.CARD_NO可否?如果一个人在该行先后办了两张卡,那么两次的account_id一样吗?

答:
您说的on 条件,多一个也无所谓,执行计划没有变化

我查询了一下,SJL_CARD_BANK 表中的crad_no和account_id是多对一的关系

select count(account_id) from keeper.SJL_CARD_BANK; --19952355

select count(*) from keeper.SJL_CARD_BANK; --19956577
除非account_id为0 会出现一致情况。

而且account_id列的值占总数据的24%就是说account_id为0的就有4967074行记录,另外那个表XSH_VALID_CARD比较大,没有查。



暂无图片 评论
暂无图片 有用 0
Thomas

只能给些建议,谨供参考:

两表都把account_id和card_no设置为组合索引。查询时on a.account_id=b.account_id and a.card_no=b.card_no,看效率是否有提升。

SALE_CARD_BALANCE的刷新应该是定时的吧。不知频率怎样。建议不要这样刷,而是用物化视图方式:

create materialized view log on UNITED.XSH_VALID_CARD with rowid;

create materialized view log on KEEPER.SJL_CARD_BANK with rowid;


第一个MV:

CREATE MATERIALIZED VIEW MV_CARD_SUM_AMOUT
REFRESH FAST ON DEMAND

AS SELECT ACCOUNT_ID,CARD_NO,SUM(AMOUNT) AS SUM_AMOUNT FROM UNITED.XSH_VALID_CARD GROUP BY  ACCOUNT_ID,CARD_NO;

首次创建MV会很慢,但以后刷新会很快,这应该是增量刷新,刷新语法自行百度。

第二个MV(用于替代表SALE_CARD_BALANCE):

CREATE MATERIALIZED VIEW MV_SALE_CARD_BALANCE

REFRESH COMPLETE ON DEMAND

SELECT
A.ACCOUNT_ID,
A.CARD_NO,

B.SUM_AMOUT,

... FROM KEEPER.SJL_CARD_BANK A JOIN UNITED.MV_CARD_SUM_AMOUNY B

ON A.ACCOUNT_ID = B.ACCOUNT_ID AND A.CARD_NO=B.CARD_NO 
and  A.SALE_SYS = 'new'
AND A.DETAIL_TYPE != '废卡'.......(原来的GROUP BY应该可以去掉了)

这是个全量刷新, 待创建完MV_CARD_SUM_AMOUT再运行该MV创建语句,待每次增量刷新完MV_CARD_SUM_AMOUT,再刷新下此MV。

暂无图片 评论
暂无图片 有用 0
NIU
题主
2023-02-08
我看看,根据您的说 我研究研究 感谢
回答交流
提交
问题信息
请登录之后查看
邀请回答
暂无人订阅该标签,敬请期待~~
暂无图片墨值悬赏