Insert Multiple - Oracle(Tibero) 포팅 삽질기
DB 포팅을 진행할 때 insert multiple 부분에 대한 삽질기
목차
- 문제
- 삽질 과정 - 1: insert multiple with sequence
- 삽질 과정 - 2: mybatis malfunction useGeneratedKeys
- 해결 방안
- 고찰 1 (insert의 방법)
- 고찰 2 (prepared statement와 foreach insert)
문제
PostgreSQL/MySQL/MariaDB에서 지원되는 쿼리를 Oracle(Tibero) DBMS로 포팅을 진행한다.
문제의 쿼리는 아래와 같다.
<insert id="insertServiceCodeList" parameterType="serviceCodeVo" useGeneratedKeys="true" keyColumn="code" keyProperty="code">
insert into service_code (
serv_info_seq_id, name,
default_key_type,
cre_date,
creator,
description,
state
) values
<foreach collection="list" item="item" separator=",">
(
#{item.servInfoSeqId},
#{item.name},
#{item.defaultKeyType},
now(),
#{item.creator},
#{item.description},
0
)
</foreach>
</insert>
DDL seq_id의 Default는 sequence.nextval 또는 auto_increment이다.
삽질 과정 - 1: insert multiple with sequence
- Oracle은 위의 쿼리 지원안함.
- Oracle은 insert all을 해야함
- 근데 insert all 할 때 sequence.nextval이 지원안됨 ㅡㅡ(insert into 단일건에 대해서만 지원함)
- 이것에 대해서는 2가지 해결 방법이 있음.
- 해결방안 1
Create Sequence service_code_seq;
create or replace function get_seq( seq_name in varchar2 )
return
number
is
v_num number;
sql_stmt varchar2(64);
begin
sql_stmt := 'select ' || seq_name || '.nextval from dual';
execute immediate sql_stmt into v_num;
return v_num;
end;
<insert id="insertServiceCodeList" parameterType="serviceCodeVo" useGeneratedKeys="true" keyColumn="code" keyProperty="code">
insert all
<foreach collection="list" item="item" >
into service_code (
code,
serv_info_seq_id,
name,
default_key_type,
cre_date,
creator,
description,
state
) values (
get_seq( 'service_code_seq' ),
#{item.servInfoSeqId},
#{item.name},
#{item.defaultKeyType},
systimestamp,
#{item.creator},
#{item.description},
0
)
</foreach>
select * from dual;
</insert>
- 해결방안 2
<insert id="insertServiceCodeList" parameterType="serviceCodeVo" useGeneratedKeys="true" keyColumn="code" keyProperty="code">
insert into service_code (
code,
serv_info_seq_id,
name,
default_key_type,
cre_date,
creator,
description,
state
) select
service_code_seq.nextval,
servInfoSeqId,
name,
defaultKeyType,
systimestamp,
creator,
description,
0
from (
<foreach collection="list" item="item" separator="union all">
select
#{item.servInfoSeqId} servInfoSeqId,
#{item.name} name,
#{item.defaultKeyType} defaultKeyType,
#{item.creator} creator,
#{item.description} description
from
dual
</foreach>
)
</insert>
삽질 과정 - 1 - 결론과 동시에 문제
- 위의 해결방안 1,2 insert multiple with sequence 가능함.
- 해결방안 2는 추천안함( SQL 실행계획을 분석 하면 인라인뷰를 만들고/병합하느냐고 DB cost 잡아먹음 ㅋㅋㅋㅋ)
- 문제: MyBatis의 useGeneratedKeys가 지원안됨 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
삽질 과정 - 2: mybatis malfunction useGeneratedKeys
- 일단 안됨 그냥 안됨
- Syntax Error 뿜뿜~!!!
- 내가 원하는 것은 시퀀스ID 값을 application에서 리턴받는 것임
삽질 과정 - 2: 결론
- application code 수정은 진짜 정말 싫었지만 결국에 수정이 필수불가결...
위의 과정에 대한 삽질 URL
Is mybatis enable to insert all and return sequence(useGenerateKeys) in oracle?
I'm currently migrating from PostgreSQL to Oracle DBMS. but occur some problem. I wish to work "Insert all with sequence and return sequence to application using usegeneratekeys in mybatis." My
stackoverflow.com
http://www.gurubee.net/article/81896
Tibero insert all Sequence 질문 드립니다.
안녕하세요 현재 Tibero DB 포팅 작업을 진행하고 있습니다. 시퀀스가 있는 테이블에 insert all이 잘 되지 않아 도움 구하고자 게시물 남깁니다. m..
www.gurubee.net
해결 방안
- 일단 나는 Spring 4.x 사용 중
- Spring 기능 중 Profile과 Interface 활용해서 application code를 수정하기로 했음
- Spring Bean 등록을 Component-Scan으로 하기 때문에 *Dao.class의 @Repository 어노테이션을 제거 했음
- 코드는 아래와 같다.
web.xml
<!-- option:oracle/another -->
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>oracle</param-value>
</context-param>
root-context.xml(application-context.xml)
<!-- =================================================================== -->
<!-- Register Repository Beans(Data Access Object) -->
<!-- =================================================================== -->
<beans profile="oracle">
<bean id="ICodeDao" class="***.dao.CodeOracleDao" />
<bean id="***Dao" class="***.dao.***Dao" />
<bean id="***Dao" class="***.dao.***Dao" />
<bean id="***Dao" class="***.dao.***Dao" />
<bean id="***Dao" class="***.dao.***Dao" />
</beans>
<beans profile="another">
<bean id="ICodeDao" class="***.dao.CodeDao" />
<bean id="***Dao" class="***.dao.***Dao" />
<bean id="***Dao" class="***.dao.***Dao" />
<bean id="***Dao" class="***.dao.***Dao" />
<bean id="***Dao" class="***.dao.***Dao" />
</beans>
service.class
@Resource
ICodeDao dao;
빌드 및 운영에서는 web.xml에서의 oracle/another를 지정하면 됨
고찰 1 (insert의 방법)
일단, application code를 수정해야해서 짜증났음;
우리가 왜 multiple insert를 하느냐를 집중해야한다. -> 성능 때문에
약 1년 전 아래와 같은 테스트를 했었다(auto commit = false).
해당 테스트는 건별 insert의 속도 비교이다.
- single insert(prepared statement)
- foreach multiple insert(mybatis)
- bulk insert
결과는 아래와 같다(간단한 내부 테스트이므로 JUnit의 메소드 실행 시간임).
1000건 부터 bulk insert가 효과를 봄 여튼 위의 테스트는 아래 URL 참고해서 작성함
http://blog.harawata.net/2016/04/bulk-insert-multi-row-vs-batch-using.html
Bulk insert (multi-row vs. batch) using JDBC and MyBatis
I have seen some developers are using multi-row insert in MyBatis to perform bulk insert. If performance is your concern, though, you shoul...
blog.harawata.net
고찰 2 (prepared statement와 foreach insert)
일단 SQL에서 multiple insert한 이유는 다음과 같다.
application과 DBMS와의 1번의 Round Trip(1번의 네트워크)을 통해 성능 개선을 위했던거임
근데, 막상 1년 전의 테스트를 보니까 prepared statement로 insert하는게 더 나아보임;;ㅡㅡ
prepared statement의 경우, DBMS의 바인드 변수를 사용하니까 빠른 것 같음..
여기서 궁금한거는 prepared statement와 네트워크 대역폭이 궁금함.. << 이거는 다음에 알아봐야겠음
주저리주저리 끝.
'개발관련 > 삽질' 카테고리의 다른 글
AOP(SpringAOP/AspectJ) (0) | 2019.12.02 |
---|---|
AJP 프로토콜 모든 것을 분석 해보자 (1) | 2019.10.28 |
JWT를 분석하고 사용해보자 (3/3) (0) | 2019.08.13 |
JWT를 분석하고 사용해보자 (2/3) (0) | 2019.08.13 |
JWT를 분석하고 사용해보자 (1/3) (0) | 2019.08.13 |