hr 계정의 employees 테이블을 다음과 같이 백업합니다.
create table employees_backup
as
select * from employees;
그리고 update 문을 이용해서 employees_backup 테이블의 데이터 일부를 수정합니다.
수정은 여러분들이 마음껏 수정하시면 됩니다.
수정할때 not null 걸려있는 애들은 null 로 못바꿈
그리고 짝꿍의 employees_backup 테이블과 나의 employees_backup 테이블의 데이터의 차이를 확인합니다.
차이가 나는 데이터가 무엇인지 확인하세요. 양쪽으로 다 확인하세요 !
(minus 두가지 경우로 다 써서 확인해보기)
merge
문은 특정 테이블에 insert, update, delete를 한번에 수행하는 명령어입니다.문제 466.
운영 db와 테스트db가 여러분들 컴퓨터에 하나의 db에 있다고 가정하고 하기 위해서
두개의 테이블을 아래와 같이 생성하세요
- 운영 db 테이블
create table operate_emp as select * from emp;
- 테스트 db 테이블
create table test_emp as select * from emp;
문제 467. test_emp테이블에서 직업이 SALESMAN 과 MANAGER 인 사원들의 데이터를 지우시오
delete from test_emp where job in ('SALESMAN','MANEGER') ;
문제 468. test_emp 테이블에서 직업이 ANALYST 와 CLERK 인 사원들의 월급을 0 으로 변경하시오
update test_emp set sal = 0 where job in ('ANALYST','CLERK');
commit;
문제 469. (성해보여주기)
operate_emp 테이블로 test_emp 테이블을 merge 하는데,
operate_emp 테이블에는 존재하는 데이터인데 test_emp 테이블에는 존재하지 않는 데이터는 test_emp 테이블에 입력하고,
양쪽 다 존재하는데 데이터가 틀리면 operate_emp 의 데이터로 test_emp 에 업데이트하시오 (?)merge into test_emp t using operate_emp o on ( t.empno = o.empno ) -> 연결고리 (join 조건) when matched then -> 연결고리에 의해서 매치된다면 update set t.sal = o.sal -> 월급을 업데이트해라 t.comm = o.comm (이런식으로 여러개도 쓸 수 있음!) when not matched then -> empno가 매치되지 않는건, insert (t.empno, t.ename, t.job, t.sal, t.comm, t.hiredate, t.mgr, t.deptno ) values (o.empno, o.ename, o.job, o.sal, o.comm, o.hiredate, o.mgr, o.deptno ); -> test 데이터에 operate 데이터를 입력해라
- 다소 문법이 복잡해보이지만 성능이 좋아서 merge 문을 많이 사용한다.
- truncate 시키고 다시 가져오는 것보다는 merge로 몇개씩 바꾸는게 더 낫다.
지금은 sal 이 다르다는걸 알아서 t.sal=o.sal 이렇게 쓰는데 만약 어떤 데이터가 다른지 모른다면, 아예 모든 데이터를 다 써줘도 된다. 그러면 자기가 알아서 검색 해본다음에 업데이트 시킨다.
t.sal=o.sal t.comm=o.comm...
문제 470.
우리반 테이블을 emp17_backup 으로 백업하세요.create table emp17_backup as select * from emp17;
문제 471.
emp17 테이블의 데이터를 아래와 같이 지우시오delete from emp17 where rownum < 10; commit;
백업과 본 테이블을 맞춰주려면, 가장 간단한 방법은 truncate 하고 불러오는게 간단하긴 하지만, 데이터가 많다면 너무 오래걸리기 때문에 merge 하는게 좋다.
문제 472. (요거 꼭 확인해보삼삼)
emp17_backup 의 내용으로 emp17의 데이터를 merge 하시오
(같은 데이터라면 update 하고, 없으면 insert 하시오)merge into emp17_ e using emp17_backup s on ( e.empno = s.empno ) when matched then update set e.ename = s.ename, e.gender = s.gender, e.birth = s.birth, e.age = s.age, e.telecom = s.telecom, e.email = s.email, e.major = s.major, e.address = s.address when not matched then insert (e.empno, e.ename, e.gender, e.birth, e.age, e.telecom, e.email, e.major, e.address ) values (s.empno, s.ename, s.gender, s.birth, s.age, s.telecom, s.email, s.major, s.address );
- empno 는 위에 매치조건이기때문에 update 에 안써줌 (이미 매치가 되어야 이게 작동한다는 조건이기 때문에, 매치가 된다는 가정하임!!)
-> on 절에 쓴거는 update 에 쓰면 오류남!
문제 473.
이번에는 emp17 테이블을 truncate 시키고 emp17backup 으로 데이터를 입력하시오
(truncate 는 조심해서 써야함,,,ㅠㅠ)truncate table emp17; insert into emp17 select * from emp17_backup;
- merge 하려는 2개의 테이블 사이의 데이터의 변경의 차이가 심하고
둘다 대용량 테이블이 아니면 위의 방법truncate
이 더 간단하긴함!
하지만 truncate 의 부담감이 있음
-> 일반 개발자들은 DBA 한테 truncate 의 권한을 안주는 경우가 많음
문제 474. market_2022 테이블을 백업하세요
create table market_2022_backup as select * from market_2022;
문제 475. market_2022 테이블의 데이터를 일부 지우시오
(대용량 테이블이라 시간 좀 걸릴것)delete from market_2022 where rownum < 156405; commit;
문제 476. market_2022 테이블의 상호명을 전부 null 로 변경하시오
update market_2022 set 상호명 = null ; commit;
문제 477. market_2022 테이블의 데이터를 market_2022_backup 테이블의 데이터로 merge 하시오 (밑에 하는 방법 적어놓음)
merge into market_2022 a using market_2022_backup b on ( a.상가업소번호 = b.상가업소번호 ) when matched then update set a.상호명=b.상호명, a.지점명=b.지점명, a.상권업종대분류코드=b.상권업종대분류코드, a.상권업종대분류명=b.상권업종대분류명, a.상권업종중분류코드=b.상권업종중분류코드, a.상권업종중분류명=b.상권업종중분류명, a.상권업종소분류코드=b.상권업종소분류코드, a.상권업종소분류명=b.상권업종소분류명, a.표준산업분류코드=b.표준산업분류코드, a.표준산업분류명=b.표준산업분류명, a.시도코드=b.시도코드, a.시도명=b.시도명, a.시군구코드=b.시군구코드, a.시군구명=b.시군구명, a.행정동코드=b.행정동코드, a.행정동명=b.행정동명, a.법정동코드=b.법정동코드, a.법정동명=b.법정동명, a.지번코드=b.지번코드, a.대지구분코드=b.대지구분코드, a.대지구분명=b.대지구분명, a.지번본번지=b.지번본번지, a.지번부번지=b.지번부번지, a.지번주소=b.지번주소, a.도로명코드=b.도로명코드, a.도로명=b.도로명, a.건물본번지=b.건물본번지, a.건물부번지=b.건물부번지, a.건물관리번호=b.건물관리번호, a.건물명=b.건물명, a.도로명주소=b.도로명주소, a.구우편번호=b.구우편번호, a.신우편번호=b.신우편번호, a.동정보=b.동정보, a.층정보=b.층정보, a.호정보=b.호정보, a.경도=b.경도, a.위도=b.위도 when not matched then insert (a.상가업소번호,a.상호명,a.지점명,a.상권업종대분류코드,a.상권업종대분류명,a.상권업종중분류코드,a.상권업종중분류명,a.상권업종소분류코드,a.상권업종소분류명,a.표준산업분류코드,a.표준산업분류명,a.시도코드,a.시도명,a.시군구코드,a.시군구명,a.행정동코드,a.행정동명,a.법정동코드,a.법정동명,a.지번코드,a.대지구분코드,a.대지구분명,a.지번본번지,a.지번부번지,a.지번주소,a.도로명코드,a.도로명,a.건물본번지,a.건물부번지,a.건물관리번호,a.건물명,a.도로명주소,a.구우편번호,a.신우편번호,a.동정보,a.층정보,a.호정보,a.경도,a.위도 ) values (b.상가업소번호,b.상호명,b.지점명,b.상권업종대분류코드,b.상권업종대분류명,b.상권업종중분류코드,b.상권업종중분류명,b.상권업종소분류코드,b.상권업종소분류명,b.표준산업분류코드,b.표준산업분류명,b.시도코드,b.시도명,b.시군구코드,b.시군구명,b.행정동코드,b.행정동명,b.법정동코드,b.법정동명,b.지번코드,b.대지구분코드,b.대지구분명,b.지번본번지,b.지번부번지,b.지번주소,b.도로명코드,b.도로명,b.건물본번지,b.건물부번지,b.건물관리번호,b.건물명,b.도로명주소,b.구우편번호,b.신우편번호,b.동정보,b.층정보,b.호정보,b.경도,b.위도 );
(문제 477 해설)
1. 먼저 column 리스트를 알아야한다. (다 써야하는데 일일히 하나하나 쓰긴 복잡하니까)
select * from dictionary where table_name like '%DBA%TAB%COLUMN%'; (대문자로!!)
-> 사전 리스트 중에 column 을 포함하고 있는 사전들을 보여달라는 문법
-> 유저 테이블의 컬럼들을 보여준다는 사전
그 다음,
select column_name
from dba_tab_columns
where table_name='MARKET_2022';
위의 사전이름을 from 에 쓰면
라고 컬럼 이름이 쭉 뜬다.
2. 연결연산자로 update 문법 만들어주기
select 'a.' || column_name || '=' || 'b.' || column_name || ',' from dba_tab_columns where table_name='MARKET_2022';
연결연산자 사용해서, 'update set' 에 들어갈 업데이트 될 컬럼문을 만들어준다.
이중에서 상가업소번호는 빼기~! 맨 마지막 ',' 도 빼기!
3. 연결연산자 + listagg 사용해서 insert 문법 만들어주기
그다음 insert 문 ( , , , ) 만들기
select 'a.' || column_name || ','
이렇게 해도 되지만 보기 안좋아서,
listagg
사용하기select listagg ('a.' || column_name ,',') from dba_tab_columns where table_name='MARKET_2022';
그다음 b 도 똑같이 만들어줘야함select listagg ('b.' || column_name ,',') from dba_tab_columns where table_name='MARKET_2022';
4. 구한 정보들 입력하기
그리고 merge 문에 하나씩 입력하면 됨!
merge into test_emp t using operate_emp o on ( t.empno = o.empno ) -> 연결고리 (join 조건) when matched then -> 연결고리에 의해서 매치된다면 update set t.sal = o.sal -> 월급을 업데이트해라 t.comm = o.comm (이런식으로 여러개도 쓸 수 있음!) when not matched then -> empno가 매치되지 않는건, insert (t.empno, t.ename, t.job, t.sal, t.comm, t.hiredate, t.mgr, t.deptno ) values (o.empno, o.ename, o.job, o.sal, o.comm, o.hiredate, o.mgr, o.deptno ); -> test 데이터에 operate 데이터를 입력해라
※ 위와 같이 dba_ 로 시작하는 테이블들이 바로 데이터 사전 관련한 테이블들 입니다.
문제 478. c##scott 유저가 소유하고 있는 테이블 리스트를 확인하시오
select * from user_tables;
user_tables
: c##scott 유저가 소유하고 있는 테이블 리스트를 확인all_tables
: c##scott 유저가 소유하고 있는 테이블 + 엑세스 권한 받은 테이블들 확인dba_tables
: db에 있는 모든 테이블 리스트를 다 확인 (only dba 의 사전)뒤에 tables 를 붙인곳에 다른거 넣어서 다르게 활용할 수 있음!!
ex) dbatab_columns, user어쩌구
1) user_tab_columns : c##scott 유저가 소유하고 있는 테이블들의 컬럼정보를 확인하는 사전
2) all_tab_columns : c##scott 유저가 소유하고 있는 테이블 + 엑세스 권한 받은 테이블들의 컬럼정보를 확인하는 사전
3) dba_tab_columns : db에 있는 모든 테이블들의 컬럼정보 확인하는 사전
위의 데이터 사전들이 뭐가 있는지 외울 필요는 없고!!
아래의 쿼리를 조회해서 필요한 사전들을 찾아냅니다.
select *
from dictionary
where table_name like '%보고싶은키워드를 대문자로%'
ex. 컬럼정보가 보고싶다면,
select *
from dictionary
where table_name like '%COLUMN%'
ex2. db를 포함하고, tab을 포함하고, column 을 포함하는 데이터 사전을 보여줘!
->
select *
from dictionary
where table_name like '%DB%TAB%COLUMN%'
% 로 연결해서 쭉쭉 써주면 됨
※ 위와 같이 merge 문을 이용하지 않는다면,
다른 방법으로 두 테이블 사이에 데이터의 일치를 맞춰주는 SQL이 뭐가 있는가?
-> 상호관련 서브쿼리를 사용한 update 문 (악성 sql임, 성능 bad 인데 개발자들이 많이씀)
(오후에 설명해주시기로)
lock : 세션(유저)이 특정 데이터를 수정하게 되면 commit 하기 전까지 수정한 행(row) 에 lock을 겁니다.
특정 세션이 변경한 데이터를 다른 세션이 변경하지 못하도록 막는 기능.
만약 lock 이 없다면 데이터의 일관성이 깨집니다.
A session B session
(C##SCOTT) (C##SCOTT)
1. update emp
set sal = 9000
where ename='KING';
2. update emp
set sal = 0
where ename = 'KING';
commit;
3. select ename, sal
from emp;
-> 0 으로 보임
2 의 결과가 반영되지 않게 하기 위해서 행(rows) 에 lock 을 건다. (행 전체에!)
왼쪽 먼저 실행되고, 자동으로 lock 걸려서 오른쪽 실행 안됨
-> 왼쪽 commit 하고 나니까 오른쪽 lock 풀려서 실행됨!
select decode(status,'INACTIVE',username || ' ' || sid || ',' || serial#,'lock') as Holder,
decode(status,'ACTIVE', username || ' ' || sid || ',' || serial#,'lock') as waiter, sid, serial#, status
from( select level as le, NVL(s.username,'(oracle)') AS username,
s.osuser,
s.sid,
s.serial#,
s.lockwait,
s.module,
s.machine,
s.status,
s.program,
to_char(s.logon_TIME, 'DD-MON-YYYY HH24:MI:SS') as logon_time
from v$session s
where level>1
or EXISTS( select 1
from v$session
where blocking_session = s.sid)
CONNECT by PRIOR s.sid = s.blocking_session
START WITH s.blocking_session is null);
alter system kill session '124,9709';
124 : sid번호
9709 : 시리얼번호
->
System KILL이(가) 변경되었습니다.
-> lock 풀린것 -> 테이블 볼려고 하면세션이 종료되었습니다 라고 뜸 (죽어버린것)
-> exit 하고 다시 sqlplus c##scott/tiger로 재접속 해야함 (dos창)
※ 우리가 테이블의 데이터를 update 하면 lock 이 행단위로 걸립니다.
그리고 반드시 db 에 데이터 수정 작업을 했으면 꼭 commit 을 해서
마무리를 잘 해야한다. 안그러면 다른 세션들이 lock에 걸리게 된다.
문제 479.
아래와 같이 c##scott 세션을 2개를 열고 update 를 수행하시오c##scott (A session) c##scott (B session) 1. update emp set sal = 9000 where job='SALESMAN'; 2. update emp set sal = 7000 where ename='MARTIN';
martin 이 salesman 이므로 lock 걸려서 업데이트 안됨
문제 480.
이번에는 holder 를 죽이지 마시고 waiter 를 죽이세요
머가 홀더고 뭐가 웨이터..??alter system kill session '616,41832' ;
※ lock 은 행단위로 걸리는것 이긴 하다.. 하지만 테이블 전체에 lock 걸수도 있음
문제 481. emp 테이블 전체에 lock 을 겁니다.
lock table emp in exclusive mode;
그 어떤것도 수정할 수 없음
데이터의 일관성이 깨지지 않기 위해서 필요하다.
문제 482. c##scott 유저로 3개의 세션을 열고 다음과 같이 update 하시오
기다리고있는 세션들.. 2,3 세션 (1 세션 commit 전) = enQueue
(queue)
-> 1 세션이 commit 하고 나면 2는 빠져나온다 = deQueue
LOCK 은 update 문을 수행할 때 update를 하려는 행들에 자동으로 lock이 걸리게 되는데
select 를 할 때도 lock 을 걸 수 있습니다.
보통은 select 를 할 때는 lock 이 안걸린다.
A session B session
1. select ename, sal
from ~~~
where job='SALESMAN'; 2. select ename, sal
from ~~~~
where ename='MARTIN';
이렇게 보통 select 는 lock 이 걸리지 않는다. 2가 작동된다 (단순 보는 것이기 때문)
그런데,
지금부터 특정 테이블의 데이터를 집계를 시작 해야하는데
그 누구도 내가 select 한 데이터를 수정못하게 해야한다면?
select ... for update;
select for 문을 사용하면 된다!
A session B session
1. select ename, sal
from emp
where job='SALESMAN' 2. update emp
for update; set sal=0
where ename='MARTIN';
이렇게 했을때 B는 아무 반응X (lock걸림)
A 에서 commit 하면 B 락 풀림!
문제 483. 위의 작업을 다시 한번 수행하고 sql developer 창에서
lock holder 와 lock waiter 를 확인하시오
에서도 죽일 수 있음 (sql 아니고 tool로 죽이는 방법)
마우스 오른쪽 눌러서 세션종료
확인해보면 죽었음
예제. 아래의 테이블을 생성하시오
create table emp900
(empno number(10),
ename varchar2(10),
sal number(10) );
예제2. emp900 테이블에 아래의 데이터를 입력하시오
empno : 9384
ename : jack
sal : 3000
insert into emp900 (empno, ename, sal)
values (9384, 'JACK', 3000)
예제3. 서브쿼리를 사용한 insert 문으로 사원 테이블의 직업이 'SALESMAN' 인 사원들의 사원번호, 이름, 월급을 emp900에 입력하시오
insert into emp900 (empno, ename, sal)
select empno, ename, sal
from emp
where job='SALESMAN';
select * from emp900;
※ 위의 서브쿼리를 사용한 insert 문은 data 이행할 때 사용하는 SQL 입니다. (마이그레이션 = 데이터 옮길때)
문제 484. 짝꿍의 data를 나의 db에 이행합니다.
짝꿍의 sales 데이터를 나의 db에 이행하기 위해서
먼저 sales 테이블의 구조를 생성합니다.create table my_sales as select * from sh.sales where 1 = 2;
1=2 때문에 데이터는 못가져오고 테이블 구조만 생성
문제 485. 짝꿍의 sales 테이블의 데이터를 나의 my_sales 테이블에 이행합니다.
insert into my_sales select * from sh.sales@dblink21;
select count(*) from my_sales;
update --> 서브쿼리 가능
set --> 서브쿼리 가능
where --> 서브쿼리 가능
문제 486. 사원테이블에 ALLEN 보다 늦게 입사한 사원들의 월급을 9000으로 변경하시오
update emp set sal = 9000 where hiredate > (select hiredate from emp where ename='ALLEN');
문제 487. SCOTT 과 같은 부서번호에서 일하는 사원들의 월급을 8000으로 수정하시오
update emp set sal = 8000 where deptno = (select deptno from emp where ename='SCOTT');
문제 488. KING의 월급을 ALLEN의 월급으로 변경하시오
update emp set sal = (ALLEN 의 월급 ) where ename = 'KING' ;
'allen' 의 월급을 구하는 서브쿼리
update emp set sal = ( select sal from emp where ename='ALLEN') where ename = 'KING' ;
문제 489. KING의 부서번호를 JAMES 의 부서번호로 변경하시오
update emp set deptno = ( select deptno from emp where ename='JANES') where ename = 'KING' ;
배운거라 그냥 해본거고, 주로 '상호관련서브쿼리' 사용함
update 옆의 메인 테이블이름이 서브쿼리의 where 절에 들어가게 하는 (?)
emp와 dept를 깔끔하게 새로 생성하시오
https://cafe.daum.net/oracleoracle/Sdyr/846
문제 490.
emp테이블에 loc컬럼을 추가하시오alter table emp add loc varchar2(10);
문제 491. 지금 추가한 loc 컬럼에 해당 사원의 부서위치로 값을 갱신하시오
(merge 문 말고 상호관련 서브쿼리 를 이용한 update 문으로 수행)update emp e set loc = (select loc from dept d where d.deptno = e.deptno);
emp e 데이터 가 서브쿼리 안으로 하나씩 들어와서 계산되는것
※ emp 테이블의 부서번호가 서브쿼리의 set절의 서브쿼리에 하나씩 들어가면서
하나씩 갱신하기 때문에 시간이 오래걸린다.
-> merge 문으로 하는게 시간이 훨씬 빠르다.
alter table emp
drop column loc;
(컬럼 드랍 시키는 sql)
문제 492.
사원테이블에 deptavg (부서평균) 라는 컬럼을 추가하시오alter table emp add deptavg number (10,2);
전체 숫자 10자리, 소숫점은 2자리까지 허용
문제 493.
deptavg 컬럼에 값을 갱신하는데, 해당 사원이 속한 부서번호의 평균 월급으로 값을 갱신하시오update emp e set deptavg = (select avg(sal) from emp s where s.deptno = e.deptno) ;
악성 sql 이긴 하다..
문제 494. (위의 답 튜닝하기!!)
위의 SQL을 튜닝하시오 (merge
문으로 수정하시오)merge into EMP e using ( select deptno, avg(sal) as 부서평균 from emp group by deptno ) v on (e.deptno = v.deptno ) when matched then update set e.deptavg = v.부서평균
여러번 튜닝하지않고 툭!
문제 495.
emp 테이블에 grade 컬럼을 추가하시오alter table emp add grade number(10);
문제 496.
salgrade 테이블을 사용하여 emp 테이블의 grade 컬럼을 merge 하는데
해당사원의 급여등급으로 값을 merge 하시오 (튜닝후)merge into emp e using salgrade s on (e.sal between s.losal and s.hisal) when matched then update set e.grade = s.grade;
문제 497.
위의 merge 문을 상호관련 서브쿼리를 사용한 update 문으로 작성하시오 (튜닝전)update emp e set grade = (select grade from salgrade s where e.sal between s.losal and s.hisal);
문제 498.
merge 문이 튜닝 후 SQL임을 증명하기 위해서 대용량 테스트 테이블을 다음과 같이 생성하시오create table sales100 as select rownum as num, amount_sold from sales;
실습 환경만들기
create table sales100
as
select *
from sh.sales;
-> sales100 이라는 테이블을 만드는데 내용을 sh.sales 에서 가져와서 만든다!
create table sales200
as
select rownum rn, prod_id, cust_id, time_id, channel_id,
promo_id, quantity_sold, amount_sold
from sales100;
-> sales200 테이블을 만드는데 select에 있는 컬럼들만 만든다
데이터는 sales100 에서 가져온다!
alter table sales200
add date_id date;
-> sales200에 컬럼을 추가한다. 컬럼명은 date_id (내용은 비어있다)
create table time2
( rn number(10),
date_id date );
-> time2 라는 테이블을 만드는데, rn 컬럼과 (숫자형태10자리), date_id (날짜형태) 라는 컬럼을 만든다. 내용은 비어있음
begin
for i in 1 .. 918843 loop
insert into time2
values( i , to_date('1961/01/02','YYYY/MM/DD')+ i );
end loop;
end;
/
-> 이런내용을 time2 에 넣을건데, 너무 많으니까 loop 걸어서 내용 넣어주겠음
commit;
time2 의 date_id 로 sales200에 갱신하기
sales200 의 time_id는 비어있는 상태 (컬럼만 만들어져있음)
rn 도 테이블명
문제 499. 튜닝 후 (merge)
time2 테이블을 사용하여 sales200 테이블을 merge 하는데,
time2 의 time_id 로 sales200 의 time_id를 갱신하시오.merge into sales200 using time2 t on (s.rn = t.rn) when matched then update set s.date_id=t.date_id;
set timing on -> 시간을 보는 문법
set timing on
문제 500. (오늘의 마지막 문제)
위의 SQL의 튜닝전 SQL을 작성하시오
상호관련 서브쿼리를 이용한 update문으로 작성하시오