1.order by 문제:

데이터베이스에서 가장 신경쓰는 부분은 빠르게 처리되는 것, 필요한 양만큼만 데이터를 가져오는 것.

 

빠르게 동작하는 sql을 위해서는 order by를 이용하는 작업을 가능하면 사용하지 말아야한다.

엄청난 성능 저하를 가져오기 때문에

데이터가 적을 경우와 정렬을 빠르게 할 수 있는 방법이 있는 경우가 아니라면 order by는 주의해야만 한다.

 

2. 실행 계획과 order by

실행 계획: sql을 데이터베이스에서 어떻게 처리할 것인가?

 

SQL파싱: 구문에 오류가 있는지, 실행해야하는 대상이 존재하는지

SQL 최적화: 실행되는데 필요한 비용을 계산, 어떤 방식으로 실행하는 것이 가장 좋다는 것을 판단하는 실행계획을 세움

SQL 실행: 메모리상에 데이터를 읽거나 물리적인 공간에서 데이터를 로딩하는 등의 작업

 

2.1 order by 보다 인덱스

인덱스라는 존재가 이미 정렬된 구조이므로 이를 이용해서 별도의 정렬을 하지 않는다.

 

select /*+ INDEX_DESC(spring_board sparing_board_pk) */

*

from spring_board where b_num >0;

1)sort를 하지 않는다.

2)spring_board를 바로 접근하는 것이 아니라 spring_board_pk를 이용해서 접근

 

제약조건명이 인덱스명

 

3.인덱스와 오라클 힌트

오라클은 select문을 전달할 때 힌트라는 것을 사용할 수 있다. 힌트는 개발자가 데이터베이스에 어떤 방식으로 실행해줘야하는지를 명시하기 때문에 조금 강제성이 부여되는 방식이다.

select문을 작성할 때 힌트는 잘못 작성되어도 실행할 때는 무시되기만하고 별도의 에러는 발생하지 않는다.

/*+로 시작하고 */로 끝난다.

FULL(테이블명): /*+FULL(spring_board)*/

INDEX_ASC, INDEX_DESC: /*+INDEX_DESC(spring_board spring_board_pk)*/

DESC(테이블명 인텍스명)

인덱스명: 기본키=> spring_board_pk

 

4.rownum과 인라인 뷰

전체가 아닌 필요한 만큼의 데이터를 가져오는 방식. 오라클 데이터베이스는 페이지 처리를 위해서 rownum이라는 키워드를 사용해서 데이터에 순번을 붙여 사용.

select rownum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from spring_board;

 

1번 부터 10번까지

select /*+INDEX_DESC(spring_board spring_board_pk)*/ rownum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from spring_board where rownum <=10;

 

2페이지 데이터를 구하기 위해 11번 부터 20번까지의 데이터 출력

select /*+INDEX_DESC(spring_board spring_board_pk)*/ rownum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from spring_board where rownum <=20 and rownum >10;

결과는 아무 데이터도 출력하지 않는다. rownum 조건은 반드시 1이 포함되어야한다.

 

select /*+INDEX_DESC(spring_board spring_board_pk)*/ rownum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from spring_board where rownum <=20; --반드시 1이 포함되도록 해야한다.

 

인라인 뷰 처리(rownum의 별칭 rnum)

SELECT 

rnum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from(  //안쪽에는 함수 쓰면 안됨.

select /*+INDEX_DESC(spring_board spring_board_pk)*/

rownum asrnum, b_num, b_name, b_title, b_date

from spring_board

where rownum <=20

) boardlist

where rnum >10;

 

--순서정리

필요한 순서로 정렬된 데이터에 rownum을 붙인다.

처음부터 해당 페이지의 데이터를 rownum <=20과 같은 조건을 이용해서 구한다.

구해놓은 데이터를 하나의 테이블처럼 간주하고 인라인뷰로 처리한다.

인라인뷰에서 필요한 데이터만을 남긴다.

 

페이징 처리

페이징 처리를 위해서 필요한 파라미터: 페이지 번호, 한 페이지당 몇 개의 데이터를 보여줄 것인지가 결정되어야 한다.

 

pageNum*amount

(pageNum-1)*amount

 

화면에 페이징처리

화면 페이지는 다음과 같은 정보들이 필요

현재 페이지 번호, 이전과 다음으로 이동 가능한 링크의 표시 여부, 화면에서 보여지는 페이지의 시작번호와 끝번호

 

5페이지를 본다면 1페이지 부터 10페이지 안에서의 5페이지, 15페이지를 본다면 11페이지 부터 시작

페이징의 끝번호(endPage) 계산

this.endPage = (int)(Math.ceil(cvo.getPageNum()/10.0))*10;

-1페이지: Math.ceil(0.1)*10 = 10

-2페이지: Math.ceil(0.2)*10 = 10

-10페이지: Math.ceil(1)*10 = 10

-11페이지: Math.ceil(1.1)*10 = 20

-20페이지: Math.ceil(2)*10 = 20

 

페이징의 시작번호 계산

this.startPage = this.endPage-9;

끝번호(endPage)는 전체 데이터수에 의해서 영향을 받는다.

int realEnd = (int) (Math,ceil(total*1.0)/cvo.getAmount()));

if(realEnd <= this.endPage){

this.endPage = realEnd;

}

'DataBase+SQL' 카테고리의 다른 글

PROCEDURE TRIGGER  (0) 2023.06.29
PreparedStatement  (0) 2023.06.28
SQL 반복문, 커서  (0) 2023.06.27
PL/SQL  (0) 2023.06.26
MVIEW, 시퀀스  (0) 2023.06.23

트리거: 서브 프로그램 단위의 하나인 TRIGGER는 테이블, 뷰, 스키마 또는 데이터베이스에 관련된 PL/SQL 불록 ( 또는 프로시저)으로 관련된 특정 사건(EVENT)이 발생될 때마다 묵시적(자동)으로 해당 PL/SQL 블록이 실행

데이터 베이스 내에 오브젝트로서 저장되어 관리되고, 사용자가 지정해서 실행을 할 수 없을며, 오직 TRIGGER 생성시 정의한 특정 사건(EVENT)에 의해서만 묵시적인 자동 실행(FIRE)이 이루어진다.

 

TRIGERR 몸체(실행부)에 TCL 명령(COMMIT, ROLLBACK,SAVEPOINT 명령)이 포함될 수 없다.

 

트리거란 특정 테이블의 데이터에 변경이 가해졌을 때 자동으로 수행되는 저장 프로시저

저장 프로시저는 필요할 때마다 사용자가 직접 EXECUTE 명령어로 호출해야 하지만 트리거는 테이블의 데이터가 INSERT, UPDATE, DELETE문에 의해 변경 될 때 자동으로 수행. (트리거를 사용자가 직접 실행X)

 

CREATE TRIGGER TRIGGER_NAME
TIMING[BEFORE|AFTER] --1
EVENT[INSERT|UPDATE|DELETE] --2
ON TABLE_NAME
[FOR EACH ROW]
[WHEN CONDITIONS]
BEGIN
	STATEMENT
END

 

① 트리거의 타이밍(트리거의 동작 시점을 설정)

[BEFORE] 타이밍은 어떤 테이블에 INSERT, UPDATE, DELETE 문이 실행될 때 해당 문장이 실행되기 전에 트리거가 가지고 있는 BEGIN ~ END 사이의 문장을 실행한다. [AFTER] 타이밍은 INSERT, UPDATE, DELETE 문이 실행되고 난 후에 트리거가 가지고 있는 BEGIN ~ END 사이의 문장 실행

 

②트리거의 이벤트(트리거 동작을 위한 이벤트 종류를 정의하는 단계)

트리거의 이벤트는 사용자가 어떤 DML(INSERT, UPDATE, DELETE)문을 실행했을 때 트리거를 발생시킬 것인지 결정, ON 테이블명은 트리거가 주목하는 대상 테이블을 정의

 

③ 트리거의 유형 (FOR EACH ROW에 의해 문장 레벨 트리거와 행 레벨 트리거로 나눔)

FOR EACH ROW 생략 - 문장 레벨 트리거 (영향을 받는 행이 전혀 없더라고 TRIGGER가 한번은 실행. TRIGGER 작업이 영향을 받는 행의 데이터 또는 TRIGGER 이벤트 자체에서 제공하는 데이터에 종속되지 않은 경우 유용)

FOR EACH ROW 기술 - 행 레벨 트리거 (추가되는 행의 수만큼 트리거가 동작, 행 내에서 이벤트 발생되는걸 기준으로 트리거의 감시, 보완 범위를 정함, 영향을 받는 행의 데이터나 TRIGGER 이벤트 자체에서 제공하는 데이터에 종속되는 경우 유용)

 

④ :NEW & :OLD

행 레벨 트리거에서 컬럼의 실제 데이터 값을 제어하는데 사용되는 연산자

INSERT 문: 입력된 컬럼의 값 :NEW

DELETE문: 삭제되는 컬럼의 값 :OLD,

UPDATE문: 변경 전 컬럼 데이터 값 :OLD, 수정할 새로운 데이터값 :NEW

 

⑤ 트리거의 몸체 (BEGIN ~ END)

해당 타이밍에 해당 이벤트가 발생하게 되면 실행될 기본 로직이 포함되는 부분으로 BEGIN ~ END에 기술

 

-트리거의 사용목적

데이터베이스 테이블을 생성하는 과정에서 참조 무결성과 데이터 무결성 등의 복잡한 제약 조건을 생성하는 경우
데이터베이스 테이블의 데이터에 생기는 작업을 감시, 보완
데이터베이스 테이블에 생기는 변화에 따라 필요한 다른 프로그램을 실행하는 경우
불필요한 트랜잭션을 금지하기 위해
컬럼의 값을 자동으로 생성되도록 하는 경우

 

FUNCTION: 값을 반환하는 명명된PL/SQL BLOCK으로 오라클 내장 함수와 같이 SQL 표현식의 일부로 사용하여 복잡한 SQL문을 간단한 형태로 사용 가능. 값을 반환하는 RETURN이 반드시 포함되며 반드시 하나의 값을 반환

내장 함수와 프로시저는 문법이나 특징이 거의 비슷하지만 차이점은 프로시저는 정해진 작업은 수행한 후 결과를 반환 할 수도 있고(OUT, IN OUT 모드 사용 시) 반환하지 않고 그냥 종료할 수도 있지만 함수는 정해진 작업을 수행한 후 결과를 돌려준다(RETURN)는 부분

RETURN의 데이터 타입: CHAR, DATE, NUMBER

 

CREATE [OR REPLACE] FUNCTION FUNCTION명
(매개변수 1 데이터타입, 매개변수2 데이터타입...)
RETURN 데이터타입 -- 함수가 반환할 데이터 타입 지정. 크기는 지정할 수 없다.
IS 
	지역변수 (선언)
BEGIN
	실행부
    RETURN 반환값;	--매개변수를 받아 특정 연산을 수행한 후 반환할 값 명시
    [EXCEPTION 예외 처리부]
END [함수이름];
/

 

PL/SQL 예외

컴파일 에러: PL/SQL 블록이 PARSE 되는 동안에 오타등으로 인하여 발생되는 에러

런타임 에러: PL/SQL 블록이 실행되는 동안에 발생되는 에러 (오라클에서는 예외(EXCEPTION)라고 함)

 

오라클의 예외 2가지

오라클 예외, 사용자에 의해 정의 되는 사용자 정의 예외(USER-DEFINED EXCEPTION)

 

-미리 정의되어 있는 오라클 예외들

NO_DATA_FOUND : PL/SQL SELECT문이 한건도 리턴하지 못했을 경우 발생

PROGRAM_ERROR : PL/SQL 이 내부적인 문제를 가지고 있는 경우

TOO_MANY_ROWS : PL.SQL SELECT 문이 두 건 이상의 행을 리턴했을 때

