event.stopPropagation()을 이해하기 위해서는 우선HTML 상에서 사용자 이벤트가 어떻게 전달되는지 이해해야 합니다.
웹에서의 클릭이벤트 전파
propagation 의 사전적의미는 전파, 확산입니다.
사용자가 마우스로 웹페이지 내의 버튼을 클릭했을때 대부분의 사람들은 버튼만 반응했다고 생각하게 됩니다. 하지만 웹페이 내부에서는
버튼을 감싸고 있는 부모 태그들 또한 클릭 이벤트에 반응하게 됩니다. 이것을 Bubble Up(버블업)이라고 합니다.
위 슬라이드 우측의 문서구조를 보면 a 태그는 li, ul 의 두개의 부모태그가 있습니다.
사용자가a 태그를 클릭했을때 클릭이벤트는a 태그를 감싸고 있는 li, ul 태그로 순차적으로 전달되게 됩니다.
즉 a 태그의 onclick -> li 태그의 onclick -> ul 태그의 onclick 을 실행하게 됩니다.
만약에 ul 태그에 클릭 이벤트가 있을 경우 a 태그를 클릭하는 순간에는 의도하지 않은 ul 태그의 클릭 이벤트가 실행됩니다.
예를 들어 설명하면
이웃집에서 이사를 와서 바로 옆집에 살고 있는 철수네 집에 떡을 돌렸다고 칩시다. 철수는 떡을 받고 기쁜 나머지 아버지에게 가져다 줍니다. 철수 왈 "아버지 옆집에서 새로 이사를 왔다고맛있는 떡을 주고 갔어요~" 철수는 효심이 지극했습니다.철수 아버지 또한 효심이 지극하여 받자 마자 바로 철수 할아버지에게 떡을 가져다 줍니다. 철수 아버지 왈 "아버님옆집에서 새로 이사를 왔다고 맛있는 떡을 주고 갔습니다." 이렇게 3부자는 떡을 오손 도손 나눠 먹게됩니다.
그런데 옆집에서 철수에게 떡을 주면서 "이 떡은 꼭 너 혼자만 먹어야 한다" 라고 말하면서 철수에게 떡을 주었다면...철수는 혼자 먹습니다. 효심보다는 떡에 눈이 멀었던 거죠...
이때 혼자만 먹어라는 의미를 stopPropagation 이라고 합니다.
즉,stopPropagation 은 부모태그로의 이벤트 전파를 stop 중지하라는 의미입니다.
event.preventDefault()는?
preventDefault 를 이해하기 위해서는a 태그를 유심히 봐야 합니다. 위 슬라이드에 표시된 a 태그는 내부적으로 href="#" 속성을 가지고 있습니다. href 속성은 웹브라우저에게 a 태그클릭시 이동하여야할 페이지를 나타냅니다.
위 a 태그는 click 이벤트 또한 가지고 있기 대문에a 태그를 클릭했을 때는 두가지 행동을 하게 됩니다.
첫번째click 이벤트를 실행합니다. 두번째는 브라우저에게 href 에 표시된 곳으로 이동하도록 합니다.
href="#" 속성을 넣은 이유는a 태그에는click 이벤트가 있으니 click 이벤트만 실행하고 웹브라우저는 이동하지 말아라 하는 의도로 설정한 값입니다.이렇게 하게 되면 클릭시에는 click 이벤트만 실행되고 웹브라우저가 이동하지는 않게 됩니다. 제가 생각하기에는 좀 꼼수 같기도 합니다.
근데 여기서 한가지 주의해야 할 점이 있습니다. href="#" 은 웹브라우저가 다른 곳으로 이동하지는 않지만 스크롤이 있는 곳에서는 페이지 상단으로 이동하게 됩니다. href="#~~~" 으로 사용하는것을 앵커(닻) 라고 하는데 href="#" 은 웹브라우저의 최상단을 가리키는 앵커입니다.
글을 작성할때나, 회원가입을 할때 버튼 한번 클릭할때마다 페이지가 쭉 위로 올라가는 경험을 해보신분들은 그 짜증스러움을 익히 아시리라 생각합니다. ㅠㅠ
이 브라우저 행동을 막기위해서 사용하는게preventDefault 입니다.
preventDefault 는a 태그처럼 클릭 이벤트 외에 별도의 브라우저 행동을 막기 위해 사용됩니다.
파이썬은 스크립트 언어다? 늘 아, 그렇구나하고 넘어갔던 '스크립트' 언어에 대해 알아보고자 한다.
스크립트 언어는 연극 용어에서 따온 말이다.
연극에서의 '스크립트'는 배우의 대사를 중점으로 배우의 표정, 등장과 퇴장, 무대장치을 중심으로 지시해놓은 내용을 말한다.
아래는 예시를 위해 가져온 스크립트 샘플이다. (심심하면 읽어보시길)
[출처] http://eslab.inha.ac.kr/wcu/xe/?mid=script_sample 4#. 배 난파 며칠이 지나도 멎지 않는 폭풍으로 인하여 배는 항로에서 점점 멀어졌고 그들은 지쳐갔습니다. 바로그때, 한 선원의 목소리가 들려왔습니다. “육지다!! 육지가 보인다!”(갑판에서 육지를 가르키는 한 선원과 그 뒤로 그 곳을 보는 사람들) 그러나 육지를 찾았다는 기쁨도 잠시(쿵!! 배부셔지는소리) 배가 그만 암초에 걸리고 말았습니다.
프로그래밍에서 '스크립트 언어'의 역할을 생각해보면 왜 스크립트라고 했는지 이해가 될 것이다.
프로그래밍의 스크립트 언어는 기존에 이미 존재하는 소프트웨어를 제어하기 위한 용도를 쓰이는 언어이다.
컴파일러를 사용해 실행하면, 기계어로 번역이 된 채로 바로 실행할 수 있다. 하지만, 규모가 큰 프로그램이라면 컴파일에 몇 시간이 걸릴 수 있다. 그렇게 오랜 시간을 들여 컴파일을 했는데, 얼마 안돼 다시 수정을 한다고 하자. 특히 수정을 많이 해야하는 작업이라면, 시간 낭비가 어마어마할 것이다.
반대로, 인터프리터 방식은 소스코드를 직접 바로바로 실행해 디버깅이 편리하다.
스크립트 언어는 인터프리터 방식에 사용하기 위해 나온 것으로, 응용 소프트웨어와 상호작용하며 제어한다.
아래는 자바스크립트 소스코드의 예시다. 자바스크립트를 사용해 웹 브라우저의 실행을 제어할 수 있다.
궁금한 사람은 현재 창에서 개발자도구(F12 또는 우클릭-검사) - console 에서 아래 코드를 입력하면 된다.
window.open('221457352867')
위에서 말했듯이 스크립트 언어의 특징은 새로운 프로그램을 만들기 보다는 '기존'의 프로그램을 제어하기 위해 쓰인다. 웹이 없다면 자바스크립트를 쓰는 의미가 없는 것처럼, 스크립트 언어들은 기존 프로그램을 전제로 사용한다.
파이썬은 프로그램 개발에도 사용되지만, 다른 프로그램 위에서 스크립트 언어로서 사용되기 편리하므로 스크립트 언어라고 부른다.
3. 장단점
<장점>
- 단순하고 쉬운 문법
- 실시간으로 실행
<단점>
- 실행 속도가 느리므로 속도가 중요할 때는 사용을 지양한다.
(백준 알고리즘에서 같은 문제를 C와 Python을 이용해 풀어볼 것. Python이 10초 이상 걸리는 문제를, C는 1초도 안되어 해결한다)
BEGIN OPEN C1; LOOP FETCH C1 INTO VEMPNO, VENAME, VSAL; EXIT WHEN C1%NOTFOUND; IF VSAL>=3000 THEN LEVEL := '높음'; ELSIF VSAL>=2000 THEN LEVEL := '보통'; ELSE LEVEL := '낮음'; END IF; DBMS_OUTPUT.PUT_LINE(VEMPNO|| ' ' ||VENAME|| ' ' ||VSAL|| ' ' ||LEVEL); END LOOP; CLOSE C1; END; /
Q1) 커서를 사용해서 모든 사원의 이름,급여,전체사원의 급여합을 구해서 출력해 보세요.
DECLARE VENAME EMP.ENAME%TYPE; VSAL EMP.SAL%TYPE; TOTSAL NUMBER(7,2) :=0; -- **초기값 지정해야 결과값 도출됨 CURSOR C1 IS SELECT ENAME, SAL FROM EMP;
BEGIN OPEN C1; LOOP FETCH C1 INTO VENAME, VSAL; EXIT WHEN C1%NOTFOUND; TOTSAL := TOTSAL + VSAL; DBMS_OUTPUT.PUT_LINE(VENAME|| ' ' ||VSAL); END LOOP; DBMS_OUTPUT.PUT_LINE('전체사원의 급여합: '||TOTSAL); --LOOP가 끝난 다음에 위치 CLOSE C1; END; /
예3) 각 부서의 급여합 구하기 (PROCEDURE로 만들기) CREATE OR REPLACE PROCEDURE DEPT_SAL IS CURSOR C1 IS SELECT NVL(SAL,0) SAL,DEPTNO FROM EMP; -- NVL ; NULL 값이 있으면(계산자체가안됨) '0'으로 넣어주는 명령어 / 그 옆에 SAL은 ALIAS D1 NUMBER(5):=0; --10번부서합 D2 NUMBER(5):=0; --20번부서합 D3 NUMBER(5):=0; --30번부서합 BEGIN --FOR 문을 사용하면 OPEN과 CLOSE를 하지 않아도 된다. FOR CURVAL IN C1 LOOP --C1에서 데이터를 꺼내와 CURVAL 변수(SAL/DEPTNO 데이터를 통으로 꺼내와서 저장)에 저장 IF CURVAL.DEPTNO=10 THEN D1 := D1 + CURVAL.SAL; ELSIF CURVAL.DEPTNO=20 THEN D2 := D2 + CURVAL.SAL; ELSIF CURVAL.DEPTNO=30 THEN D3 := D3 + CURVAL.SAL; END IF; END LOOP; DBMS_OUTPUT.PUT_LINE('10번부서합:' || D1); DBMS_OUTPUT.PUT_LINE('20번부서합:' || D2); DBMS_OUTPUT.PUT_LINE('30번부서합:' || D3); END; /
EXECUTE DEPT_SAL;
Q2) 커서를 사용해서 모든 사원번호,이름,부서명,급여,보너스,부서번호를 출력하는 프로시져를 만들고 사용해 보세요. 보너스는 부서번호가 10번이면 급여의 10% 부서번호가 20번이면 급여의 20% 부서번호가 30번이면 급여의 30%
((* 문제에 <모든> 사원번호가 있으므로 CURSOR 사용. FOR문 이용해여 작성))
CREATE OR REPLACE PROCEDURE PRINTINFO IS CURSOR C1 IS SELECT EMPNO 사원번호, ENAME 이름, DNAME 부서명, SAL 급여, D.DEPTNO 부서번호 FROM EMP E, DEPT D WHERE E.DEPTNO=D.DEPTNO ; BONUS NUMBER(5):=0; BEGIN FOR CURVAL IN C1 LOOP --C1에서 데이터를 꺼내와 CURVAL 변수(SAL/DEPTNO 데이터를 통으로 꺼내와서 저장)에 저장 IF CURVAL.부서번호=10 THEN BONUS := CURVAL.급여 * 0.1; ELSIF CURVAL.부서번호=20 THEN BONUS := CURVAL.급여 * 0.2; ELSIF CURVAL.부서번호=30 THEN BONUS := CURVAL.급여 * 0.3; END IF; DBMS_OUTPUT.PUT_LINE('사원번호:'||CURVAL.사원번호||'이름:'||CURVAL.이름||'부서명:'||CURVAL.부서명||'급여:'||CURVAL.급여||'보너스:'||BONUS); END LOOP;
END; / EXECUTE PRINTINFO;
[6] 트리거(Trigger)
- 데이터베이스가 미리 정해놓은 조건을 만족하거나 어떤 동작이 수행되면 자동으로 수행되는 동작.특정 동작에 대한 이벤트로 인해서 실행되는 프로시져 --INSERT, UPDATE, DELETE (데이터베이스에서의 이벤트) - 형식) CREATE OR REPLACE TRIGGER 트리거이름 TIMING[BEFORE|AFTER] EVENT[INSERT|UPDATE|DELETE] ON 테이블명 [FOR EACH ROW] -행마다 적용할 건지 / DECLARE 변수선언; BEGIN 실행문장; END; / - 트리거유형 1) 문장레벨트리거 : DML발생시 한번만 수행. 영향을 받는 행이 없어도 수행된다. 2) 행레벨트리거 : DML발생시 각 행이 변경될때마다 수행. FOR EACH ROW 옵션을 설정해서 생성한다. :OLD 와 :NEW객체를 사용할 수 있다. --오라클에 의해 생성된 OLD(또는 NEW) 객체를 참조한다는 뜻
OLD는 DELETE(수정, 삭제된 행에 대한 정보를 갖는 객체) 에서만 사용가능, NEW(수정, 추가된 행에 대한 정보를 갖고있음)는 INSERT에서만 사용가능
예1) 부서가 추가되었을 때, '부서가 추가되었습니다' 를 출력하는 트리거 생성하기 CREATE OR REPLACE TRIGGER MSG_DEPT AFTER INSERT ON DEPT BEGIN DBMS_OUTPUT.PUT_LINE('부서가 추가 되었습니다.'); END; / INSERT INTO DEPT VALUES(11, '부서1', '서울');
Q1) 사원이 수정될때(UPDATE) 사원정보가 수정되었어요. 라고 출력하는 트리거 만들고 실행되도록 해보세요.
CREATE OR REPLACE TRIGGER MSG_EMP AFTER UPDATE ON EMP BEGIN DBMS_OUTPUT.PUT_LINE('사원정보가 수정되었습니다.'); END; / INSERT INTO EMP(EMPNO, ENAME) VALUES(91, '다슬송');
UPDATE EMP SET ENAME ='송다슬' WHERE ENAME='다슬송';
=============================================================== 예3) 행레벨트리거 --> FOR EACH ROW옵션을 설정해서 트리거 생성
#문장레벨트리거 - 영향을 받는 행이 없거나 여러 행이 영향을 받아도 무조건 한 번만 수행됨 CREATE OR REPLACE TRIGGER PRINT_MSG AFTER DELETE ON DEPT BEGIN DBMS_OUTPUT.PUT_LINE('부서가 삭제되었어요'); END; /
SQL> DELETE FROM DEPT WHERE DEPTNO=12; 부서가 삭제되었어요 -- 삭제된 행이 없어도 무조건 1번 수행됨
1 row deleted.
#행레벨트리거 - FOR EACH ROW 옵션주기 -- 영향을 받은 행의 갯수만큼만 실행됨 CREATE OR REPLACE TRIGGER PRINT_MSG AFTER DELETE ON DEPT FOR EACH ROW BEGIN -- :OLD 삭제된 부서에 대한 정보를 갖는 객체 DBMS_OUTPUT.PUT_LINE(:OLD.DEPTNO || '부서가 삭제되었어요'); END; /
CREATE OR REPLACE TRIGGER PRINT_DEPTNO AFTER INSERT ON DEPT FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE(:NEW.DNAME || '부서가 추가되었습니다.'); END; /
SQL> INSERT INTO DEPT VALUES(55, '부서55', '제주'); 부서55부서가 추가되었습니다. 부서가 추가 되었습니다.
1 row created.
## 트리거 수정 : DELETE 이벤트에서는 :OLD 객체를 통해 삭제된 행의정보를 얻어옴 CREATE OR REPLACE TRIGGER PRINT_MSG AFTER DELETE ON DEPT FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE(:OLD.DEPTNO || '부서가 삭제되었어요'); END; /
예) UPDATE CREATE OR REPLACE TRIGGER UPDATE_DEPTNO AFTER UPDATE ON DEPT FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE(:OLD.DNAME || '부서의 위치가 ' || :OLD.LOC || ' 에서'|| :NEW.LOC || ' 위치로 변경됨'); END; /
SQL> UPDATE DEPT SET LOC='신촌' WHERE DEPTNO=40; OPERATIONS부서의 위치가 BOSTON 에서신촌 위치로 변경됨
1 row updated.
============================================================ Q1) EMP테이블에 사원정보가 추가되면 사원번호 XX님이 입사하셨습니다 라고 출력되는 트리거 만들고 실행되도록 해보기
CREATE OR REPLACE TRIGGER MSG_EMP AFTER INSERT ON EMP FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE('사원번호'|| :NEW.ENAME || ' 님이 입사하셨습니다.'); END; / INSERT INTO EMP(EMPNO) VALUES(88); =============================================================== Q2) EMP테이블에 사원정보가 삭제되면 사원번호 XX님이 퇴사하셨습니다. 라고 출력되는 트리거 만들고 실행되도록 해보기
CREATE OR REPLACE TRIGGER MSG_EMP AFTER DELETE ON EMP FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE('사원번호'|| :OLD.EMPNO || ' 님이 퇴사하셨습니다'); END; / DELETE FROM EMP WHERE EMPNO=88; ============================================================ Q3) EMP테이블에서 사원정보가 수정되면 XXX번 사원의 부서가 XXX부서에서 XXX 부서로 변경되었습니다. ->부서번호 사용 라고 출력되는 트리거 만들고 실행되도록 해보기
CREATE OR REPLACE TRIGGER UPDATE_DEPTNO AFTER UPDATE ON EMP FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE(:OLD.EMPNO || '부서의 위치가 ' || :OLD.DEPTNO || ' 에서'|| :NEW.DEPTNO || ' 위치로 변경됨'); END; / UPDATE EMP SET DEPTNO=40 WHERE DEPTNO=30;
============================================================ 예) 상품테이블(재고수량) CREATE TABLE ITEM ( CODE CHAR(6) PRIMARY KEY, --상품코드 NAME VARCHAR(12) NOT NULL, -- 상품명 COMPANY VARCHAR2(12), --제조회사 PRICE NUMBER(8), --가격 CNT NUMBER(6) DEFAULT 0 );
INSERT INTO ITEM VALUES('A01','냉장고','삼성',2000000,0); INSERT INTO ITEM VALUES('A02','컴퓨터','삼성',1000000,0); COMMIT;
-- 입고테이블에 데이터 2개 추가해 보세요. 2) DELETE FROM WAREHOUSE; COMMIT;
1) INSERT INTO WAREHOUSE VALUES(1,'A01','2020/03/16',5,179000); INSERT INTO WAREHOUSE VALUES(2,'A02','2020/03/16',10,780000); COMMIT; ============================================================ 3) Q1) 입고테이블에 상품이 입고되면 상품테이블에 재고수량이 증가되도록 TRIGGER를 만들어 보세요. (UPDATE 사용)
INSERT INTO WAREHOUSE VALUES(2, 'A02','2020/03/16',10,780000); --> ITEM테이블의 'A01' 제품의 재고수량이 10개 증가되도록
CREATE OR REPLACE TRIGGER U_ITEM -- UPDATE_ITEM은 오류발생 (UPDATE라는 명령어가 존재하기 때문?) AFTER INSERT ON WAREHOUSE FOR EACH ROW BEGIN UPDATE ITEM SET CNT= (CNT+:NEW.PUTCNT) WHERE CODE= :NEW.CODE; DBMS_OUTPUT.PUT_LINE( :NEW.PUTCNT || '만큼 증가되었습니다.'); END; /
(강사답안) CREATE OR REPLACE TRIGGER INSERT_WAREHOUSE AFTER INSERT ON WAREHOUSE FOR EACH ROW BEGIN UPDATE ITEM SET CNT=CNT + :NEW.PUTCNT WHERE CODE=:NEW.CODE; END; / ============================================================ Q2) 입고정보 삭제하기 DELETE FROM WAREHOUSE WHERE NUM=1; -- 상품테이블의 재고수량이
-- 삭제된 수량만큼 감소되도록(ITEM 테이블에서) 트리거 만들어 보세요.
CREATE OR REPLACE TRIGGER DELETE_WAREHOUSE AFTER DELETE ON WAREHOUSE FOR EACH ROW BEGIN UPDATE ITEM SET CNT=CNT - :OLD.PUTCNT WHERE CODE=:OLD.CODE; END; / ============================================================= Q3) 입고수량 수정하기 UPDATE WAREHOUSE SET PUTCNT=3 WHERE NUM=1; -- A01제품의 재고수량이 수정된 수량만큼 변경되도록 트리거 만들기
CREATE OR REPLACE TRIGGER UPDATE_WAREHOUSE AFTER UPDATE ON WAREHOUSE FOR EACH ROW BEGIN UPDATE ITEM SET CNT = CNT - :OLD.PUTCNT + :NEW.PUTCNT WHERE CODE = :OLD.CODE; -- (NEW.CODE도 가능) END; /
UPDATE WAREHOUSE SET PUTCNT=3 WHERE NUM=1; SELECT * FROM ITEM;
COMMIT;
[7] 예외처리 형식) EXCEPTION WHEN 예외타입1 THEN 예외처리1 WHEN 예외타입2 THEN 예외처리2 .. WHEN OTHERS THEN 예외처리3;--위에서 설정된 예외 이외의 예외를 처리
예1) DECLARE NUM1 NUMBER(5); NUM2 NUMBER(5); NUM3 NUMBER(5); BEGIN NUM1 := &NUM1; NUM2 := &NUM2; NUM3 := NUM1/NUM2; DBMS_OUTPUT.PUT_LINE(NUM1 || '/' || NUM2 ||'=' || NUM3); EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT.LINE('0으로 나눌 수 없음'); WHEN OTHERS THEN DBMS_OUTPUT.PUT.LINE('오류발생'); END; /
-- 급여에 대한 정보를 갖는 테이블 CREATE TABLE PAY ( NUM NUMBER(5) PRIMARY KEY, --PK주기위한 의미없는 번호 EMPNO NUMBER(5) REFERENCES EMP(EMPNO), PASL NUMBER(10), -- 급여정보 D DATE -- 지급일 ); CREATE SEQUENCE PAY_SEQ;
INSERT INTO PAY VALUES(PAY_SEQ.NEXTVAL,7369,3000,2020/2/28'); --> 급여가 지급될 사원번호와 급여, 지급일을 파라미터로 전달받아 PAY테이블에 정보가 추가되고 EMP테이블에 급여정보가 수정디는 프로시져를 만들고 호출해 보세요.
CREATE OR REPLACE PROCEDURE EMP_PAY ( VEMPNO EMP.EMPNO%TYPE, VSAL EMP.SAL%TYPE, VDATE PAY.D%TYPE ) IS BEGIN INSERT INTO PAY VALUES(PAY_SEQ.NEXTVAL,VEMPNO,VSAL,VDATE); UPDATE EMP SET SAL=VSAL WHERE EMPNO=VEMPNO; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; DBMS_OUTPUT.PUT_LINE('오류로 인해 작업을 모두 취소했습니다.'); END; /
<10> TCL (Transaction Control Language) (1) 트랜젝션 : 분리되어서는 안되는 논리적 작업 단위 (ex: 여행사상품예약프로그램<숙소테이블/렌트카/항공권/결제 등등>/계좌이체프로그램<입/출금 테이블>) (cf:스프링; 트랜젝션 처리가 쉬움)
(2) TCL : DML(UPDATE, DELETE, INSERT)문이 실행되어 DBMS에 저장되거나 되돌리기 위해 실행해야 하는 SQL (3) TCL 의 종류 1) COMMIT : SQL구문의 결과를 영구적으로 DB에 반영 2) ROLLBACK : SQL구문의 결과를 취소 3) SAVEPOINT : 트랜젝션의 한 지점에 표시하는 임시 저장점 (4) TCL 의 특성 읽기일관성/잠금현상 : 어떤 사용자가 변경중인 행을 다른 사용자가 변경할 수 없게 하는 기술로써 변경중인 사용자에 의해 COMMIT 또는 ROLLBACK이 실행된 후 변경되는 특성 (5) 트랜젝션의 시작과 종료 1) DBMS에 처음 접속했을 때 2) COMMIT 또는 ROLLBACK했을 때 3) DDL구문(CREATE,DROP,..)이 실행되었을 때 ㄴCOMMIT 되지 않은 이전 DML명령어들이 자동으로 COMMIT된다.
예1) # DB접속 - 트랜잭션 시작 SQL> INSERT INTO DEPT VALUES(51,'부서1','서울'); SQL> INSERT INTO DEPT VALUES(52,'부서2','종로'); SQL> INSERT INTO DEPT VALUES(53,'부서3','종로');
*INSERT 후 COMMIT 하지 않고 EXIT로 종료할 시에 해당부분 자동으로 COMMIT되고 프로 그램 종료.
DEPTNO DNAME LOC ---------- ---------------------------- --------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 51 부서1 서울 71 부서4 종로 52 부서2 종로 53 부서3 종로 72 부서4 송파
예2) SQL> INSERT INTO DEPT VALUES(81,'부서81','송파'); SQL> INSERT INTO DEPT VALUES(82,'부서82','송파');
SQL> SAVEPOINT A; -- SAVEPOINT 설정 Savepoint created.
SQL> INSERT INTO DEPT VALUES(83,'부서83','송파'); SQL> INSERT INTO DEPT VALUES(84,'부서84','송파'); SQL> ROLLBACK TO A; --A 지점이후부터 ROLLBACK됨(83,84번 작업취소) Rollback complete.
SQL> SELECT * FROM DEPT;
DEPTNO DNAME LOC ---------- ---------------------------- ----------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 51 부서1 서울 71 부서4 종로 53 부서3 종로 72 부서4 송파 81 부서81 송파 82 부서82 송파
(11) 시퀀스(ex: 게시판에서 글 번호 순차적으로 정렬되어야 할 때, 수식이 아닌 시퀀스 사용) - 연속되는 숫자값을 자동으로 증감시키는 일련번호를 발생시키는 객체 - 형식 CREATE SEQUENCE 시퀀스명 [INCREMENT BY N] [START WITH N] -- 이미 시퀀스 생성된 이후 특정 번호부터 다시 발생시켜야할 때 사용 [MAXVALUE N | NONMAXVALUE N] [MINVALUE N | NONMINVALUE N] [CYCLE | NOCYCLE ] --최대 또는 최소 값에 도달했을 때 반복할지 정함 [CACHE | NOCACHE ] --미리 한 번에 만들어서 하나씩 꺼냄(하나 씩 만들어서 하나씩 꺼낼 때 보다 성능 빠름)_시퀀스 성능이 다소느리므로 CACHE를 많이 사용. (NOCACHE 성능 많이 떨어짐)-성능향상관련개념-->튜닝. **화면단으로 순차적으로 보이게 로직으로 만들고, 데이터베이스로는 PK로 중복값이 없고 최신글이 앞으로 나오게 잡아준다. (즉 1->2->3->4->7->9 이러한 문제는 중요치 않음)
- 삭제 DROP SEQUENCE 시퀀스명; - 함수 NEXTVAL : 다음값 얻어오기 CURRVAL : 현재발생된 값 얻어오기
예1) CREATE SEQUENCE MYSEQ; SELECT MYSEQ.NEXTVAL FROM DUAL; --'DUAL'은 테이블이 없을 때 임시사용됨
SQL> SELECT MYSEQ.NEXTVAL FROM DUAL;
NEXTVAL ---------- 1 SQL> SELECT MYSEQ.NEXTVAL FROM DUAL;
NEXTVAL ---------- 2 SQL> SELECT MYSEQ.NEXTVAL FROM DUAL;
NEXTVAL ---------- 3
SQL> DROP SEQUENCE MYSEQ;
Sequence dropped.
SQL> SELECT MYSEQ.NEXTVAL FROM DUAL; SELECT MYSEQ.NEXTVAL FROM DUAL * ERROR at line 1: ORA-02289: sequence does not exist
예2) CREATE SEQUENCE MYSEQ1 INCREMENT BY 10 --10씩 증가 START WITH 10; --시작값을 10으로 지정 Sequence created. SELECT MYSEQ1.NEXTVAL FROM DUAL;
SQL> SELECT MYSEQ1.NEXTVAL FROM DUAL;
NEXTVAL ---------- 10
SQL> SELECT MYSEQ1.NEXTVAL FROM DUAL;
NEXTVAL ---------- 20
SQL> SELECT MYSEQ1.CURRVAL FROM DUAL; --현재 발생된 SEQUENCE 값 꺼내오기
CURRVAL ---------- 20
예3) CREATE TABLE BOARD ( NUM NUMBER(5) PRIMARY KEY, --글번호 TITLE VARCHAR2(20), WRITER VARCHAR2(20), CONTENT VARCHAR2(50), REGDATE DATE );
CREATE SEQUENCE BOARD_SEQ; --시퀀스 생성
INSERT INTO BOARD VALUES(BOARD_SEQ.NEXTVAL, 'TEST1', '김씨','테스트1',SYSDATE); INSERT INTO BOARD VALUES(BOARD_SEQ.NEXTVAL, 'TEST2', '이씨','테스트2',SYSDATE);
예) READ ONLY 옵션 설정하기 - INSERT, UPDATE, DELETE는 못하고 읽기(SELECT)만 가능 CREATE OR REPLACE VIEW MYEMP1 AS SELECT EMPNO,ENAME,SAL,DEPTNO FROM EMP WITH READ ONLY;
SQL> INSERT INTO MYEMP1 VALUES(8001, '김씨', 2000, 20); ㄴERROR at line 1: ORA-42399: cannot perform a DML operation on a read-only view
예4) WITH CHECK OPTION - 뷰의 조건에 맞는 데이터들만 DML작업을 할 수 있음 CREATE OR REPLACE VIEW MYEMP1 AS SELECT EMPNO, ENAME, SAL, DEPTNO FROM EMP WHERE DEPTNO=10 WITH CHECK OPTION;
SQL> INSERT INTO MYEMP1 VALUES(8002,'김씨',2000,20); INSERT INTO MYEMP1 VALUES(8002,'김씨',2000,20) * ERROR at line 1: ORA-01402: view WITH CHECK OPTION where-clause violation ==> 20번 부서는 VIEW의 조건에 맞지 않으므로 오류!
3) 뷰의 종류 <1> 단일뷰 : 하나의 테이블로 생성되는 뷰(기본적으로 DML작업이 됨) <2> 복합뷰 : 두 개이상의 테이블로 생성되는 뷰(조인된 뷰).DML작업을 못함
예) # 복합뷰만들기 CREATE VIEW MYEMP3 AS SELECT EMPNO,ENAME,JOB,E.DEPTNO,DNAME FROM EMP E, DEPT D WHERE E.DEPTNO=D.DEPTNO;
객체들의 정보를 갖고 있는 테이블 같은 저장공간 --> DATA DICTIONARY SET LINESIZE 100 DESC USER_VIEWS; Name Null? Type ----------------------------------------------------- -------- ---------------------------- VIEW_NAME NOT NULL VARCHAR2(30) TEXT_LENGTH NUMBER TEXT LONG TYPE_TEXT_LENGTH NUMBER TYPE_TEXT VARCHAR2(4000) OID_TEXT_LENGTH NUMBER OID_TEXT VARCHAR2(4000) VIEW_TYPE_OWNER VARCHAR2(30) VIEW_TYPE VARCHAR2(30) SUPERVIEW_NAME VARCHAR2(30) EDITIONING_VIEW VARCHAR2(1) READ_ONLY VARCHAR2(1)
4) 뷰 삭제하기 형식) DROP VIEW 뷰이름;
예) SQL> DROP VIEW MYEMP1; -- VIEW 삭제하기 View dropped. SQL> SELECT VIEW_NAME FROM USER_VIEWS; VIEW_NAME ----------- MYEMP2 MYEMP3
Q1) EMP테이블과 DEPT테이블을 이용해서 이름,직업,부서,부서명을 조회하는 뷰를 만들고 사용해 보세요. 사용후 뷰 삭제하기
CREATE VIEW MYEMP0 AS SELECT E.ENAME "이름", E.JOB "직업", D.DEPTNO "부서",D.DNAME "부서명" FROM EMP E, DEPT D WHERE E.DEPTNO= D.DEPTNO ;
SQL> DROP VIEW MYEMP0;
View dropped.
SQL> SELECT * FROM MYEMP0; SELECT * FROM MYEMP0 * ERROR at line 1: ORA-00942: table or view does not exist
**VIEW는 DML작업이 아닌 조회를 위한 목적으로 사용됨**
================================================================= [12] 인덱스 - 인덱스란 포인터(주소값을 갖고있는 것)를 사용하여 행의 검색을 촉진시키는 객체 - 데이터를 빠르게 찾기 위한 이진의 균형탐색 TREE를 이용하여 디스크 입출력횟수를 줄인다. - 인덱스는 논리적으로도 물리적으로도 테이블과 독립적이다. - 언제든지 생성하거나 삭제할 수 있으며 이는 테이블과 다른 인덱스에 영향을 주지 않는다는 의미이다. - 인덱스테이블에는 인덱스키와 ROWID(실제적으로 물리적인 위치)에 대한 정보만을 갖고 있다. - 테이블정의의 PRIMARY KEY나 UNIQUE제약조건을 정의할때 UNIQUE인덱스가 자동으로 생성된다. - 형식) CREATE [UNIQUE] INDEX 인덱스명 ON 테이블명(컬럼명 ASC|DESC)
예) CREATE INDEX IDX_EMP ON EMP(EMPNO DESC);
QL> DESC USER_INDEXES; --사용자가 만든 INDEX에 대한 정보를 갖는 DATA DICTIONARY
Name Null? Type ----------------------------------------------------- -------- --------------------- INDEX_NAME NOT NULL VARCHAR2(30) INDEX_TYPE VARCHAR2(27) TABLE_OWNER NOT NULL VARCHAR2(30) TABLE_NAME NOT NULL VARCHAR2(30) TABLE_TYPE VARCHAR2(11) UNIQUENESS VARCHAR2(9) COMPRESSION VARCHAR2(8) PREFIX_LENGTH NUMBER TABLESPACE_NAME VARCHAR2(30) INI_TRANS NUMBER MAX_TRANS NUMBER INITIAL_EXTENT NUMBER ... ...
SQL> SELECT INDEX_NAME,TABLE_NAME FROM USER_INDEXES;
SYS_C007014 BOARD ERROR at line 1: ORA-01418: specified index does not exist
- 삭제 DROP INDEX 인덱스명; DROP INDEX IDX_EMP_EMPNO;
예1) UNIQUE인덱스 : 인덱스로 설정되는 컬럼에 중복값이 들어가면 안됨 CREATE UNIQUE INDEX IDEX_EMP_EMPNO ON EMP(EMPNO DESC);
예2) 사원이름 컬럼을 인덱스로 생성해 보세요.
CREATE INDEX IDX_EMP_ENAME ON EMP(ENAME DESC); Index created.
SELECT INDEX_NAME,TABLE_NAME FROM USER_INDEXES WHERE INDEX_NAME='IDX_EMP_ENAME'; 또는 (위, 아래 결과 같음) SELECT INDEX_NAME,TABLE_NAME FROM USER_INDEXES WHERE INDEX_NAME LIKE 'IDX_%';
- 인덱스 생성 기준 1) 사용자의 SQL에서 WHERE절에 자주 사용되는 컬럼이 대상이 된다. 2) 인덱스 갯수는 사용형태에 따라 적절하게 생성한다. 3) 너무 많은 인덱스는 오히려 성능이 감소된다. 4) 빈번하게 변경되지 않는 테이블에 적용해야 한다. 5) 작은 테이블보다는 큰 테이블에 인덱스를 생성한다.
- 인덱스 생성시 주의사항 DML명령어에 주의해야 한다. INSERT -> 테이블에 데이터가 추가되고 인덱스 테이블에 정렬되어 추가된다. DELETE -> 삭제시 인덱스 테이블에는 삭제되지 않고 삭제되었음을 표시한다. 즉 데이터가 남아있게 되므로 실제데이터가 10만건이라면 인덱스에 데이터는 20만건이 될수도 있다(성능저하) *--오라클은 먼저 인덱스 테이블을 체크하므로 UPDATE -> 수정시 인덱스테이블에 DELETE된후 INSERT 작업이 수행된다.
[13] 사용자계정관리
1) 계정생성 CREATE USER 아이디 IDENTIFIED BY 비밀번호; 예) SQL> CONN system/java1234 -- 계정생성위해 관리자계정으로 접속 SQL> CREATE USER hello1234 IDENTIFIED BY hello0000; --사용자계정만들기 SQL> GRANT connect,resource TO hello1234;-- 접속권한부여하기 (resource; roll 권한부여) SQL> conn hello1234/hello0000;--사용자계정으로 접속 SQL> CREATE TABLE MYEMP(EMPNO NUMBER(4) PRIMARY KEY,ENAME VARCHAR2(100));
2) 계정삭제 (*테이블을 객체라고 부름) DROP USER 아이디; # CASCADE : 삭제할 계정에 데이터가 존재하면 삭제가 안됨. 이때 CASCADE옵션을 주면 삭제 가능
2) IF ~ ELSE 문 형식1) IF 조건식 THEN 실행문; ELSE 실행문; END IF;
예1) 임의의 정수 입력받아 짝수/홀수 판별하기 DECLARE NUM NUMBER(5):=&NUM; BEGIN IF NUM MOD 2=0 THEN --MOD 나머지 값 구하는 연산자 DBMS_OUTPUT.PUT_LINE(NUM ||'은 짝수'); ELSE DBMS_OUTPUT.PUT_LINE(NUM ||'은 홀수'); END IF; END; /
형식2) IF 조건식 THEN 실행문; ELSIF 조건식 THEN 실행문; ELSIF 조건식 THEN 실행문; .. END IF;
Q1) 두 정수를 입력받아 두 수중 큰 수 구하기
DECLARE NUM1 NUMBER(5):=&NUM; NUM2 NUMBER(5):=&NUM; BEGIN IF NUM1>NUM2 THEN DBMS_OUTPUT.PUT_LINE(NUM1 ||'이 큰수'); ELSE DBMS_OUTPUT.PUT_LINE(NUM2 ||'이 큰수'); END IF; END; / Enter value for num: 2 old 2: NUM1 NUMBER(5):=&NUM; new 2: NUM1 NUMBER(5):=2; Enter value for num: 9 old 3: NUM2 NUMBER(5):=&NUM; new 3: NUM2 NUMBER(5):=9; 9이 큰수
PL/SQL procedure successfully completed.
Q2) 두 점수를 입력받아 평균이 80이상이면 '합격' 아니면 '불합격' 출력하기
DECLARE NUM1 NUMBER(5):=&NUM; NUM2 NUMBER(5):=&NUM; BEGIN IF (NUM1 + NUM2)/2 >=80 THEN DBMS_OUTPUT.PUT_LINE('합격'); ELSE DBMS_OUTPUT.PUT_LINE('불합격'); END IF; END; /
3) FOR문
-형식 FOR 변수 IN 초기값..마지막값 LOOP 반복수행할 문장;
END LOOP;
예) 1부터 100까지 합 출력 하시오.
DECLARE TOT NUMBER(5):=0; BEGIN FOR I IN 1..100 LOOP TOT := TOT + I; DBMS_OUTPUT.PUT(I || ' '); --1~100까지 옆으로 출력(자바의 PRINT 동일) , PUT_LINE 은 자바의 PRINTLN END LOOP; DBMS_OUTPUT.PUT_LINE('1부터 100까지합:' || TOT); END; /
Q1) 1부터 100까지 수 중 3의 배수출력하고 3의 배수합 구해서 출력하기 IF NUM MOD 2=0 THEN --MOD 나머지 값 구하는 연산자
DECLARE MULTIPLETHREE NUMBER(5):=0; BEGIN FOR I IN 1..100 LOOP IF I MOD 3=0 THEN MULTIPLETHREE := MULTIPLETHREE + I; DBMS_OUTPUT.PUT(I || ' ' ); END IF; END LOOP; DBMS_OUTPUT.PUT_LINE('1부터 100까지 수 중 3의 배수 합:' || MULTIPLETHREE); END; / 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69 72 75 78 81 84 87 90 93 96 99 1부터 100까지 수 중 3의 배수 합:1683
PL/SQL procedure successfully completed.
Q2) 단 입력받아 구구단 출력하기 DECLARE NUM NUMBER(5) := &NUM; BEGIN FOR I IN 1..9 LOOP DBMS_OUTPUT.PUT_LINE(NUM || '*' || I || '=' || NUM*I); END LOOP; END; /
Q3) 구구단 출력하기(2단부터 9단까지)
2단 2*1=2 2*2=4 ........ 2*9=18 3단 3*1=3 ..
.. 9단 9*1=9 9*2=18 ....... 9*9=81
FOR I IN 2..9 LOOP FOR J IN 1..9 LOOP BEGIN FOR I IN 2..9 LOOP DBMS_OUTPUT.PUT(I || '단'); FOR J IN 1..9 LOOP DBMS_OUTPUT.PUT(I || '*' || J || '=' || I*J || ' '); END LOOP; DBMS_OUTPUT.PUT_LINE(' '); --flush와 같음 (데이터 뽑아오는 기능) END LOOP; END; /
- EXIT : FOR문을 강제로 빠져나가기
예2) 정수입력받아 소수 판별하기
DECLARE NUM NUMBER(5) := &NUM; BEGIN FOR I IN 2..NUM LOOP IF I=NUM THEN DBMS_OUTPUT.PUT_LINE(NUM || '는/은 소수 입니다'); ELSIF NUM MOD I = 0 THEN DBMS_OUTPUT.PUT_LINE(NUM || '는/은 소수가 아닙니다'); EXIT; END IF; DBMS_OUTPUT.PUT_LINE(' '); END LOOP; END; / Enter value for num: 7 old 2: NUM NUMBER(5) := &NUM; new 2: NUM NUMBER(5) := 7; 7는/은 소수 입니다
PL/SQL procedure successfully completed.
방법1) FOR문 안에 IF I = NUM 실수입니다. ELSE IF NUM MOD I = 0 소수입니다. EXIT
방법2) NUM-1 IF NUM MOD I = 0 소수입니다. EXIT ELSIF I = NUM-1 실수입니다.
03.18 시험출제확정내용=============================================== <8> 제약조건<***> - 테이블의 해당 컬럼에 잘못된 값이 입력/변경/삭제되는 것을 방지하기 위해 설정되는 조건(무결성 제약조건) - 결함이 없는 데이터를 관리하기 위함
1) 종류 (1) PRIMARY KEY (기본키) - 하나의 테이블에 하나만 존재하며 각 행을 식별하기 위한 용도로 사용 - NULL값을 허용하지 않고 중복값을 허용안함(NOT NULL + UNIQUE KEY) (2) FOREIGN KEY (외래키,참조키) - 부모테이블의 특정 컬럼(기본키)를 참조하는 컬럼 - 부모테이블에 참조하는 값이 없으면 오류가 발생한다.
자식테이블이 있는 부모테이블의 경우 바로 부모테이블 삭제 안됨 --자식테이블 먼저 삭제 후 부모테이블 삭제
DROP TABLE GRADE; --자식 DROP TABLE STUDENT; --부모
4) 테이블 구조 변경하기
<1> 컬럼추가 ALTER TABLE 테이블명 ADD(컬럼명 타입); 예) ALTER TABLE EMP ADD(EMAIL VARCHAR2(15));
<2> 컬럼변경 ALTER TABLE 테이블명 MODIFY(컬럼명 타입); 예) ALTER TABLE EMP MODIFY(EMAIL VARCHAR2(20));
<3> 컬럼명 변경 ALTER TABLE 테이블명 RENAME COLUMN 컬럼명 TO 바꿀컬럼명; 예) ALTER TABLE EMP RENAME COLUMN EMAIL TO MAIL;
<4> 컬럼삭제 ALTER TABLE 테이블명 DROP COLUMN 컬럼명; 예) ALTER TABLE EMP DROP COLUMN MAIL;
<8> 제약조건<***> - 테이블의 해당 컬럼에 잘못된 값이 입력/변경/삭제되는 것을 방지하기 위해 설정되는 조건(무결성 제약조건) - 결함이 없는 데이터를 관리하기 위함
1) 종류 (1) PRIMARY KEY (기본키) - **하나의 테이블에 하나만 존재**하며 각 행을 식별하기 위한 용도로 사용 - NULL값을 허용하지 않고 중복값을 허용안함(NOT NULL + UNIQUE KEY) (2) FOREIGN KEY (외래키,참조키) - 부모테이블의 특정 컬럼(기본키)를 참조하는 컬럼 - ***부모테이블에 참조하는 값이 없으면 오류가 발생한다***. (3) UNIQUE KEY(유일키) --중복 X - 컬럼의 모든 값이 유일해야 하는 경우에 사용. 중복데이터를 허용하지 않음 - NULL은 허용됨(해당 유일키가 없는경우 안 넣어도 됨)-ex)이메일,전화번호가 없는 회원 (4) CHECK 제약조건(많이사용안됨) - 조건에 맞는 데이터만 입력되도록 조건을 부여하는 제약조건 (5) NOT NULL 조건(많이사용됨) - NULL값을 허용하지 않는 컬럼에 설정(ex: 회원가입시 이름 필수)
예1) 제약조건에 이름을 부여하지 않은 경우 CREATE TABLE DEPT1 ( DEPTNO NUMBER(2) PRIMARY KEY, DNAME VARCHAR2(15) DEFAULT '인사부', LOC CHAR(9) CHECK(LOC IN('서울','부산')) );
INSERT INTO DEPT1(DEPTNO,DNAME,LOC) VALUES(1, '총무부', '서울'); INSERT INTO DEPT1(DEPTNO,LOC) VALUES(2, '부산'); INSERT INTO DEPT1 VALUES(3, '영업부', '서울');