一段业务逻辑,需要先判断一条记录在数据库中是否有存在,若存在则更新该记录,若不存在则插入记录。
应用之前的做法是:
1、先用条件判断记录在数据库中的个数。
2.1、若count(*)>0,则执行UPDATE操作。
2.2、若count(*)=0,则执行INSERT操作。
或
1、先插入记录。
2.1、若报ORA-001主键错误,则存在记录,此时执行UPDATE操作。
2.2、若无报错,认为插入完成。
以上两种方法,我认为都可以实现这种业务逻辑,区别在于第二种方法可能只需要一次SQL操作,前提是大部分记录都不存在,如果大部分操作都是UPDATE操作,可以这么改:
1、先更新。
2.1、若更新条数>0,则存在记录,执行完成。
2.2、若更新条数=0,则不存在记录,执行INSERT操作。
以上逻辑最差的情况就是需要执行两次SQL,如果数据量不大,则可以忽略消耗时间,但如果是大表,可能消耗就会翻倍。针对这种情况,或许可以考虑使用merge。一般使用merge都是用来将一个表数据导入另一个表,但他可以对同一个表操作,例如:
需求:RULE_COLLISION表:根据app_name、rule_id和start_time更新collision_count字段,或直接插入一条新的记录。
SQL:
merge into RULE_COLLISION t1
using (SELECT 'TEST' app_name, 'TIMELIMIT_COMPONENT' MODULE, '规则一' RULE_ID, 3 COLLISION_COUNT, to_date('2014-07-21', 'yyyy-mm-dd') start_time from dual) t2
on (t1.app_name = t2.app_name AND t1.rule_id = t2.rule_id AND t1.start_time = t2.start_time)
when matched then
update SET t1.collision_count = t2.collision_count
when not matched then
insert values (t2.app_name, t2.MODULE, t2.RULE_ID, t2.COLLISION_COUNT, t2.start_time);
----------------------------------------------------------
Plan hash value: 3989089639
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 1 | 322 | 2 (0)| 00:00:01 |
| 1 | MERGE | RI_RULE_COLLISION_DETAIL | | | | |
| 2 | VIEW | | | | | |
| 3 | NESTED LOOPS OUTER | | 1 | 224 | 2 (0)| 00:00:01 |
| 4 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| RI_RULE_COLLISION_DETAIL | 1 | 224 | 0 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | RULE_COLLISION_ID | 1 | | 0 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("T1"."APP_NAME"(+)='GALT' AND "T1"."RULE_ID"(+)='规则一' AND "T1"."START_TIME"(+)=TO_DATE(' 2014-07-21 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
3 db block gets
2 consistent gets
0 physical reads
0 redo size
829 bytes sent via SQL*Net to client
1315 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed
这里用到的是NESTED LOOPS OUTER,不是HASH JOIN,如果是两表操作是否就不同了?这块后面还需要实验研究下,有高手可以请教请教!
应用之前的做法是:
1、先用条件判断记录在数据库中的个数。
2.1、若count(*)>0,则执行UPDATE操作。
2.2、若count(*)=0,则执行INSERT操作。
或
1、先插入记录。
2.1、若报ORA-001主键错误,则存在记录,此时执行UPDATE操作。
2.2、若无报错,认为插入完成。
以上两种方法,我认为都可以实现这种业务逻辑,区别在于第二种方法可能只需要一次SQL操作,前提是大部分记录都不存在,如果大部分操作都是UPDATE操作,可以这么改:
1、先更新。
2.1、若更新条数>0,则存在记录,执行完成。
2.2、若更新条数=0,则不存在记录,执行INSERT操作。
以上逻辑最差的情况就是需要执行两次SQL,如果数据量不大,则可以忽略消耗时间,但如果是大表,可能消耗就会翻倍。针对这种情况,或许可以考虑使用merge。一般使用merge都是用来将一个表数据导入另一个表,但他可以对同一个表操作,例如:
需求:RULE_COLLISION表:根据app_name、rule_id和start_time更新collision_count字段,或直接插入一条新的记录。
SQL:
merge into RULE_COLLISION t1
using (SELECT 'TEST' app_name, 'TIMELIMIT_COMPONENT' MODULE, '规则一' RULE_ID, 3 COLLISION_COUNT, to_date('2014-07-21', 'yyyy-mm-dd') start_time from dual) t2
on (t1.app_name = t2.app_name AND t1.rule_id = t2.rule_id AND t1.start_time = t2.start_time)
when matched then
update SET t1.collision_count = t2.collision_count
when not matched then
insert values (t2.app_name, t2.MODULE, t2.RULE_ID, t2.COLLISION_COUNT, t2.start_time);
通过伪表dual,实现RULE_COLLISION表的自我更新或插入,这种做法和上面逻辑都是相同的,但这样只会执行一次SQL,如下是执行计划:
Execution Plan----------------------------------------------------------
Plan hash value: 3989089639
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 1 | 322 | 2 (0)| 00:00:01 |
| 1 | MERGE | RI_RULE_COLLISION_DETAIL | | | | |
| 2 | VIEW | | | | | |
| 3 | NESTED LOOPS OUTER | | 1 | 224 | 2 (0)| 00:00:01 |
| 4 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| RI_RULE_COLLISION_DETAIL | 1 | 224 | 0 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | RULE_COLLISION_ID | 1 | | 0 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("T1"."APP_NAME"(+)='GALT' AND "T1"."RULE_ID"(+)='规则一' AND "T1"."START_TIME"(+)=TO_DATE(' 2014-07-21 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
3 db block gets
2 consistent gets
0 physical reads
0 redo size
829 bytes sent via SQL*Net to client
1315 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed
这里用到的是NESTED LOOPS OUTER,不是HASH JOIN,如果是两表操作是否就不同了?这块后面还需要实验研究下,有高手可以请教请教!