VALUE_ERROR : 산술, 변환, 절삭 또는 크기 제약에 에러가 생겼을 때 발생되는 예외

 

EXCEPTION
WHEN EXCEPTION1 [OR EXCEPTION2...] THEN
	STATEMENT1, 
    	STATEMENT2;
...
[WHEN EXEPTION3 [OR EXCEPTION4...] THEN
	STATEMENT3;
    	STATEMENT4;
 ...]

 

'DataBase+SQL' 카테고리의 다른 글

오라클 데이터베이스 페이징 처리  (0) 2023.08.30
PreparedStatement  (0) 2023.06.28
SQL 반복문, 커서  (0) 2023.06.27
PL/SQL  (0) 2023.06.26
MVIEW, 시퀀스  (0) 2023.06.23

반복문: SQL문을 반복작으로 여러번 실행하고자 할 때 사용, PL/SQL에서는 다양한 반복문이 사용

  • BASIC LOOP: 조건없이 반복 작업
  • FOR LOOP: COUNT를 기본으로 작업
  • WHILE LOOP: 조건을 기본으로 작업
  • EXIT: LOOP를 종료

BASIC LOOP: LOOP에서 END LOOP 사이를 계속 반복해서 실행(무한 루프), 여기서 빠져나가려면 EXIT문을 사용, 조건에 따라 루프를 종료할 수 있도록 WHEN 절을 덧붙일 수 있다.

LOOP
	STATEMENT1;
    	STATEMENT2;
    	EXIT [WHEN CONDITION];
END LOOP;

 

FOR LOOP: 반복되는 횟수가 정해진 반복문을 처리, 사용되는 인덱스는 정수로 자동 선언, 자동적으로 1씩 증가 또는 감소(REVERSE는 1씩 감소)

 

FOR INDEX_COUNTER IN [REVERSE] LOWER_BOUND..UPPER_BOUND LOOP
	STATEMENT1,
    	STATEMENT2;
END LOOP;

END FOR가 아니라 END LOOP!

 

WHILE LOOP: 제어 조건이 TRUE인 동안만 일련의 문장을 반복하기 위해 사용

조건은 반복이 시작될 때 체크하게 되어 LOOP내의 문장이 한번도 수행하지 않을 수 있다.

LOOP을 시작할 때 조건이 FALSE이면 반복 탈출

WHILE 조건문 LOOP
	STATEMENT1;
    	STATEMENT2;
END LOOP;

 

커서(CURSOR): 오라클 서버에서는 SQL문을 실행할 때마다 처리(Parse, Execution)를 위한 메모리 공간(SQL 커서)을 사용.

사용자가 요청하는 데이터를 데이터베이스 버퍼 캐쉬에서 커서로 복사해 온 후 커서에서 원하는 데이터를 추출(Fetch) 원하는 작업을 하게 된다. 이 메모리 공간을 Private SQL Area.

특정 SQL문장을 처리한 결과를 담고 있는 영역을 가리키는 일종의 포인터.

커서를 사용하면 처리된 SQL문장의 결과 집합에 접근 가능

 

묵시적 커서: 오라클 내부에 자동으로 생성하는 커서

명시적 커서: 사용자가 직접 정의해서 사용하는 커서

 

커서열기(OPEN) - 패치(FETCH) - 커서닫기(CLOSE): 3단계로 진행

  1. 명시적 커서 선언 (CURSOR)
  2. 명시적 커서 오픈 (OPEN)
  3. 커서에서 데이터 추출 (FETCH)
  4. 커서 사용 종료 (CLOSE)

DECLARE CURSOR(커서 선언): 사용할 커서를 선언부에 직접 정의, 사용할 커서에 이름을 부여하고 이 커서에 대한 쿼리를 선언

CURSOR 커서명 IS 커서에 담고 싶은 내용을 가져오는 서브쿼리(다중행);

CURSOR A1 IS SELECT * FROM DEPARTMENTS;

 

OPEN CUSOR(커서 열기) : 질의를 수행하고 검색 조건을 충족하는 모든 행으로 구성된 결과 셋(커서 선언 시 기술했던 서브쿼리를 수행해서 데이터를 커서로 가져오는 과정)을 생성하기 위해 CURSOR를 OPEN.

CURSOR는 결과 셋에서 첫번째 행을 가리킨다.

OPEN 커서명;
OPEN C1;

 

FETCH CURSOR(패치 단계에서 커서 사용): 정의한 커서를 열고 난 후에야 SELECT 문의 결과로 반환되는 로우에 접근 가능. 결과 집합의 로우 수는 보통 1개 이상이므로 전체 로우에 접근하기 위해서는 반복문을 사용

  • FETCH문은 결과 셋에서 로우 단위로 데이터를 읽어들임
  • FETCH 후에 CURSOR는 결과 셋에서 다음 행으로 이동
FETCH 커서명 INTO 변수들
INTO {VARIABLE1 [, VARIABLE2, ...]};

LOOP
	FETCH C1 INTO VDEPARTMENTS.DEPARTMENT_ID, VDEPARTMENTS.DEPARTMENT_NAME, VDEPARTMENTS.LOCATION_ID;
    EXIT WHER C1%NOTFROUND;
END LOOP;

 

얻어진 여러개의 행에 대한 결과값을 모두 처리하여면 반복문에 FETCH문을 기술

NOTFOUND: 커서의 상태를 알려주는 속성 중에 하나, 커서 영역의 자료가 모두 FETCH 되었다면 TRUE를 되돌린다.

즉 커서 C1 영역의 자료가 모두 FETCH되면 반복문 탈출 

 

커서의 상태

속성 의미
%NOTFOUND 커서 영역의 자료가 모두 FETCH 되었다면 TRUE
%FOUND 커서 영역에 FETCH 되지 않은 자료가 있다면 TRUE
%ISOPEN 커서가 OPEN된 상태이면 TRUE
%ROWCOUNT 커서가 얻어 온 레코드의 개수

 

CLOSE CURSOR(커서 닫기): CLOSE 문장은 CURSOR를 사용할 수 없게 하고 결과 셋의 정의를 해제

CLOSE CURSOR_NAME;
CLOSE C1;

 

CURSOR와 FOR LOOP(묵시적으로 CURSOR에서 행을 처리)

LOOP에서 각 반복마다 CURSOR를 열고 행을 인출(FETCH)하고 모든 행이 처리되면 자동으로 CURSOR가 CLOSE되므로 사용하기가 편리

 

DECLARE
    VDEPARTMENTS DEPARTMENTS%ROWTYPE;
    CURSOR C1 --커서 이름
    IS
    SELECT DEPARTMENT_ID, DEPARTMENT_NAME, LOCATION_ID
    FROM DEPARTMENTS; --부서 테이블의 전체 내용을 조회
BEGIN
    DBMS_OUTPUT.PUT_LINE('부서번호 / 부서명 / 지역명');
    DBMS_OUTPUT.PUT_LINE('-----------------------------------');
    
    OPEN C1;
    --오픈한 C1커서가 SELECT문에 의해 검색된 한개의 행의 정보를 읽어온다. 
    --포인터가 있는 레코드 한개
    LOOP --읽어온 정보는 INTO뒤에 기술한 변수에 저장
        FETCH C1 INTO VDEPARTMENTS.DEPARTMENT_ID, VDEPARTMENTS.DEPARTMENT_NAME,
        VDEPARTMENTS.LOCATION_ID;
        EXIT WHEN C1%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE(VDEPARTMENTS.DEPARTMENT_ID ||' '||
        RPAD(VDEPARTMENTS.DEPARTMENT_NAME, 20) ||' '||VDEPARTMENTS.LOCATION_ID);
    END LOOP;
    DBMS_OUTPUT.PUT_LINE(CONCAT('읽어드린 레코드수 : ', C1%ROWCOUNT));
    CLOSE C1;
END;
/

 

CURSOR와 FOR LOOP(묵시적으로 CURSOR에서 행을 처리.)

LOOP에서 각 반복마다 CURSOR를 열고 행을 인출(FETCH)하고 모든 행이 처리되면 자동으로 CURSOR가 CLOSE되므로 사용하기가 편리.

 

FOR RECORD_NAME IN CURSOR_NAME LOOP
--명시적 커서의 OPEN, FETCH가 자동적으로 수행됨
	STATEMENT1;
    STATEMENT2;
END LOOP	--루프문을 빠져 나갈 때 자동적으로 커서가 CLOSE 됨.

 

커서변수: 한 개이상의 쿼리를 연결해 사용 가능, 변수처럼 커서 변수를 함수나 프로시저의 매개변수로 전달

● 커서 변수 선언

TYPE 커서_타입명 IS REF CURSOR;	--커서 타입선언(생략)
커서_변수명 커서_타입명;	-- 커서변수 선언

 

오라클에서 제공하는 커서 타입(결과 집합이 고정되어 있지 않으므로)인 SYS_REFCURSOR이란 타입을 사용하는 것,

따라서 SYS_REFCURSOR를 사용할 때는 별도로 커서 타입을 선언할 필요없이 다음과 같이 커서 변수만 선언하면 된다.

커서변수 SYS_REFCURSOR;	--커서변수 선언

 

● 커서변수의 사용

① 커서 변수와 커서 정의 쿼리문 연결

커서를 정의하는 쿼리가 있어야 하는데 커서변수와 쿼리문을 연결할 때 다음과 같이 OPEN...FOR 구문을 사용

OPEN 커서변수명 FOR SELECT 문;

 

② 커서 변수에서 결과집합 가져오기

커서를 구성하는 쿼리에 커서 변수까지 연결했으니 커서 변수에 결과 집합ㄴ을 가져오는 패치 작업이 남았는데, 이때도 FETCH문 사용

FETCH 커서변수명 INTO 변수1, 변수2,...;

 

● 커서변수의 사용

DECLARE
    VFIRST_NAME EMPLOYEES.FIRST_NAME%TYPE;
    --TYPE EMPLOYEESCURSOR IS REF CURSOR;   --커서 타입 선언
    --VEMPLOYEES EMPLOYEESCURSOR;   --커서 변수 선언
    VEMPLOYEES SYS_REFCURSOR;    --오라클 서버가 제공하는 커서타입(SYS_REFCURSOR)으로 커서변수 선언.
BEGIN
    --커서 변수를 사용한 커서 정의 및 오픈
    OPEN VEMPLOYEES FOR SELECT FIRST_NAME FROM EMPLOYEES WHERE DEPARTMENT_ID = 90;
    
    --LOOP문
    LOOP
        --커서 변수를 사용해 결과 집합을 EMPNAME 변수에 할당
        FETCH VEMPLOYEES INTO VFIRST_NAME;
        EXIT WHEN VEMPLOYEES%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE('사원명 : ' ||VFIRST_NAME);   --사원명을 출력
    END LOOP;
END;
/

 

ORACLE SUBPROGRAM

익명 블록 서브 프로그램
이름이 지정되지 않은 PL/SQL 블록 이름이 지정된 PL/SQL 블록
매번 사용시마다 컴파일 최초 실행될 때 한번만 컴파일
데이터베이스에 저장되지 않습니다. 데이터베이스에 저장
다른 응용프로그램에서 호출 불가 다른 응용프로그램에서 호출 가능
값 반환X 함수일 경우 값 RETURN, RETURN이 없는 함수 존재X
파라미터 사용X 파라미터 사용O

1) 저장 프로시저(Stored Procedure: 스토어드 프로시저)

프로시저 : 지정된 특정 처리를 실행하는 서브 프로그램의 한 유형.

저장 프로시저: 자주 사용되는 쿼리문을 모듈화시켜서 필요할 때마다 호출하여 사용하는 것

CREATE [OR REPLACE] PROCEDURE PRCEDURE_NAME
(매개변수1 [IN(MODE)] 자료형,	--MODE에 따라 프로시저를 호출 시 값을 전달 받거나 프로시저 호출 후
매개변수2 [OUT(MODE)] 자료형...)	--해당 매개 변수 값을 받아 사용, IN/OUT/IN OUT,IN은 생략 가능
--OUT은 생략 불가능
IS
	LOCAL_VARIABLE DECLARATION	--변수선언
BEGIN
	STATEMENT1;
END [PRCEDURE_NAME];

USER_SOURCE: 저장 프로시저를 작성한 후 사용자가 저장 프로시저가 생성되었는지 확인

 

① 매개 변수: 프로시저 이름 뒤에 ()를 기술하여 그 내부에 매개변수를 정의

변수명 모드 자료형

 

--프로시저 생성               프로시저명 ( 변수명         입력             자료형)
CREATE OR REPLACE PROCEDURE EMPPROC02 (VDEPARTMENT_ID IN EMPLOYEES.DEPARTMENT_ID%TYPE)
IS
    CURSOR C1
    IS
    SELECT * FROM EMPLOYEES WHERE DEPARTMENT_ID = VDEPARTMENT_ID;
BEGIN
    DBMS_OUTPUT.PUT_LINE('사원번호 / 사원명 / 급여');
    DBMS_OUTPUT.PUT_LINE('---------------------------------------');
    FOR VEMPLOYEES IN C1 LOOP
        DBMS_OUTPUT.PUT_LINE(VEMPLOYEES.EMPLOYEE_ID||' / '||VEMPLOYEES.FIRST_NAME||' / ' ||
                            VEMPLOYEES.SALARY);
    END LOOP;
END;
/
SHOW ERROR;

 

--프로시저 실행
--[형식]
--EXECUTE 또는 EXEC 프로시저;
--위 코드는 주석과 함께 명시할 수 없다.
--부서번호가 90
EXECUTE EMPPROC02(90);

 

IN 모드 OUT 모드 (RETURN과 같은 역할) IN OUT 모드
기본모드. 명시적으로 지정 명시적으로 지정
값이 서브 프로그램에 전달됨 값이 호출환경에 반환 값이 서브 프로그램에도 전달되고 호출환경에도 반환
실제 파라미터가 리터럴 표현식, 상수 또는 초기화된 변수가 될 수 X 변수만 사용가능 변수만 사용가능
기본값을 할당할 수 있음 기본값 할당 불가 기본값 할당 불가

 

● IN MODE 매개변수: 실행환경에서 서브 프로시저로 값 전달.

부서별로 SALARY 인상. 부서코드가 10이면 10% 인상, 20이면 20%인상, 나머지 동결.

 

CREATE OR REPLACE PROCEDURE EMPPROC_INMODE
(VDEPARTMENT_ID IN EMPLOYEES01.DEPARTMENT_ID%TYPE)
IS
BEGIN
    UPDATE EMPLOYEES01 SET SALARY = DECODE(VDEPARTMENT_ID, 10, SALARY*1.1, 20, 
    SALARY*1.2, SALARY)
    WHERE DEPARTMENT_ID = VDEPARTMENT_ID;
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('수정이 완료되었습니다.');
END EMPPROC_INMODE;
/

 

PreparedStatement

Connection 객체의 prepareStatement() 메소드를 사용하여 객체 생성

SQL문장이 미리 컴파일 되고 실행 시간동안 인수 값을 위한 공간을 확보한다는 점에서 Statement와 다름

각각의 인수에 대해 위치홀더(?)를 사용하여 SQL문장을 정의할 수 있게 함

 

PreparedStatement pstmt = null;
try{
	String query = "INSERT INTO member(id, password, name)
    				VALUES(?,?,?)";	//?: 바인딩 변수
    pstmt = conn.prepareStatement(query);
    pstmt.setString(1, "javauser");
    pstmt.setString(2, "java1234");
    pstmt.setString(3, "홍길동");
}catch(SQLException e){e.printStackTrace();}

 

'DataBase+SQL' 카테고리의 다른 글

PROCEDURE TRIGGER  (0) 2023.06.29
PreparedStatement  (0) 2023.06.28
PL/SQL  (0) 2023.06.26
MVIEW, 시퀀스  (0) 2023.06.23
뷰(VIEW)  (0) 2023.06.22

자주 사용하는 명령을 PL/SQL 구문을 이용하여 데이터베이스에 저장하고 필요할 때 호출하여 사용하는 방법 연습

1)PL/SQL: Oracle's Procedural Language extension to SQL의 약자

SQL문장에서 변수  정의, 조건처리(IF), 반복처리(LOOP, WHILE, FOR)등을 지원

DB에 직접 탑재되어 컴파일, 실행 => 성능 우수, DB 관련 처리를 할 때 수많은 기능을 제공, 일반 프로그래밍 언어적인 요소를 거의 다 가지고 있어서 실무에서 요구되는 절차적인 데이터 처리를 다 할 수 있음. 특히 SQL과 연동되어 막강할 기능 구현 가능

 

PL/SQL은 SQL에 없는 기능 제공

  • 변수 선언 가능
  • 비교 처리 가능
  • 반복 처리 가능

PL/SQL 기본 단위: 블록(BLOCK)=> 선언부, 실행부, 예외처리부 구성

                                                     => 이름이 없는 블록(익명 블록: 처음부터 마지막까지 한꺼번에 정의), 이름이 있는 블록                                                             (함수, 프로시저, 패키지) 구분

 

DECLEARE
	선언부(DECLARE SECTION)
    -변수나 상수를 선언
BEGIN
	실행부(EXECUTABLE SECTION)
    SQL문
    제어문, 반복문
    커서
EXCEPTION 
	예외 처리부(EXCEPTION SECTION)
END;
/

 

장점

  • 프로그램의 모듈화: 특정 프로그램을 Procedure, function, package등의  프로그램으로 만들어 여러 응용 프로그램을 만들시 공통으로 이용 및 관리 할 수 있다.
  • 관리의 용이
  • 네트워크 입출력 감소: 프로시저 등을 실행하는 구문만 데이터베이스에 보낼 수 있어 네트워크 입출력을 현격하게 줄일 수 있다.

특징

  • PL/SQL 블록 내에서는 한 문장이 종료할 때마다 세미콜론(;)을 사용
  • END 뒤에 ;을 사용하여 하나의 블록이 끝났다는 것을 명시
  • DECLARE나 BEGIN이라는 키워드로 PL.SQL 블록이 시작하는 것을 알 수 있다.
  • 단일 행 주석은 --이고 여러 행 주석을 /**/이다.
  • PL/SQL 블록은 행에 /가 있으면 종료

 

자바
자료형 변수 = 값;		--대입연산자
비교 연산자 ==		--비교

오라클
변수 자료형 := 값;	--대입
비교 연산자 =		--비교

SELECT 컬럼1, 컬럼2 INTO 변수1, 변수2	--변수에 데이터 담기
FROM 테이블
WHERE 조건문

 

모든 문장의 종결 기호는 세미콜론으로 명시, 대입 연산자로는 := 사용, 대입 연산자는 변수의 선언 시 및 변수의 대입에 이용

변수 자료형 := 값;

SELECT문에 의해 추출되는 DATA는 INTO절의 변수에 저장해서 처리

 

--'Hello World!' 출력
BEGIN
    DBMS_OUTPUT.PUT_LINE('Hello World!');       --DBMS_OUTPUT: Package, 오라클 문장 작은 따옴표
END;
/

 

보기 - DBMS 출력 - +에서 사용자 설정

 

DECLARE
    VEMPLOYEE_ID NUMBER(6);     --변수 자료형
    VFIRST_NAME VARCHAR2(20);
BEGIN
    VEMPLOYEE_ID := 105;
    VFIRST_NAME := 'David';
    
    DBMS_OUTPUT.PUT_LINE('사번 / 이름');
    DBMS_OUTPUT.PUT_LINE('-----------');
    DBMS_OUTPUT.PUT_LINE(VEMPLOYEE_ID || ' / ' || VFIRST_NAME);
END;
/

 

1) 변수

IDENTIFIER [CONSTANT] DATATYPE [NOT NULL] [:=|DEFAULT EXPRESSION];

 

구문 설명
IDENTIFIER 변수의 이름
CONSTANT 변수의 값을 변경할 수 없도록 제약
DATATYPE 자료형(데이터 타입)을 기술
-변수 데이터 타입: SQL 타입(NUMBER, CHAR, VARCHAR2,DATE,...) 과 PL/SQL 타입
-PL/SQL 데이터 타입: BOOLEAN, BINARY_INTEGER
NOT NULL 값을 반드시 포함하도록 하기 위해 변수를 제약
EXPRESSION LITERAL, 다른 변수, 연산자나 함수를 포함하는 표현식

 

PL/SQL에서 변수를 선언할 때 사용되는 자료형은 SQL에서 사용하던 자료형과 거의 유사

상수명 CONSTANT 데이터 타입 := 상수값;

IDENTIFIER := EXPRESSION;
DECLARE
	VAL_NUM NUMBER;
BEGIN
	VAL_NUM := 100;
    DBMS_OUTPUT.PUT_LINE(VAL_NUM);	--DBMS_OUTPUT: 패키지, PUT_LINE: 프로시저
END;
/

2] 스칼라 변수 / 레퍼런스 변수 : PL/ SQL에서 변수를 선언하기 위해 사용할 수 있는 데이터 형

 

스칼라: SQL에서의 자료형 지정과 거의 동일

--변수 자료형
VEMPLOYEE_ID NUMBER(6);
VFIRST_NAME VARCHAR2(20);

 

레퍼런스: %TYPE 속성과 %ROWTYPE 속성 사용, 이전에 선언된 다른 변수 또는 데이터베이스의 칼럼에 맞추어 변수를 선언하기 위해 %TYPE 속성을 사용

 

--변수 테이블. 컬럼(자료형 참조)%TYPE
VEMPLOYEE_ID EMPLOYEES.EMPLOYEE_ID%TYPE;
VFIRST_NAME EMPLOYEES.FIRST_NAME%TYPE;

%TYPE 속성을 사용하여 선언한 VEMPLOYEE_ID 변수는 EMPLOYEES 테이블의 EMPLOYEE_ID 칼럼의 자료형과 크기를 그대로 참조해서 정의

 

%ROWTYPE은 로우 단위로 참조(레코드 하나)

VEMPLOYEES EMPLOYEES%ROWTYPE;

 

%ROWTYPE의 장점: 칼럼의 개수와 데이터 형식을 모르더라도 지정 가능, SELECT 문장으로 로우 검색할 때 유리

 

3]PL/SQL에서 SQL 문장

PL/SQL의 SELECT 문은 INTO 절이 필요한데, INTO절에 데이터를 저장할 변수를 기술

SELECT절에 있는 칼럼은 INTO 절에 있는 변수와 1대1 대응을 하기에 개수와 데이터형, 길이가 일치하여야한다.

SELECT SELECT_LIST
INTO {VARIABLE_NAME1 [,VARIABLE_NAME2,...] |RECORD_NAME}
FROM TABLE_NAME
WHERE CONDITION;

 

 구문 설명
SELECT_LIST 열의 목록이며 행 함수, 그룹 함수, 표현식을 기술할 수 있다.
VARIABLE_NAME 읽어들인 값을 저장하기 위한 스칼라 변수
RECORD_NAME 읽어 들인 값을 저장하기 위한 PL/SQL RECORD 변수
CONDITION PL/SQL 변수와 상수를 포함하여 열명, 표현식, 상수, 비교 연산자로 구성되며 오직 하나의 값을 RETURN할 수 있는 조건이어야 한다.

 

4] 전체 레코드를 참조하기 위해서는 %ROWTYPE으로 선언

 

DECLARE 
    --레코드로 변수 선언
    VEMPLOYEES EMPLOYEES%ROWTYPE;
BEGIN
    --Lisa 사원의 정보를 레퍼런스 변수에 저장
    SELECT *
    INTO VEMPLOYEES
    FROM EMPLOYEES
    WHERE FIRST_NAME = 'Lisa';
    
    --레코드 변수에 저장된 사원 정보를 출력
    DBMS_OUTPUT.PUT_LINE('사원번호 : ' || TO_CHAR (VEMPLOYEES.EMPLOYEE_ID));
    DBMS_OUTPUT.PUT_LINE('이 름 : ' ||VEMPLOYEES.FIRST_NAME);
    DBMS_OUTPUT.PUT_LINE('급 여 : ' ||VEMPLOYEES.SALARY);
    DBMS_OUTPUT.PUT_LINE('입사일자 : ' || TO_CHAR(VEMPLOYEES.HIRE_DATE, 'YYYY-MM-DD'));
    DBMS_OUTPUT.PUT_LINE('부서번호 : ' || VEMPLOYEES.DEPARTMENT_ID);
END;
/

 

--예제 EMPLOYEES 테이블에 등록된 총 사원의 수와 급여의 합, 급여의 평균을 변수에 대입하여 출력
DECLARE
    VCNT NUMBER;
    VSUM NUMBER;
    VAVG NUMBER(10,2);
BEGIN
    SELECT COUNT(*), SUM(SALARY), AVG(SALARY) INTO VCNT, VSUM, VAVG
    FROM EMPLOYEES;
    
    DBMS_OUTPUT.PUT_LINE('총사원의 수 : ' || VCNT);
    DBMS_OUTPUT.PUT_LINE('급여의 합 : ' || VSUM);
    DBMS_OUTPUT.PUT_LINE('급여의 평균 : ' ||VAVG);
END;
/
--예제 Jack 사원의 직무, 급여, 입사일자, 부서명을 변수에 대입하여 출력
DECLARE
    VEMPLOYEES EMPLOYEES%ROWTYPE;
    VDEPARTMENTS DEPARTMENTS%ROWTYPE;
BEGIN
    SELECT JOB_ID, SALARY, HIRE_DATE, DEPARTMENT_NAME
    INTO VEMPLOYEES.JOB_ID, VEMPLOYEES.SALARY, VEMPLOYEES.HIRE_DATE, VDEPARTMENTS.DEPARTMENT_NAME
    FROM EMPLOYEES EMP INNER JOIN DEPARTMENTS DEPT
    ON EMP.DEPARTMENT_ID = DEPT.DEPARTMENT_ID
    WHERE FIRST_NAME='Jack';
    
    DBMS_OUTPUT.PUT_LINE('직무 : ' || VEMPLOYEES.JOB_ID);
    DBMS_OUTPUT.PUT_LINE('급여 : ' || VEMPLOYEES.SALARY);
    DBMS_OUTPUT.PUT_LINE('입사일자 : ' || TO_CHAR( VEMPLOYEES.HIRE_DATE, 'YYYY-MM-DD'));
    DBMS_OUTPUT.PUT_LINE('부서명 : ' || VDEPARTMENTS.DEPARTMENT_NAME);
END;
/

 

--사원 테이블 (EMPLOYEES01)에서 사원번호가 제인 큰 사원을 찾아낸 뒤 ,
--이 번호 +3번으로 아래의 사원을 사원테이블에 신규 입력하는 
--PL/SQL을 만들어 보자

--복사 테이블 생성
CREATE TABLE EMPLOYEES01
AS
SELECT * FROM EMPLOYEES;

DECLARE
    MAX_EMPLOYEE_ID EMPLOYEES01.EMPLOYEE_ID%TYPE;
BEGIN
    SELECT MAX(EMPLOYEE_ID) INTO MAX_EMPLOYEE_ID FROM EMPLOYEES01;
    
    INSERT INTO EMPLOYEES01 (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY, HIRE_DATE,
                            MANAGER_ID, JOB_ID, DEPARTMENT_ID)
    VALUES (MAX_EMPLOYEE_ID +3, 'Olivia', 'Gee', 'Spring', 2800, SYSDATE, 100, 'PR_REP', 20);
    
    COMMIT;
END;
/
SELECT * FROM EMPLOYEES01;

 

3) 제어문: 조건에 의해 분기하는 선택문과 반복된 문장을 한번 기술하고도 여러변 수행하도록 하는 반복문이 있다.

 

1] IF ~ THEN ~ END IF: 특정 조건을 만족하면 처리를 하고, 그렇지 않으면 아무런 처리X

IF 조건문 THEN
	조건처리;
END IF;

 

2] IF ~ THEN ~ ELSE ~ END IF: 조건을 만족할 때의 처리와 그렇지 않을 때의 처리, 즉 두 가지 처리문 중에서 한개를 선택해야 할 때 사용

IF 조건문 THEN
	조건처리1;
ELSE
	조건처리2;
END IF;

 

DECLARE
    --%ROWRYPE 속성으로 로우를 저장할 수 있는 레퍼런스 변수 선언
    VEMPLOYEES EMPLOYEES%ROWTYPE;
    VSALARY NUMBER(8,2);
BEGIN
    DBMS_OUTPUT.PUT_LINE('사번 / 이름 / 급여');
    DBMS_OUTPUT.PUT_LINE('---------------------------------------');
    --Jack 사원의 전체 정보를 로우 단위로 얻어와 VEMPLOYEES에 저장
    SELECT * INTO VEMPLOYEES
    FROM EMPLOYEES
    WHERE FIRST_NAME ='Jack';
    --커미션이 NULL일 경우 급여만
    IF (VEMPLOYEES.COMMISSION_PCT IS NULL) THEN --커미션이 NULL이면
        VSALARY := VEMPLOYEES.SALARY;
    ELSE    --커미션이 NULL이 아니면 급여+수당
        VSALARY := VEMPLOYEES.SALARY + (VEMPLOYEES.SALARY * VEMPLOYEES.COMMISSION_PCT);
    END IF;
    
    --레퍼런스 변수와 스칼라 변수에 저장된 값을 출력
    DBMS_OUTPUT.PUT_LINE('사원번호 : ' ||VEMPLOYEES.EMPLOYEE_ID ||
                        ' / 사원명 : ' || VEMPLOYEES.FIRST_NAME ||
                        ' / 급여 : ' || TO_CHAR(VSALARY, '$999,999'));
END;
/

 

3] IF ~ THEN ~ ELSIF ~ ELSE ~ END IF: 여러 개 조건에 따라 처리도 여러 개일 때 사용하는 다중 IF

 

IF 조건문1 THEN
	조건처리 1;
ELSIF 조건문2 THEN
	조건처리 2;
ELSIF 조건문3 THEN
	조건처리 3;
ELSE
	조건처리N;
END IF;

 

DBMS RANDOM.VALUE

랜덤한 숫자를 생성

형식: DBMS_RANDOM.VALUE(최소 범위의 숫자, 최대 범위의 숫자)

 

--소수점 3번 째 자리까지 나타냄
SELECT ROUND(DBMS_RANDOM.VALUE(1,5),3) FROM DUAL;

SELECT ROUND(DBMS_RANDOM.VALUE(10,270),-1) FROM DUAL;

 

DBMS_RANDOM.STRING

랜덤한 문자열 생성

형식: DBMS_RANDOM.STRING(옵션문자, 길이수)

 

-옵션문자

'u','U': 대문자 , 'l','L': 소문자 , 'a', 'A': 대소문자 구분없는 영문자 ,'x', 'X': 영문자와 숫자 혼합 , 'p', 'P': 문자 혼합

 

DECLARE
    VSALARY NUMBER := 0;
    VDEPARTMENT_ID NUMBER := 0;
BEGIN
    SELECT ROUND(DBMS_RANDOM.VALUE(10, 270), -1) INTO VDEPARTMENT_ID FROM DUAL;
    --랜덤 숫자가 출력됨.
    
    SELECT SALARY INTO VSALARY
    FROM EMPLOYEES
    WHERE DEPARTMENT_ID = VDEPARTMENT_ID AND ROWNUM =1;
    
    DBMS_OUTPUT.PUT_LINE('부서번호: '|| VDEPARTMENT_ID ||' 급여 : ' ||VSALARY);
    
    IF VSALARY BETWEEN 1 AND 6000 THEN
        DBMS_OUTPUT.PUT_LINE('낮음');
    ELSIF VSALARY BETWEEN 6001 AND 10000 THEN
        DBMS_OUTPUT.PUT_LINE('중간');
    ELSIF VSALARY BETWEEN 10001 AND 20000 THEN
        DBMS_OUTPUT.PUT_LINE('높음');
    ELSE
        DBMS_OUTPUT.PUT_LINE('최상위');
    END IF;
EXCEPTION
    WHEN NO_DATA_FOUND THEN		--데이터 발견, 존재 X
    DBMS_OUTPUT.PUT_LINE(VDEPARTMENT_ID || '부서에 해당 사원이 없습니다.');
END;
/

 


JDBC(Java DataBase Connectivity): java.sql, javax.sql로 구성

자바 프로그램에서 데이터베이스와 연결하여 데이터베이스 관련 작업을 할 수 있게 해주는 API, 다양한 종류의 관계형 데이터베이스에 접근할 때 사용되는 자바 표준 SQL 인터페이스

 

JDBC는 다음과 같은 데이터베이스 기능을 지원하기 위한 표준 API를 제공

  • 데이터베이스를 연결하여 테이블 형태의 자료를 참조
  • SQL 문을 질의
  • SQL 문의 결과를 처리

각 DBMS와 관령하여 회사도 많고 그로 인해 사용하는 방법이 달라지면 X >> 이러한 문제를 해결하기 위해 SUN은 표준이 되는 인터페이스 제공

 

데이터베이스를 만드는 회사는 SUN사에서 JDBC인터페이스를 통해서 자신들의 데이터베이스에 맞게 기능들을 구현하는 것, 인터페이스만 알면 데이터베이스 조작 가능

 

JDBC드라이버(자료): 데이터베이스 회사들이 SUN사의 표준이 되는 인터페이스를 상속받아 구현해 놓은 것, 해당 DBMS에서 JDBC 관련 API 호출이 가능하도록 관련 인터페이스와 클래스를 구현한 클래스 라이브러리

JDBC는 SQL문을 DBMS에 전달하고 결과 셋을 반환해 주는 중개자 역할을 담당, 결과로 받은 데이터를 자바에서 사용할 수 있는 형태로 변환하여 프로그램에 반환

DBMS: 질의문을 컴파일하여 요청한 작업을 처리

java-컴파일->.class -->배표시 class 파일만 압축파일: .jar

 

JDBC: 자바에서 데이터베이스에 접근할 수 있게 해주는 PROGRAMMING API

THIN DRIVER: 확장, 배포 용이 WEB 적합

 

데이터베이스 인터페이스 사용하기 위해

프로젝트 우클릭, properties, javabuild path, add external jars

 

관계형 데이터베이스는 데이터를 여러 개의 테이블에 나누어서 저장

결과 집합(ResultSet): 쿼리의 조건을 만족하는 레코드들의 집합

커서(cursor): 특정 SQL 문장을 처리한 결과를 담고 있는 영역을 가리키는 일종의 포인터

 

JDBC를 이용하여 데이터베이스에 데이터를 액세스하기 위해서는 기본적인 순서

1.java.sql.*;

JDBC API를 사용하기 위해서는 반드시 선언

2.JDBC 드라이버 로딩 및 레지스터 등록

Class.forName() 사용

3.데이터베이스 연결(Connection 객체 얻는다.)

DriverManager.getConnnection() 사용

4.SQL을 위한 Statement 객체 생성

Connection.createStatement() 사용

5.SQL 실행

executeQuery() / executeUpdate()/ execute() 사용

6.ResultSet 결과셋 얻기

SQL문의 실행 결과셋 얻기.ResultSet에 저장된 데이터 값 추력

7.객체 자원 반납(연결 해제)

close() 사용

 

예외처리

-비체크 예외(runtime, nullpoint,...exception)

-체크 예외(반드시 예외처리) : 입출력

1.파일 입출력

2.네트워크 입출력

3.데이터베이스 입출력

 

JDBC관련 기본 클래스

패키지 인터페이스(클래스) 클래스 용도 이용 메소드
java.lang 클래스
Class
지정된 JDBC 드라이버를 실행시간동안 메모리에 로드 forName();
java.sql 클래스
DriverManager
여러 JDBC 드라이버를 관리하는 클래스로 데이터베이스를 접속하여 연결 객체 반환 getConnection();
인터페이스
Connection
특정한 데이터베이스 연결 상태를 표현하는 클래스로 질의할 문장 객체 반환 createStatement();
close();
인터페이스
Statement
데이터베이스에 SQL 질의 문장을 질의하여 그 결과집합 객체를 반환 executeQuery();
close();
인터페이스
ResultSet
질의 결과의 자료를 저장하며 테이블 구조 next(); getString(); getInt();
close();

 

데이터베이스에 연결하기 위해서는 2가지 과정이 필요하다.

가장 먼저 드라이버 클래스를 로딩, DB와 접속을 시도

 

DriverManager 클래스는 Class.forName() 메소드를 통해서 생성

드라이버 클래스들은 로드될 때 자신의 인스턴스(객체)를 생성하고, 자동적으로 DriverManager 클래스는 메소드를 호출하여 그 인스턴스를 등록

 

드라이버 클래스를 찾지 못할 경우 forName() 메소드는 ClassNotFoundException 예외를 발생시키므로 반드시 예외 처리

 

DriverManager: 데이터 원본에 JDBC드라이버를 통하여 커넥션을 만드는 역할, 데이터베이스 연결에 관한 책임을 가지고 있는 객체, Class.forName() 사용하고자 하는 JDBC 드라이버를 지정하여 해당 드라이버 내부에 있는 클래스들을 메모리에 로드하며 반드시 예외 처리를 해야함.

 

Connection: DB의 연결 정보를 담은 객체, 특정 데이터 원본과 연결된 커넥션을 나타낸다. 직접 객체 생성이 불가능하고 getConnection() 메소드를 사용하여 객체 생성 Statement 객체를 생성할 때도 Connection 객체를 사용하여 createStatement() 메소드를 호출하여 생성, SQL 문장을 실행시키기 전에 우선 Connection 객체가 있어야 함.

 

[형식 1]

1-1.Class.forName("드라이버 클래스명")

      Class.forName("oracle.jdbc.driver.OracleDriver") 또는

      Class.forName("oracle.jdbc.OracleDriver")

 

1-2.DriverManager.registerDriver(

                                new orcle.jdbc.driver.OracleDriver());

 

1-3. Connection conn = DriverManager.getConnection(

                                     "연결 url", "사용자명", "password");

 

 

JDBC 드라이버 로딩 & Connection 객체 생성

[형식 1]

Class.forName("oracle.jdbc.driver.OracleDriver"); 또는

Class.forName("oracle.jdbc.OracleDriver");

 

<javaproject 우클릭, properties, javabuilepath, classpath, ojdbc8.jar>

Statement

Connection 객체를 통해 DB에 SQL문을 전달하여 실행 시키고 결과 값을 반환받는 역할을 하는 객체

Connection 클래스의 createStatement() 메소드를 호출하여 얻어지며 생성된 Statement 객체로 질의 문장을 String객체에 담아 인자로 전달하여 executeQuery() 메소드를 호출하여 SQL질의 수행

 

executeUpdate(String sql) - SQL문이 insert, update, delete문 등일 경우

Statement stmt = conn.createStatement();

StringBuffer sb = new StringBuffer();
sb.append("UPDATE employees SET hire_date = sysdate ");
sb.append(" WHERE employee_id = 100");	//where절 앞에 공백
int updateCount = stmt.executeUpdate(sb.toString());

int executeUpdate(String sql) 사용시   //입력, 수정, 삭제

  • executeUpdate() 메소드의 매개변수의 자료형은 String이다.   //반환형 String
  • 문자열, 날짜형 데이터 싱클쿼테이변(')으로 표현해야 한다.
  • 데이터는 대소문자를 구분한다.

 

executeQuery(String sql) - SQL문이 select일 경우

Statement s = conn.createStatement();
String sql = "SELECT employee_id, first_name FROM employees";
ResultSet rs = s.executeQuery(sql);

Statement stmt = conn.createStatement();
StringBuffer sb = new StringBuffer();
sb.append("SELECT employee_id, first_name);
sb.append("FROM employees ");
ResultSet rs = stmt.executeQuery(sb.toString());

 

문자열 결합 연산자 +

-원본 문자열이 변경되지 않으니 문자열을 수정할 때마다 메모리에 새로운 문자열이 생성된고, 이전 문자열은 메모리에 남아 쓸데없는 자리만 차지하게 된다.

 

StringBuffer, StringBuilder

-StringBuffer 객체에 저장된 문자열은 수정할 수 있다.

-String 객체와 다르게 StringBuffer 객체는 +연산자를 통해 매번 새로운 String 객체를 생성하지 않고, append() 메서드를 통해 String 객체 하나만으로도 문자열을 다룰 수 있다.

-버퍼(buffer: 데이터를 임시로 저장하는 메모리)에 문자열 저장

-버퍼 내부에서 추가, 수정, 삭제 작업 가능

 

결과 집합(ResultSet)에서 레코드에 하나씩 접근하여서 작업을 하여야 한다. executeQuery() 메소드에 의하여 반환된 ResultSet 객체에는 SELECT문장에 의하여 추출된 모든 레코드가 들어 있다. 하지만 한 번에 하나의 레코드만 접근할 수 있다. 이것을 위하여 커서(특정 sql 문장을 처리한 결과를 담고 있는 영역을 가리키는 일종의 포인터 )라는 포인터가 제공되는데 이 커서를 움직이는 다양한 메소드가 제공

 

메소드 설명
close() 결과 집합은 닫는다.
first() 커서를 처음 레코드로 옮긴다.
last() 커서를 마지막 레코드로 옮긴다.
getRow() 현재 레코드 번호를 얻는다.
next() 다음 레코드로 이동한다.
previous() 이전 레코드로 이동한다.
absolute(int row) 지정된 row로 커서를 이동한다.
isFirst 첫 레코드이면 true 반환
isLast() 마지막 레코드이면 true 반환

 

메소드의 매개변수로 칼럼명으로 접근할 수 있고, 쿼리문 작성시 컬럼 순번으로 접근할 수 있다.

 

Statement stmt = conn.createStatement();
StringBuffer sb = new StringBuffer();
sb.append("SELECT employee_id, first_name ");
sb.append("FROM employees ");
ResultSet rs = stmt.executeQuery(sb.toString());

int empno = rs.getInt("employee_id");
String ename = rs.getString("first_name");

int empno = rs.getInt(1);
String ename = rs.getString(2);

 

자바에서는 인덱스 번호가 0부터 시작하지만 SQL에서는 1부터 시작한다.

결과 집합 처리 시 체크 사항

  • rs.next()를 맨 처음 한번 반드시 실행하여야 한다. 결과를 얻은 객체의 가장 처음 로우에 커서를 옮기는 작업으로 생각하면 된다. 만약 결과가 없다면 false를 리턴
  • ResultSet 객체에 next()를 호출함으로써 해당 객체는 getXXX() 메소드를 호출할 준비를 하게된다.
  • getInt(), getString(), getDate() 등 여러가지 데이터 형에 대한 메소드가 존재
  • 자바의 인덱스의 시작은 0부터이지만, 데이터베이스는 1부터 시작한다.

결과 집합에서 이동: 한 레코드씩 처리, 다음 행으로 이동시 next() 메소드를 사용

ResultSet 반환

BOF(Begin Of File, Before the First Row): 질의 결과 처음 커서 위치

EOF(End Of File, After the Last Row)

 

ResultSet: SELECT문을 사용한 질의 성공 시 ResultSet 반환

 

JDBC 코딩 절차

Driver 등록 - DBMS 연결 - Statement 생성 - SQL 전송 - 결과 받기 - 닫기(객체반환)

Statement method를 이용하여 SQL문 실행, 실행결과를 ResultSet(Select) 혹은 int형 변수(DML)로 받아서 처리

String query = "UPDATE subject SET s_name = '전기전자학과' WHERE s_num = '03'";
int result = stmt.executeUpdate(query);

 

'DataBase+SQL' 카테고리의 다른 글

PreparedStatement  (0) 2023.06.28
SQL 반복문, 커서  (0) 2023.06.27
MVIEW, 시퀀스  (0) 2023.06.23
뷰(VIEW)  (0) 2023.06.22
서브 쿼리  (0) 2023.06.21

View는 원본 테이블에 가서 데이터를 가져올 후 사용자에게 반환하기 때문에 부하가 많이 발생. (요청할 때 마다 접근)

 

11) Materialized View (MVIEW): 구체화된 뷰

Mview는 사용자가 요청하는 데이터를 가지고 있다가 요청이 들어오면 즉시 사용자에게 보내는 형태로 처리.

View는 데이터가 없기 때문에 실체가 없는 테이블이라고 하며 Mview는 데이터를 가지고 있기 때문에 "실체화된 View"라고 한다. => 사용자가 많고 데이터가 많을수록 Mview를 사용하는 것이 더 효율적이고 성능이 좋다.

 

Mview를 사용하기 위해 QUERY REWRITE라는 권한과 CREATE MATERIALIZED VIEW 권한이 필요

GRANT QUERY REWRITE TO HR;
GRANT CREATE MATERIALIZED VIEW TO HR;
--PDB_SYS로 권한 부여
--GRANT QUERY REWRITE TO HR;
--GRANT CREATE MATERIALIZED VIEW TO HR;

--MATERIALIZED VIEW(MVIEW): 구체화된 뷰 생성
CREATE MATERIALIZED VIEW M_EMP
BUILD IMMEDIATE --서브쿼리 부분을 수행해서 데이터를 가져오라는 의미
REFRESH
ON DEMAND --원본테이블 변경 시 동기화 여부 결정 사용자가 수동으로 동기화 명령을 수행해서 설정, 먼저 동기화를 쓰고 
--ON COMMIT 옵션은 원본 테이블에 데이터 변경 후 COMMIT이 발생하면 자동으로 동기화시키라는 의미.
COMPLETE --MVIEW 내의 데이터 전체가 원본 테이블과 동기화 되는 방법.
ENABLE QUERY REWRITE
AS
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY FROM EMP01;

SELECT * FROM M_EMP;

DELETE FROM EMP01 WHERE EMPLOYEE_ID = 250;

SELECT COUNT(*) FROM EMP01; --107 : 데이터를 지워서 적어짐
SELECT COUNT(*) FROM M_EMP; --108 : 개수가 달라짐. 기존 테이블의 데이터로 존재
--DBMS_MVIEW 패키지의 REFRESH구문으로 동기화를 수행, 자동으로 동기화하지 않고 수동으로 동기화를 해서 한번에 적용
--BIGIN부터 END까지 BLOCK설정

BEGIN
    DBMS_MVIEW.REFRESH('M_EMP');
END;
/
--오라클에서는 패키지.프로시저명을 사용

SELECT COUNT(*) FROM M_EMP; --107 : 동기화됨.

--확인
SELECT MVIEW_NAME, QUERY FROM USER_MVIEWS
WHERE MVIEW_NAME = 'M_EMP';

DROP MATERIALIZED VIEW M_EMP;
--변경 시 ALTER 사용 X, 삭제하고 다시 CREATE

 

PARTITION BY

오라클에서 그룹함수를 사용할 때 PARTITION BY를 사용하여 그룹으로 묶어서 연산.

GROUP BY 를 사용하지 않고, 조회된 각 행에 그룹으로 집계된 값을 표시 할 때 OVER 절과 함께 PARITION BY절 사용

--부서 번호와 사원명, 부서별 급여의 합을 함께 출력.
SELECT DEPARTMENT_ID, FIRST_NAME,
SUM(SALARY) OVER (PARTITION BY DEPARTMENT_ID) AS SALARY_TOTAL
FROM EMPLOYEES
WHERE DEPARTMENT_ID BETWEEN 10 AND 30
ORDER BY DEPARTMENT_ID;

--그룹함수([칼럼]) OVER(PARTITION BY 칼럼1, 칼럼2,...[ORDER BY 절]..)

SELECT DEPARTMENT_ID, FIRST_NAME, SALARY,
        SUM(SALARY) OVER(PARTITION BY DEPARTMENT_ID) AS DEPARTMENT_TOTAL,
        SUM(SALARY) OVER() AS SALARY_TOTAL --10~30 SALARY들의 합
FROM EMPLOYEES
WHERE DEPARTMENT_ID BETWEEN 10 AND 30
ORDER BY DEPARTMENT_ID;
--각 부서에 소속된 사원 한명만을 출력, 이때 사원번호, 사원명, 직무번호, 급여, 부서번호
SELECT EMPLOYEE_ID, FIRST_NAME, DEPARTMENT_ID, HIRE_DATE
FROM(SELECT ROW_NUMBER() OVER(PARTITION BY DEPARTMENT_ID ORDER BY EMPLOYEE_ID) AS RNUM,
            EMPLOYEE_ID, FIRST_NAME, DEPARTMENT_ID, HIRE_DATE
            FROM EMPLOYEES) DATA
WHERE DATA.RNUM = 1;

시퀀스

레코드를 식별하기 위해 사용

PRIMARY KEY(중복 허용하지 않음)로 지정한 칼럼에 일련번호를 자동으로 부여받기 위해 시퀀스의 생성

 

(1) 시퀀스 생성: 유일(UNIQUE)한 값을 생성해주는 오라클 객체(=>CREATE), 기본키와 같이 순차적으로 증가하는 칼럼을 자동적으로 생성, 보통 테이블 당 하나의 시퀀스

중간이 비어도 채울필요X, 자동으로 MAX값의 다음 값

CREATE SEQUENCE SEQUENCE_NAME
	[START WITH N]
    [INCREMENT BY N]
    [{MAXVALUE N|NOMAXVALUE}]	--최대값//안잡으면 잡을 수 있는 값만큼
    [{MINVALUE N|NOMINVALUE}]	--최소값
    [{CYCLE|NOCYCLE}]			--최대 값까지 간 후에 다시 최소값부터 사용할 것인지
    [{CACHE N|NOCACHE}]

 

① START WITH: 시퀀스의 시작 값을 지정. N을 1로 지정하면 1부터 순차적으로 시퀀스 번호가 증가

② INCREMENT BY: 시퀀스의 증가 값. N을 2로 하면 2씩 증가. START WITH를 1로 하고 INCREMENT BY를 2로 하면 1,3,5,7로 시퀀스 번호가 증가

③MAXVALUE N|NOMAXVALUE: MAXVALUE는 시퀀스가 증가할 수 있는 최댓값을 말한다. NOMAXVALUE는 시퀀스의 값을 무한대로 지정

④MINVALUE N | NOMINVALUE: 시퀀스의 최솟값. 기본값 1, NOMINVALUE를 지정할 경우 최솟값은 무한 소

⑤CYCLE(사이클)|NOCYCLE: 시퀀스 값이 최댓값까지 증가가 완료되면 다시 최솟값에서부터 시퀀스를 시작하도록 하려면 CYCLE, NOCYCLE은 최댓값을 넘어서면 오류(디폴트 값: NOCYCLE)

⑥CACHE(캐쉬) N|NOCACHE: 오라클 서버가 미리 지정하고 메모리에 유지할 값의 수로 디폴트 값은 20, 값 들어갈때 같이 올라간다.

 

--테이블 생성
CREATE TABLE TEST(
    NO NUMBER NOT NULL PRIMARY KEY
);
--테이블 TEST에 사용할 시퀀스 TEST_SEQ 생성
CREATE SEQUENCE TEST_SEQ
START WITH 1
INCREMENT BY 1
MINVALUE 1
MAXVALUE 100000
NOCYCLE
CACHE 2;	--최소값인 1보다 커야한다.
--DROP SEQUENCE TEST_SEQ;	--SEQUENCE는 DROP 다음 다시 CREATE로 수정

INSERT INTO TEST VALUES(TEST_SEQ.NEXTVAL);
--시퀀스를 사용하지 않았을 경우 자동증가하는 값으로 설정하기 위한 쿼리문
INSERT INTO TEST VALUES((SELECT MAX(NO)+1 FROM TEST));

SELECT * FROM TEST;

--시퀀스의 현재값 반환
SELECT TEST_SEQ.CURRVAL FROM DUAL;

--시퀀스의 현재값에서 증가한 값(+1) 반환
SELECT TEST_SEQ.NEXTVAL FROM DUAL;

 

'DataBase+SQL' 카테고리의 다른 글

SQL 반복문, 커서  (0) 2023.06.27
PL/SQL  (0) 2023.06.26
뷰(VIEW)  (0) 2023.06.22
서브 쿼리  (0) 2023.06.21
그룹 함수  (0) 2023.06.20

1)뷰의 개념

  • 물리적인 테이블을 근거한 논리적인 가상 테이블(실제로 데이터를 가지고 있는 테이블X)                                               =>데이터 저장 공간이 없다. 단지 쿼리문을 저장하고 있는 객체
  • 기본 테이블에서 파생된 객체로 기본 테이블에 대한 하나의 쿼리문
  • 실제 테이블에 저장된 데이터를 뷰를 통해서 볼 수 있도록 한다.

 

--ORA-01031: 권한이 불충분합니다
GRANT CREATE VIEW TO hr; --PDB_SYS로 접속해서 권한 부여, GRANT=DCL

--Grant을(를) 성공했습니다.
--CREATE를 주면 생성뿐만 아니라 조회하고, 삭제할 수 있는 권한도 부여
--각 각 주고 싶으면 SELECT 이용
--작업 끝나면 PDB_SYS 접속 해제(최고 권한자 접속 해제)
--PDB(데이터베이스)_SYS(사용자이름)

 

 

뷰의 사용 목적: 직접적인 테이블 접근을 제한(보안성), 복잡한 질의를 쉽게 만듦(편의성)

뷰의 특징: 테이블에 대한 제한을 가지고 테이블의 일정한 부분만 보일 수 있는 가상의 테이블, 실제 자료를 갖지는 않지만 테이블을 관리 가능, 하나의 테이블에 뷰의 개수는 제한이 없다.

 

2) 뷰 생성과 조회

테이블 생성과 같이 CREATE문 사용

기본 테이블: 뷰에 의해 제한적으로 겁근해서 사용하는 실질적으로 데이터를 저장하고 있는 물리적인 테이블.

 

3) 뷰 생성

테이블을 생성할 때와 유사하게 CREATE VIEW 명령어로 생성

CREATE [OR REPLACE] [FORCE|NOFORCE] VIEW VIEW_NAME
[(ALIAS,ALIAS,ALIAS,...)]
AS SUBQUERY
[WITH CHECK OPTION]
[WITH READ ONLY];		--해당 뷰를 통해서는 SELECT만 가능하며
				--INSERT/UPDATE/DELETE 불가능
                
 CREATE VIEW 뷰이름
 AS
 SELECT ~
 
 --뷰 확인
 SELECT 컬럼 FROM 뷰이름;

 

OR REPLACE VIEW: 새로운 뷰를 만들 수 있을 뿐만 아니라 기존에 뷰가 존재하더라도 삭제하지 않고 새로운 구조의 뷰로.

 

WITH CHECK OPTION: 해당 뷰를 통해서 볼 수 있는 범위 내에서만 UPDATE OR INSERT가 가능

 

1] 뷰에 관련된 데이터 딕셔너리

데이터 딕셔너리 USER_VIEWS에 사용자가 생성한 모든 뷰에 대한 정의가 저장

VIEW_NAME: 뷰의 이름, TEXT:칼럼과 뷰를 작성할 때 기술한 서브 쿼리문

SELECT VIEW_NAME, TEXT
FROM USER_VIEWS;

 

2] 뷰의 동작 원리

  1. 사용자가 뷰에 대해서 질의를 하면 USER_VIEWS에서 뷰에 대한 정의를 조회
  2. 기본 테이블에 대한 뷰의 접근 권한을 살핌.
  3. 뷰에 대한 질의를 기본 테이블에 대한 질의로 변환
  4. 기본 테이블에 대한 질의를 통해 데이터를 검색
  5. 검색된 결과 출력

4) 뷰의 종류(뷰를 정의하기 위해서 사용되는 기본 테이블의 수에 따라 단순 뷰와 복합 뷰로 나뉜다)

단순 뷰 복합 뷰
하나의 테이블로 생성 여러 개의 테이블로 생성
그룹 함수의 사용이 불가능 그룹 함수의 사용이 가능
DISTINCT 사용이 불가능 DISTINCT 사용이 가능
DML(INSERT/UPDATE/DELETE) 사용 가능 DML(INSERT/UPDATE/DELETE) 사용 불가능

 

1]단순 뷰에 대한 데이터 조작 :  INSERT, UPDATE, DELETE 사용 가능

--단순뷰를 기준으로 INSERT/UPDATE/DELETE 수행이 가능
--이때 뷰를 기준으로 입력하면 실제 테이블(기본 테이블)에 데이터가 저장
--뷰에 INSERT하면 기본 테이블에도 삽입이 된다.
--기본테이블에 INSERT가 가능한 조건이여야 뷰에 INSERT가능
INSERT INTO VIEW_EMP01
VALUES(250, 'ANGEL', 7000, 30);

 

2] 단순 뷰의 칼럼에 별칭 부여

--사원번호, 사원명, 급여, 부서번호로 구성된 뷰를 작성하되 기본 테이블은 EMP01로 하고 칼럼명은 한글화
CREATE OR REPLACE VIEW VIEW_EMP02 --기존에 뷰가 존재하면 구조 변경
AS
SELECT EMPLOYEE_ID 사원번호, FIRST_NAME 사원명, SALARY 급여, DEPARTMENT_ID 부서번호 --별칭이 컬럼명이됨
FROM EMP01;

-- 별칭을 부여한 뷰는 실제 컬럼명으로 조회할 수 없다.
--ORA-00904: "DEPARTMENT_ID": 부적합한 식별자
--부서번호로 조회해야 한다.
SELECT * 
FROM VIEW_EMP02
WHERE DEPARTMENT_ID =10;

SELECT * 
FROM VIEW_EMP02
WHERE 부서번호 =10;

 

--부서별 급여의 합, 급여의 평균을 조회할 수 있는 VIEW_SALARY 뷰 생성
CREATE OR REPLACE VIEW VIEW_SALARY
AS
SELECT DEPARTMENT_ID, SUM(SALARY) AS "SALARYSUM", TRUNC(AVG(SALARY)) AS "SALARYAVG" --함수가 선언되어 있는 컬럼에 대해서는 별칭이 무조건 필요!
FROM EMP01
GROUP BY DEPARTMENT_ID
ORDER BY DEPARTMENT_ID;

3] 단 순 뷰에 DML 명령어로 조작 불가능한 경우

  • 뷰 정의에 포함되지 않은 칼럼 중에 기본 테이블의 칼럼이 NOT NULL 제약 조건이 지정되어 있는 경우 INSERT 사용 불가능
  • SALARY*12와 같이 산술 표현식으로 정의된 가상 칼럼이 뷰에 정의되면 INSERT나 UPDATE 불가능
  • DISTINCT을 포함한 경우에도 DML명령 사용 불가능
  • 그룹함수나 GROUP BY 절을 포함한 경우에도 DML(INSERT, UPDATE, DELETE,SELECT) 명령 사용X

4] 복합 뷰: 2개 이상의 기본 테이블에 의해 정의된 뷰

--사원번호, 사원이름,급여, 부서번호, 부서명 조회할 수 있는 VIEW_EMP_DEPT 생성
CREATE VIEW VIEW_EMP_DEPT
AS
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY, E.DEPARTMENT_ID, DEPARTMENT_NAME
FROM EMPLOYEES E INNER JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID
ORDER BY DEPARTMENT_ID DESC;

SELECT * FROM VIEW_EMP_DEPT;

--ON대신 USING으로 변경
--ORA-25154: USING 절의 열 부분은 식별자를 가질 수 없음
CREATE OR REPLACE VIEW VIEW_EMP_DEPT
AS
SELECT E.EMPLOYEE_ID, E.FIRST_NAME, E.SALARY, DEPARTMENT_ID, D.DEPARTMENT_NAME
FROM EMPLOYEES E INNER JOIN DEPARTMENTS D
USING (DEPARTMENT_ID)
ORDER BY DEPARTMENT_ID DESC;

 

6) 뷰 수정을 위한 OR REPLACE 옵션

CREATE VIEW 대신 CREATE OR REPLACE VIEW를 사용하면, 존재하지 않은 뷰이면 새로운 뷰를 생성하고 기존에 존재하는 뷰이면 내용을 변경

 

7) 기본 테이블 없이 뷰를 생성하기 위한 FORCE 옵션: 기본 테이블이 존재하지 않더라도 뷰를 생성하려면 FORCE옵션 추가

--경고: 컴파일 오류와 함께 뷰가 생성되었습니다.
CREATE OR REPLACE FORCE VIEW VIEW_NOTABLE
AS
SELECT EMPLOYEE_ID, FIRST_NAME, DEPARTMENT_ID
FROM EMP15		--존재하지 않는 테이블
WHERE EMPLOYEE_ID = 10;

 

8)WITH CHECK OPTION: 뷰 생성시 조건으로 지정한 칼럼값을 변경하지 못하도록 하는 것

CREATE OR REPLACE VIEW VIEW_CHK
AS
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY,DEPARTMENT_ID
FROM EMP01
WHERE DEPARTMENT_ID =20 WITH CHECK OPTION;
-------------------------------------------------
--SQL 오류: ORA-01402: 뷰의 WITH CHECK OPTION의 조건에 위배 됩니다
UPDATE VIEW_CHK
SET DEPARTMENT_ID =10   --급여가 5000니상인 사원을 10번 부서로 이동하는 쿼리문
WHERE SALARY>=5000;     --부서 번호에 옵션을 지정하였으므로 이 뷰를 통해서는 부서번호 변경 불가능

 

9)WITH READ ONLY: 데이터 자체를 읽기 전용으로 VIEW를 통해 변경 불가능

CREATE OR REPLACE VIEW VIEW_READ
AS
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY,DEPARTMENT_ID
FROM EMP01
WHERE DEPARTMENT_ID = 30 WITH READ ONLY;

--SQL 오류: ORA-42399: 읽기 전용 뷰에서는 DML 작업을 수행할 수 없습니다.
UPDATE VIEW_READ
SET SALARY =1000;

-WITH CHECK OPTION:조건에 사용한 컬럼의 값을 수정하지 못하게 함

-WITH READ ONLY: 기본 테이블의 모두를 수정 못하게 함.

 

10)뷰 활용하기

ROWNUM칼럼 : 오라클에서 내부적으로 부여, INSERT문에 의해 입력한 순서에 따라 1 씩 증가되며 값이 지정,레코드가 들어올때 카운팅(내부 컬럼: 행의 번호)

SELECT ROWNUM, EMPLOYEE_ID,FIRST_NAME, HIRE_DATE
FROM EMPLOYEES
ORDER BY HIRE_DATE DESC;
--입사일을 기준으로 내림차순으로 정렬을 하였는데도 해당 행의 ROWNUM은 바뀌지 않는다.
--데이터가 입력된 시점에서 결정되면 다시는 값이 바뀌지 않기 때문이다
--새로운 테이블에 입사일을 기준으로 내림차순으로 정렬한 쿼리문의 결과를 저장하면 
--최근에 입사한 순으로 ROWNUM 칼럼 값이 1부터 부여
CREATE OR REPLACE VIEW VIEW_HIRE
AS
SELECT EMPLOYEE_ID,FIRST_NAME, HIRE_DATE
FROM EMPLOYEES
ORDER BY HIRE_DATE DESC;

SELECT ROWNUM, EMPLOYEE_ID, FIRST_NAME, HIRE_DATE
FROM VIEW_HIRE;

--기본 ROWNUM을 첫번째부터 구할 때 사용 , N번째 부터 N번째까지 구하라는 것일 때 사용X
SELECT ROWNUM, EMPLOYEE_ID, FIRST_NAME, HIRE_DATE
FROM VIEW_HIRE
WHERE ROWNUM<=5;

 


 

SELECT 컬럼
FROM (SELECT~):인라인 뷰 SELECT의 SELECT

SELECT 컬럼, (SELECT 컬럼 FROM 테이블
			 WHERE 외부테이블.컬럼 = 내부테이블.컬럼)
FROM 테이블: 스칼라

 

1]인라인 뷰로 TOP-N 구하기

--인라인 뷰로 TOP-N
SELECT ROWNUM, EMPLOYEE_ID, FIRST_NAME, HIRE_DATE
FROM (SELECT EMPLOYEE_ID, FIRST_NAME, HIRE_DATE --함수X
        FROM EMPLOYEES
        ORDER BY HIRE_DATE DESC)
WHERE ROWNUM <=5;

--서브 쿼리에 이름을 붙여주고 인라인 뷰로 사용 시 서브쿼리의 이름으로 FROM 절에 기술 가능
--같은 서브쿼리가 여러번 사용될 경우 중복 작성을 피할 수 있고 실행속도도 빨라진다는 장점이 있음
WITH TOPN_HIRE
AS
(SELECT EMPLOYEE_ID, FIRST_NAME, HIRE_DATE
FROM EMPLOYEES
ORDER BY HIRE_DATE DESC)

SELECT ROWNUM, EMPLOYEE_ID,FIRST_NAME, HIRE_DATE
FROM TOPN_HIRE;

--EMPLOYEES 테이블과 DEPARTMENTS 테이블을 조회하여 부서 번호와 부서별 최대 급여 및 부서명 출력
SELECT E.DEPARTMENT_ID, D.DEPARTMENT_NAME, E.SALARY
FROM ( SELECT DEPARTMENT_ID, MAX(SALARY) SALARY
        FROM EMPLOYEES 
        GROUP BY DEPARTMENT_ID
        ) E INNER JOIN DEPARTMENTS D
ON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
--EMPLOYEES 테이블에서 급여가 자신이 속한 부서의 평균 급여보다 많이 받는
--사원의 부서번호, 이름, 급여를 출력하는 SELECT
SELECT E.DEPARTMENT_ID, E.FIRST_NAME, E.SALARY
FROM EMPLOYEES E INNER JOIN (SELECT DEPARTMENT_ID, AVG(SALARY) D_SALARY
                            FROM EMPLOYEES
                            GROUP BY DEPARTMENT_ID) D
ON E.DEPARTMENT_ID = D.DEPARTMENT_ID
WHERE E.SALARY > D.D_SALARY
ORDER BY E.DEPARTMENT_ID;

2] 순위 관련 함수들

  • RANK()  : 중복 순위 개수만큼 다음 순위 값을 증가 시킴
  • 형식 : RANK() OVER(ORDER BY 컬럼명 (ASC|DESC)) (AS 별칭)
  • DENSE_RANK() : 중복 순위가 존재해도 순차적으로 다음 순위 값을 표시함 
  • ROW_NUMBER() : 중복값에 관계없이 SEQUENCE(순차적인 순위 값) 값을 반환
--RANK() OVER
SELECT SALARY_RANK, FIRST_NAME, SALARY
FROM (SELECT FIRST_NAME, SALARY,
            RANK() OVER(ORDER BY SALARY DESC) AS SALARY_RANK
        FROM EMPLOYEES
        ORDER BY SALARY DESC)
WHERE ROWNUM <= 5;

--사원테이블에서 80번 부서에 소속된 사원 중에서 입사년도가 가장 최근에 입사한 순부터 
--사원번호, 사원명, 입사일자, 순위를 부여하여 출력
SELECT EMPLOYEE_ID AS 사원번호, FIRST_NAME AS 사원명, HIRE_DATE AS 입사일자,
        RANK() OVER(ORDER BY HIRE_DATE DESC) AS 순위
FROM EMPLOYEES
WHERE DEPARTMENT_ID =80;

--DENSE_RANK() OVER
SELECT 순위, FIRST_NAME, SALARY
FROM (SELECT FIRST_NAME, SALARY, DENSE_RANK() OVER(ORDER BY SALARY DESC) AS 순위
FROM EMPLOYEES
ORDER BY SALARY DESC)
WHERE ROWNUM <=5;

--사원 테이블에서 80번 부서에 소속된 사원 중에서 급여를 가장 많이 받는 순으로
--사원번호, 사원명,급여, 순위 출력
--급여가 가장 높은 순
SELECT EMPLOYEE_ID AS 사원번호, FIRST_NAME AS 사원명, SALARY AS 급여,
        DENSE_RANK() OVER(ORDER BY SALARY DESC) AS 순위 --DENSE_는 중복되었을 때 다음 순서를 그대로 준다.
FROM EMPLOYEES
WHERE DEPARTMENT_ID =80;

'DataBase+SQL' 카테고리의 다른 글

PL/SQL  (0) 2023.06.26
MVIEW, 시퀀스  (0) 2023.06.23
서브 쿼리  (0) 2023.06.21
그룹 함수  (0) 2023.06.20
JOIN(조인)  (0) 2023.06.19

SQL 가로 목록으로 표현

SELECT 
    COUNT(DECODE(DEPARTMENT_ID, 10, 1)) "10번 부서 인원 수", --부서번호 기준 DECODE는 범위값X, 일치하는 값 대상, 값이 같으면 1을 준다고 표현했음
    COUNT(DECODE(DEPARTMENT_ID, 20, 1)) "20번 부서 인원 수", --1명씩 COUNT, 2줄 써도 같은 의미,1의 개수 COUNTING
    COUNT(DECODE(DEPARTMENT_ID, 30, 1)) "30번 부서 인원 수",
    COUNT(DECODE(DEPARTMENT_ID, 40, 1)) "40번 부서 인원 수",
    COUNT(DECODE(DEPARTMENT_ID, 50, 1)) "50번 부서 인원 수",
    COUNT(DECODE(DEPARTMENT_ID, 50, 1)) "50번 부서 인원 수"
FROM EMPLOYEES;


1) 서브 쿼리

하나의 SELECT 문장의 절 안에 포함된 또 하나의 SELECT 문 => 메인쿼리: 서브 쿼리를 포함하고 있는 쿼리문, 서브쿼리: 포함된 또 하나의 쿼리

SELECT 컬럼
FROM 테이블
WHERE 칼럼 연산자 (SELECT~--서브쿼리): 괄호 묶기

실행 순서(우선 순위)는 서브쿼리가 먼저 한 번만 실행.

  • SUB QUERY 부분은 WHERE 절에 연산자 오른쪽에 위치해야 하며 반드시 괄호로 묶어야 한다.
  • 서브 커리에서 SELECT 하지 않은 컬럼은 주 쿼리에서 사용할 수 없다.
  • 특별한 경우(인라인 뷰 등)를 제외하고는 SUB QUERY 절에 ORDER BY 절이 올 수 없다.
  • 서브 쿼리 안에 서브 쿼리가 들어갈 수 있다. 메모리가 허용하는 한 무제한으로 중첩할 수 있다.
  • 단일 행 SUB QUERY와 다중 행 SUB QUERY에 따라 연산자를 잘 선택해야 한다.
--ORA-00907: 누락된 우괄호
--괄호 안에 ORDER BY
SELECT DEPARTMENT_NAME
FROM DEPARTMENTS
WHERE DEPARTMENT_ID=(SELECT DEPARTMENT_ID
FROM EMPLOYEES
WHERE FIRST_NAME='Susan'
ORDER BY DEPARTMENT_ID);
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY, NVL(COMMISSION_PCT,0), TO_CHAR(HIRE_DATE,'YYYY.MM.DD') HIRE_DATE
FROM EMPLOYEES
WHERE DEPARTMENT_ID=(
    SELECT DEPARTMENT_ID
    FROM EMPLOYEES
    WHERE FIRST_NAME='Guy'
)
AND FIRST_NAME <> 'Guy'; --WHERE절 부정 연산자 <>:같지않음

2)단일 행 서브 쿼리

내부 SELECT문장으로부터 오직 하나의 로우(행, ROW)만을 반환 받으며, 단일 행 비교 연산자를 사용

 

1] 서브 쿼리에서 그룹 함수의 사용: 서브 쿼리를 사용하여 평균 급여보다 더 많은 급여를 받는 사원을 검색

SELECT 그룹함수(컬럼) FROM 테이블; --일반 컬럼과 같이 쓰여지지 않았을 경우

SELECT 컬럼 그룹함수(컬럼)
FROM 테이블
GROUP BY 컬럼

--서브 쿼리를 사용하여 평균 급여보다 더 많은 급여를 받는 사원을 검색
SELECT FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE SALARY>(SELECT AVG(SALARY)
              FROM EMPLOYEES);
              
--문제1)EMPLOYEES 테이블에서 LAST_NAME 컬럼에서 Kochhar의 급여보다 많은 사원의 정보를
--사원번호, 이름, 담당업무, 급여를 출력해 주세요
SELECT EMPLOYEE_ID, FIRST_NAME, JOB_ID, SALARY
FROM EMPLOYEES
WHERE SALARY >(SELECT SALARY
               FROM EMPLOYEES
               WHERE LAST_NAME='Kochhar');

--문제2) 가장 적은 급여를 받는 사원의 사번, 이름, 급여를 출력
SELECT EMPLOYEE_ID,FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE SALARY = (SELECT MIN(SALARY) FROM EMPLOYEES);

--추가문제 가장 많은 급여를 받는 사원 이름과 사원의 핸드폰 번호를 출력
SELECT FIRST_NAME, PHONE_NUMBER
FROM EMPLOYEES
WHERE SALARY = (SELECT MAX(SALARY) FROM EMPLOYEES);

--문제3)가장 오랜 기간 근무한 사원의 이름과 이메일, 담당업무, 입사일을 출력
SELECT FIRST_NAME, EMAIL, JOB_ID, HIRE_DATE
FROM EMPLOYEES
WHERE HIRE_DATE = (SELECT MIN(HIRE_DATE) FROM EMPLOYEES);

 

3)다중 행 서브 쿼리 => 비교 연산자 사용 불가능

서브 쿼리에서 반환되는 결과가 하나 이상의 행일 때 사용하는 서브 쿼리

반드시아래와 같은 다중 행 연산자와 함께 사용

종류 의미
IN 메인 쿼리의 비교 조건('=' 연산자로 비교할 경우)이 서브 쿼리의 결과 중에서 하나라도 일치하면 참
ANY 메인 쿼리의 비교 조건이 서브
ALL 메인 쿼리의 비교 조건이 서브 쿼리의 검색 결과와 모든 값이 일치하면 참
EXIST 메인 쿼리의 비교 조건이 서브 쿼리의 결과 중에서 만족하는 값이 하나라도 존재하면 참

 

1] IN 연산자

결과가 2개 이상 구해지는 쿼리문을 서브 쿼리로 기술할 경우에 다중 행 연산자와 함께 사용해갸 한다.

IN 연산자는 메인 쿼리의 비교 조건에서 서브 쿼리의 출력 결과와 하나라도 일치하면 메인 쿼리의 WHERE 절의 참이 되도록하는 연산자

WHERE 컬럼 IN(값,값,값,..)
WHERE 컬럼 = 값 OR 컬럼 = 값 OR...

 

2] ALL연산

ALL 조건은 메인 쿼리의 비교 조건이 서브 쿼리의 검색 결과와 모든 값이 일치하면 참. 찾아진 값에 대해서 AND 연산을 해서 모두 참이면 참, > ALL은 모든 비교값보다 크냐고 묻는 것이 되므로 최대값보다 더 크면 참이 된다. 

따라서 < 로 비교하면 서브쿼리에 MIN 함수를 적용한 효과, > 로 비교하면 서브쿼리에 MAX 함수를 적용한 효과

--30번 소속 직원들 중에서 급여를 가장 많이 받은 사원보다 더 많은 급여를
--받는 사람의 이름, 급여를 출력 (30번 부서 직원 급여들 모두에 대해서 커야 하므로 최대값보다 큰 급여만)
SELECT FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE SALARY > ALL (SELECT SALARY
                    FROM EMPLOYEES
                    WHERE DEPARTMENT_ID =30); --전부 만족

 

3] ANY 연산자(연산자 > ANY 는 찾아진 값에 대해서 하나라도 크면 참)

ANY조건은 메인 쿼리의 비교 조건이 서브쿼리의 검색 결과와 하나 이상만 일치하면 참

> ANY는 찾아진 값에 대해서 하나라도 크면 참이 되므로 찾아진 값 중에서 가장 작은 값 즉, 최소값 보다 크면 참이 된다.

< 연산자로 비교하면 서브쿼리에 MAX 함수를 적용한 효과가 나고, > 연산자로 비교하면 서브 쿼리에 MIN 함수를 적용한 효과가 난다.

 

SELECT FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE SALARY > ANY (SELECT SALARY
                   FROM EMPLOYEES
                   WHERE DEPARTMENT_ID =110);

 

 상호 연관 서브 쿼리: 상호 연관 Sub Query란 Main Query값을 Sub Query에 주고 Sub Query를 수행한 후
그 결과를 다시 Main Query로 반환해서 수행하는 Sub Query를 말한다. 
즉, Main Query와 Sub Query가 서로 데이터를 주고 받는 형태이다.

 

EXISTS 연산자
EXISTS 연산자는 서브 쿼리문에서 주로 사용하며 서브 쿼리의 결과 값이 참이 나오기만 하면
바로 메인 쿼리의 결과 값을 리턴한다.

 

SELECT EMPLOYEE_ID, FIRST_NAME, E.JOB_ID, JOB_TITLE
FROM EMPLOYEES E INNER JOIN JOBS J ON E.JOB_ID = J.JOB_ID
WHERE EXISTS (SELECT *
              FROM JOB_HISTORY --사원에 잇는 정보를 참조해서 가져온다: 상호 연관 서브 쿼리-> 괄호 안에 것만 실행 불가능
              WHERE E.EMPLOYEE_ID = EMPLOYEE_ID)
ORDER BY E.EMPLOYEE_ID;

 

4) 서브 쿼리로 테이블 작성하기

SUB QUERY는 오는 위치에 따라서 그 이름이 다름

--서브쿼리
SELECT 컬럼 -- 메인쿼리
FROM 테이블
WHERE 컬럼 연산자()(SUBQUERY);

--인라인 뷰
SELECT 컬럼
FROM (SELECT~(SUBQUERY));

--스칼라 서브쿼리
SELECT 컬럼명, (SELECT 컬럼(SUBQUERY) FROM 테이블
				WHERE 내부 테이블. 컬럼 = 외부 테이블.컬럼)
FROM 테이블;

--모든 사원의 사원번호, 이름, 관리자 번호, 관리자명을 조회
SELECT EMPLOYEE_ID, FIRST_NAME, MANAGER_ID, NVL((SELECT M.FIRST_NAME FROM EMPLOYEES M
                                             WHERE M.EMPLOYEE_ID = E.MANAGER_ID), '없음') 관리자명
FROM EMPLOYEES E
ORDER BY 1;

 

[문제] EMPLOYEES 테이블에서/ 급여가/ 자신이 속한 부서/의 평균 급여/보다 /많이 받는/ 사원의 부서번호, 이름, 급여를 출력하는 SELECT문을 작성하시오.

--[문제] EMPLOYEES 테이블에서/ 급여가/ 자신이 속한 부서/의 평균 급여/보다 /많이 받는/ 사원의 부서번호, 이름, 급여를 출력하는 SELECT문을 작성하시오.
SELECT E.DEPARTMENT_ID, FIRST_NAME, E.SALARY
FROM EMPLOYEES E
WHERE E.SALARY > (SELECT AVG(SALARY)
                    FROM EMPLOYEES D
                    WHERE D.DEPARTMENT_ID = E.DEPARTMENT_ID)
ORDER BY E.DEPARTMNET_ID, E.SALARY DESC;

 

테이블의 구조만 복사

CREATE TABLE EMP03
AS
SELECT *
FROM EMPLOYEES
WHERE 1=0;

 

5)서브 쿼리를 이용한 데이터 추가

서브 쿼리를 사용하여 INSERT 문장을 작성하며 VALUES 절은 사용하지 않는다.

VALUES 절에 기술하는 자료를 서브 쿼리에서 얻어온다. 단, 서브 쿼리의 값 개수와 INSERT할 테이블의 열 수가 일치해야한다.

 

6)서브 쿼리를 이용한 데이터 수정

 

7)서브 쿼리를 이용한 두 개 이상의 칼럼에 대한 값 변경

UPDATE TABLE_NAME
SET COLUMN_NAME1 = (SUB_QUERY1), COLUMN_NAME2 = (SUB_QUERY2),...
WHERE 조건;

UPDATE TABLE_NAME
SET(COLUMN_NAME1, COLUMN_NAME2) = (SUB_QUERY)
WHERE 조건;

--20번 부서의 부서명과 지역명을 30번 부서의 부서명과 지역명으로 수정.
UPDATE DEPT01
SET DEPARTMENT_NAME = (SELECT DEPARTMENT_NAME
                       FROM DEPT01
                       WHERE DEPARTMENT_ID =30),
    LOCATION_ID = ( SELECT LOCATION_ID
                    FROM DEPT01
                    WHERE DEPARTMENT_ID =30)
WHERE DEPARTMENT_ID =20;

UPDATE DEPT01
SET(DEPARTMENT_NAME, LOCATION_ID) = (SELECT DEPARTMENT_NAME, LOCATION_ID
                                    FROM DEPT01
                                    WHERE DEPARTMENT_ID = 30)
WHERE DEPARTMENT_ID =20;

 

8) 서브 쿼리를 이용한 데이터 삭제

 


외래키, 참조키를 찾아 삭제, 수정하기 위한 쿼리문

--HR 사용자로 생성한 DEPT01 테이블과 참조키(외래키) 설정 테이블 확인
SELECT FK.OWNER, FK.CONSTRAINT_NAME, FK.TABLE_NAME
FROM ALL_CONSTRAINTS FK, ALL_CONSTRAINTS PK
WHERE FK.R_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
AND PK.OWNER = 'HR'
AND FK.CONSTRAINT_TYPE='R'
AND PK.TABLE_NAME = 'DEPT01'
ORDER BY FK.TABLE_NAME;

'DataBase+SQL' 카테고리의 다른 글

MVIEW, 시퀀스  (0) 2023.06.23
뷰(VIEW)  (0) 2023.06.22
그룹 함수  (0) 2023.06.20
JOIN(조인)  (0) 2023.06.19
SELECT문 함수  (2) 2023.06.16

+ Recent posts