JDBC

Java database connectivity

 

java 프로그램 안에서 SQL을 실행하기 위해서 데이터베이스를 연결해주는 응용프로그램 인터페이스

(자바 API / 자바가 제공하는 표준 인터페이스)

 

JDBC를 사용하면 다른 관계형 데이터베이스에서도 SQL을 사용하기 쉽다. 데이터베이스 응용업무

(Sybase, Oracle, Informix, mSQL 등 따로 만들 필요가 없다.)

 


1번 방법


1. jdbc 드라이버 설치
  - jdbc 드라이버 파일 오라클설치 폴더에 있음. 
    c드라이브 oraclexe - product - 서버 -jdbc - ojdbc.jar파일(자바용 압축파일) 
    자바에서 해당파일을 실행 가능
    ojdbc5 (1.4 이하버전)
    ojdbc6 (1.6 이상)
    ojdbc6_g (모바일)


- 자바 설치 폴더에 해당파일 복사
    바탕화면에 ojdbc6 복붙
1) D:\B_Util\2.Java\JDK1.8\jre\lib\ext
   에 ojdbc6 붙여넣기

 

 

 


2번 방법 - 가장 많이 사용하는 방법

 

2)- 프로젝트 생성후 library에 등록
 (1) 프로젝트 생성 
  Open perspective- 자바선택
   new 자바 프로젝트 생성 제목 : DBPROC
   Properties - java Build Path - Libraries - ojdbc6 
   NEW CLASS 생성

 


Class 생성

1. DBTest

2. OracleTest

3. StateTest

 

 

▶ DBTest : 오라클 SQL 연결

1) import

import java.sql.Connection;    // 연결객체 생성
import java.sql.DriverManager; // JDBC 드라이버 로딩 및 세트 관리
import java.sql.SQLException;  // 예외처리

 JDBC 에서 사용하는 객체 

 

2) DriverManager 클래스 : 드라이버 로드

 - 데이터 원본에 JDBC 드라이버로 Connection을 받음.

 - Class.forName() 메소드 호출로 생성 및 DriverManager 에 등록

 - DriverManager 클래스는 모두 static으로 반드시 객체를 생성시킬 필요는 없다.

 - Connection 인터페이스를 구현(객체생성)시키기 위해 getConnection() 메서드를 호출한다.

 - 드라이버 클래스를 찾지 못할 경우 ClassNotFoundException 예외를 생성시킨다. 

   => 예외처리

 

3) Connection 인터페이스: DB연결

 - DB 원본에 연결하기 위해 Connection 인터페이스 구현(Connection 객체 생성)

 

 

4) SQLException 클래스 : 예외처리

 - 애플리케이션 실행하는 동안 발생하는 예외 처리의 기본 클래스이다.

 - 오류의 특정정보 반환

 - getMessage()로 예외 전체 텍스트 반환

 - catch 문에 (ClassNotFoundException e) 안쓰고 싶으면 메인메소드 옆에
    throws ClassNotFoundException 쓰기

// DB와 연동하기 위해서는 import 반드시 써줘야함.
import java.sql.Connection;  // 연결객체 생성
import java.sql.DriverManager; 
import java.sql.SQLException;

class DBtest { //dbConnection()는 static 이므로 객체를 생성하지 않아도 쓸수 있다.
	public static Connection dbConnection() {
		// 오라클 계정은 변수선언 사용해서 자주 쓰면 좋다.
		String driver="oracle.jdbc.driver.OracleDriver"; //고정된 값 
		String url="jdbc:oracle:thin:@localhost:1521:xe"; // 로컬 번호 
		String userid="LHY93"; 
		String passwd="java"; 
		
		Connection conn=null;
 
//try-catch
		try {
			//1. try에서 드라이버 파일 로딩
			Class.forName(driver); // = oracle.jdbc.driver.OracleDriver

			//2. 연결객체 생성 : 로컬번호,계정id,계정pw
			conn=DriverManager.getConnection(url,userid,passwd); 

			System.out.println("성공적으로 DB에 연결되었습니다.");	
            
          // 예외 클래스 생성  
		} catch (ClassNotFoundException e) {	
			System.out.println("Db Dirver 로딩 실패"); 
         // DriverManager의 reference Libraries-ojdbc6 파일 못찾을때
			
		} catch(SQLException e) {
			System.out.println("DB 연결 실패");
         // Connection 객체 생성에 실패 conn=DriverManager.getConnection의 예외처리
            
		} catch(Exception e) {
			System.out.println("오류발생");
			e.printStackTrace();
		}
		return conn; // 갖고있는 값 반환
	}
}

 

※ 싱글톤(Singleton) : 객체의 인스턴스가 오직 1개만 생성되는 패턴.

참조변수가 아무리 많이 생성되어도 객체는 하나만 생성되게 한다.

생성자가 반드시 private이여야 한다. 내부에서 한번만 생성시키도록

 

 


OracleTest: Statement 객체생성 및 SQL 질의

1) import

import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;

2-1)  Statement 인터페이스 (비효율적) - 정적쿼리

 - Connection 객체를 통해 CreateStatement() 메서드로  Statement 객체를 생성

 - Statement 객체 생성후 executeQuery()메소드로 SQL문 실행 가능 (String으로 전달)

 - 반드시 try-catch 문 또는 throw를 통해 예외처리 해줘야함.

 - 단순한 질의문을 사용할 경우에 좋음

 

2-2) Statement 객체의 동작
    - 구문분석- 문법검사, 의미검사, 권한검사, 실행계획
    - 치환(bind) - 값을 입력 받아 변수에 할당
    - 실행(execute) - 디스크에서 데이터를 찾아 버퍼에 복사
    - 인출(patch) - 데이터 블록에서 원하는 데이터를 찾아 추출하는 과정

 

2-3) Statement 객체의 제한(문제점) 
    ex) String sql="select * from member where mem_id= " +id + 
                   "and mem_pass = " + passwd;
        만약 사용자가 id에 "1"을 입력하면 sql은 
        "select * from member where  mem_id= 1" and mem_pass = " + passwd;
        만약 사용자가 id에 "1"eotls "1 OR 1=1 --"을 입력하면 sql은
        "select * from member where  mem_id= OR 1=1--" and mem_pass = " + passwd;
        => '--'는 주석이므로 "--" 이후는 모두 무시됨 => Statement 객체는
        SQL Injection에 취약함
    - Statement 객체는 객체마다 1개의 ResultSet 객체만이 동시에 오픈되어 사용

 

2-4)  PreparedStatement 인터페이스 (효율적) - 동적 쿼리

 - Connection객체를 통해 prepareStatement() 메서드로 prepareStatement객체를 생성

 - 한번 분석되면 재사용에 효율적이다.

 - SQL 문장이 미리 컴파일 되고, 실행시간동안 인수 값을 위한 공간을 확보할 수 있다.

 - 캐시사용

 

(사용형식)
  .SQL 작성시 변수에 할당할 부분에 "?"를 사용하여 쿼리 작성
  ex) insert into lprod(lprod_id,lprod_gu,lprod_nm) values(?, ?, ?);
  . 이 "?"가 들어간 자리에 set ~ 메서드를 이용하여 값을 할당
    객체명.setInt(1,변수명1); --'1'은 첫 번째 '?'와 매핑
    객체명.setString(2, 변수명2); --'2'은 두 번째 '?'와 매핑
               :
  . set~메서드의 종류
   setInt(idx,int) -- idx 인덱스를 정수형(int)으로 지정
   setString(idx,String) -- idx 인덱스를 String으로 지정
   setCharacter(idx,Reader,int) -- idx 인덱스의 파라메터 값을 long 타입으로 지정(인덱스,읽어올 스트림,길이)

                                                      오라클의 long VARCHAR 타입 (문자열)

   setLong(idx,long) -- idx 인덱스를 long으로 지정
   setTime(idx,Time) -- idx 인덱스를 java.sql.Time 타입으로 지정

 

 

3) ResultSet 인터페이스 

 - 커서(cursor)를 가지고 있음.

 - SQL문에서 SELECT 문을 사용한 질의의 결과물로 반환

 - 질의에 의해 생성된 테이블.

 - 메소드 사용으로 커서의 위치를 옮길수 있다. next() : 다음행 ~

 - 반복문(while) 사용할 때 next() 에 다음 행이 없다면 false / 있으면 true


4) 질의 수행

ResultSet rs = stmt.executeQuery("수행문");

stmt.execute("qurey"); 
stmt.executeQurey("qurey"); // select 문 수행
stmt.executeUpdate("qurey"); // insert, update, delete 수행

 

5) ResultSet 결과 받기 : while문

while(rs.next()) 더이상 읽어올 값이 없으면 false가 되고 while문을 빠져나온다.

while(rs.next()) { // 한 행씩 값 읽기.
    int lid=rs.getInt(1); // 컬럼1 : index값 오라클은 첫번째 자리가 1번 부터 이다.
    String lgu=rs.getString(2); //컬럼2
    String lnm=rs.getString(3); // 컬럼3
    String res=lid + lgu + lnm;
    System.out.println(res);
    }

 

6)  Close 

 - 가장 최근에 생성된 객체부터 닫아주기

 : ResultSet  -> Statement  / PreparedStatement -> Connection 

- try-catch의 마지막으로 finally : 예외가 발생하던 안하던 반드시 수행한다.

finally {
      try {
            if(rs != null) {rs.close();}
            if(stmt != null) {stmt.close();}
            if(conn != null) {conn.close();}
 }

전체  OracleTest

import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;

public class OracleTest {
   public static void main(String[] args) {
     Connection conn=null; // DBtest클래스에서 반환되는 값
     Statement stmt=null;
     ResultSet rs=null;
		
   try {
     String sql="select * from lprod"; // 수행시킬 문자열 선언
			
     conn=DBtest.dbConnection(); // static Connection  타입의 메서드 호출 (conn)
     stmt=conn.createStatement(); // Statement 객체 생성
     rs=stmt.executeQuery(sql); // executeQuery : select 문을 수행
		
    /*lprod 오라클에서 정의된 타입 불러오기
    *  lprod_id number(7) => int (소숫점이하가 없다 = 정수 int타입)
    *  lprod_gu varchar2(4) => String
    *  lprod_nm varchar2(40) => String
    */ 
    
 // rs에는 lprod_id | lprod_gu | lprod_nm의 값을 불러온다. 한 행 전체를 가져온다.		
     System.out.println("순번		분류코드		분류명");
     System.out.println("==========================================");
			
     while(rs.next()) {
       int lid=rs.getInt(1); // 컬럼1 : index값 오라클은 첫번째 자리가 1번 부터 이다.
       String lgu=rs.getString(2); //컬럼2
       String lnm=rs.getString(3); // 컬럼3
       String res=lid + "	" + lgu +"	" + lnm;
       System.out.println(res);
	}
	} catch(SQLException e) {
          System.out.println("오류발생");
          e.printStackTrace();
    } finally {
      try {//예외가 발생될수 있는 명령이나. 필요한 명령어들// 강요되어진 try-catch = sql문
          if(rs != null) {rs.close(); }
          if(stmt != null) {stmt.close();}
          if(conn != null) {conn.close();}
    }
	 catch(Exception e) {
          throw new RuntimeException(e.getMessage()); // throw 해당위치에서 오류를 강제로 발생시킨다.
        // 생성자 메소드 익명블록. 객체 참조변수가 없다. 이번만 예외객체를 발생시킴..
				}
			}
		}	
	}
// finally는 반드시 안써도 된다. 오류가 발생하던 안하던 반드시 실행되어야 한다.

Statement / PreparedStatement Test

 

1) Statement 인 경우

String sql = 안에 단따옴표 ' ' 꼭 써줘야 함. 

String sql="insert into lprod(lprod_id,lprod_gu,lprod_nm) values('lid ','lgu','lnm')"

 

※ int result 왜 int 타입인지?

명령을 실행하고 반환되는 값이 (정수)

  성공 : 1

  실패 : 1이 아닌 값 (0)

 

public class StateTest {
  public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       Statement stmt=null;
       PreparedStatement pstmt = null;
       ResultSet rs=null;
		
       System.out.print("순번 입력 : ");
       int lid=sc.nextInt();
       System.out.print("분류코드 입력 : ");
       String lgu=sc.next();
       System.out.print("분류명 입력 : ");
       String lnm=sc.next();
			
       Connection conn=DBtest.dbConnection();
   try { 
       String sql="insert into lprod(lprod_id,lprod_gu,lprod_nm) values('" 
                   + lid + "', '" + lgu + "', '" + lnm + "')";
       System.out.println(sql);
			
       stmt=conn.createStatement();
       int result=stmt.executeUpdate(sql);

       if(result==1) { // 1 : 성공
       System.out.println("분류코드가 입력되었습니다...");
     } else {         // 1 이외의 값 : 실패
              System.out.println("분류코드 입력이 실패하였습니다.");
     }
     } catch (Exception e) {e.printStackTrace();}
     finally {
         try {
              if(rs !=null) {rs.close();}
              if(stmt !=null) {stmt.close();}
              if(conn != null) {conn.close();}
			} catch (Exception e) { }
		}
	}
}

 

 

2) PreparedStatement 인 경우

String sql="insert into lprod(lprod_id,lprod_gu,lprod_nm) values(?,?,?)";

객체명.set~메서드로 '?'의 값 할당

- set~메서드의 종류 -
   setInt(idx,int) : idx 인덱스를 정수형(int)으로 지정
   setString(idx,String) : idx 인덱스를 String으로 지정
   setCharacter(idx,Reader,int) : idx 인덱스의 파라메터 값을 long 타입으로 지정(인덱스,읽어올 스트림,길이)

                                                      오라클의 LONG VARCHAR 타입 (문자열)

   setLong(idx,long) : idx 인덱스를 long으로 지정

   setFloat(idx,float) : idx 인덱스를 float으로 지정

   setDouble(idx,double) : idx 인덱스를 double으로 지정
   setTime(idx,Time) : idx 인덱스를 java.sql.Time 타입으로 지정

 

public class StateTest {
  public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       Statement stmt=null;
       PreparedStatement pstmt = null;
       ResultSet rs=null;
		
       System.out.print("순번 입력 : ");
       int lid=sc.nextInt();
       System.out.print("분류코드 입력 : ");
       String lgu=sc.next();
       System.out.print("분류명 입력 : ");
       String lnm=sc.next();
			
       Connection conn=DBtest.dbConnection();
   try { 
        String sql="insert into lprod(lprod_id,lprod_gu,lprod_nm) values(?,?,?)";
        pstmt = conn.prepareStatement(sql); //prepareStatement 객체 생성
			
        pstmt.setInt(1, lid);     // 첫번째 '?' : 순번(정수)
        pstmt.setString(2, lgu);  // 두번째 '?' : 분류코드(String)
        pstmt.setString(3,lnm);   // 세번째 '?' : 분류명(String)
        int result = pstmt.executeUpdate();

       if(result==1) { // 1 : 성공
       System.out.println("분류코드가 입력되었습니다...");
     } else {         // 1 이외의 값 : 실패
              System.out.println("분류코드 입력이 실패하였습니다.");
     }
     } catch (Exception e) {e.printStackTrace();}
     finally {
         try {
              if(rs !=null) {rs.close();}
              if(stmt !=null) {stmt.close();}
              if(conn != null) {conn.close();}
			} catch (Exception e) { }
		}
	}
}

 

-- 인서트로 인해 트리거를 호출,,,
-- 호출된 트리거를 통해 또다시 트리거를 호출 => 오류 발생 => 테이블계정 막혀서 접근 불가

 

트리거 안에서는 COMMIT, ROLLBACK, SAVEPOINT 사용 못한다.

 


  - 어떤 이벤트가 발생되면 자동적으로 다른 테이블의 변경동작을 수행시키는 일종의 프로시져
  - 자동적으로 실행되며 수동으로 실행시킬 수 없음.
  - 트리거 대상 테이블을 상대로 트리거 내용이 존재할 수 없음
  - 트렌잭션의 일부로 처리됨. 따라서 트리거 내부에 COMMIT, ROLLBACK, SAVEPOINT 등이 존재할 수 없음


(사용형식)
   CREATE [OR REPLACE] TRIGGER 트리거 이름
     BEFORE|AFTER      INSERT|DELETE|UPDATE 
--   수행시점 전/후할 것      이벤트 실행 (OR로 연결됨)
     ON 테이블명
     [FOR EACH ROW]
     [WHEN 조건]
     [DECLARE]
       [선언영역]
    BEGIN
       트리거 본문;
    END;   

 

 

 . 트리거 타이밍 : 트리거 본문이 실행되는 시점으로 BEFORE 또는 AFTER 사용
    . 이벤트 : 트리거 본문을 실행시키는 원인이 되는 DML명령으로 OR연산자로 조합 사용 가능
    . 트리거 종류
      - 문장단위 트리거 : 'FOR EACH ROW'가 생략된 트리거로 DML명령의 결과에 포함된 
        행의 수와 관계없이 한번만 실행되는 트리거.
      - 행단위 트리거 : 'FOR EACH ROW'를 기술한 트리거로 DML명령의 결과에 포함된 
        행의 수 만큼 트리거 수행, :OLD, :NEW 의사레코드 사용가능
    . 트리거 조건 : 'WHEN 조건'로 기술되며, 트리거 발생에 좀더 구체적인 조건을 제시 할 때 사용

 

한 트리거가 완료되기 전에 다른트리거를 발생시켜서는안된다.

행단위 트리거 조건이 수행될때만 WHEN 조건 쓸수있음(구체적인 조건일때)

 

:OLD(이미 있다.)   :NEW(새롭다)

-- NEW 트리거를 발생시킨 제품의 정보를 얻는다.

-- OLD : 있는자료에 덮어쓰기. 

UPDATE (NEW , OLD  둘다 있어야함)

 

-- 트리거 본문이 이벤트가 발생되기 전에 먼저 실행되는 경우 : BEFORE
-- 퇴직해서 특정사원을 삭제할 때. 퇴직자 테이블을 별도로 만들어서 정보를 옮김.
-- 삭제하기 전에(BEFORE) 자료를 옮겨야한다.

--대부분은 AFTER
--물건을 구매할때  INSERT|DELETE|UPDATE 다 발생 가능
--본문에는 REMAIN테이블에서 UPDATE
-- FOR EACH ROW : 각각의 행마다 ~ 발생한다.

 

   . 트리거 의사레코드와 트리거 함수
      - 의사레코드

      -------------------------------------------------------
        의사레코드         내용
      -------------------------------------------------------
        :NEW             이벤트가 INSERT, UPDATE일 때 사용되며 새롭게
                         입력(신규 입력이나 갱신을 위한 값)되는 행을 지칭
                         DELETE에 사용되면 모든열이 NULL로 SETTING
        :OLD             이벤트가 DELETE에, UPDATE일 때 사용되며 삭제 또는 갱신의 
                         대상이 되는 행을 지칭
                         INSERT에 사용되면 모든열이 NULL로 SETTING 
                         
      -  트리거 함수 : 이벤트를 OR연산자로 구성한 경우 이밴트의 종류를 구분하기 위한 함수
      ------------------------------------------------------
          함수                    내용
      ------------------------------------------------------
       INSERTING        이벤트가 INSERT 이면 true 반환
       UPDATING         이벤트가 UPDATE 이면 true 반환
       DELETING         이벤트가 DELETE 이면 true 반환

-- 매개변수 존재하지 않음.

--:NEW.컬럼명 => 트리거 본문에서 사용 가능


사용예) LPROD테이블에서 LPROD_ID가 12번인 자료를 삭제(이벤트)하고 삭제후(AFTER) '자료삭제 성공'이라는
       메세지를 출력하는 트리거 작성

 

--     문장단위 트리거가 실행이되면 반드시 SELECT나 COMMIT명령 해야함

   CREATE OR REPLACE TRIGGER tg_delete_lprod
       AFTER DELETE ON LPROD
    BEGIN
       DBMS_OUTPUT.PUT_LINE('자료삭제 성공');
    END;
       
     --  변수 선언 필요없어서 DECLARE 없음


     
    자동실행

 DELETE FROM LPROD WHERE LPROD_ID=12;
    COMMIT;
       
    DELETE FROM LPROD WHERE LPROD_ID>=10;
    SELECT * FROM LPROD;

 

 


 

** HR계정의 EMPLOYEES 테이블에서 사원번호,사원명,입사일,직무코드, 부서코드를 조회하여
   EMP테이블을 생성하시오

 CREATE TABLE EMP AS
      SELECT EMPLOYEE_ID AS EMP_ID,
             EMP_NAME,
             HIRE_DATE,
             JOB_ID,
             DEPARTMENT_ID AS DEPT_ID
        FROM HR.EMPLOYEES;
COMMIT;

 

** HR계정의 EMP 테이블을 복사하여 RETIRE 테이블을 생성하시오 모든 자료는 삭제 

  CREATE TABLE RETIRE AS 
      SELECT * FROM EMP;
    
  DELETE FROM RETIRE; -- 데이터 삭제

 


 

 

사용예) EMP테이블에서 사원번호 121~125번 사원을 삭제하시오. 단 삭제전 해당사원의 자료를
       RETIRE 테이블에 저장하시오. (RETIRE에 저장 후 삭제)- 행단위 트리거

 

 CREATE OR REPLACE TRIGGER tg_delete_emp
      BEFORE  DELETE OR UPDATE ON EMP
      FOR EACH ROW -- 행단위 트리거 실행
   BEGIN
      INSERT INTO RETIRE VALUES(:OLD.EMP_ID,:OLD.EMP_NAME,:OLD.HIRE_DATE
                               ,:OLD.JOB_ID,:OLD.DEPT_ID);
   END;


사용예) 장바구니에 입력,수정,삭제가 발생되었을 때 재고수불테이블의 재고변경을 수행하는
       트리거를 작성하시오.

 

  CREATE OR REPLACE TRIGGER tg_cart_change
       AFTER INSERT OR UPDATE OR DELETE ON CART
       FOR EACH ROW 
     DECLARE
        L_QTY NUMBER:=0;
        L_PID PROD.PROD_ID%TYPE;
        L_DATE DATE;
     BEGIN
       -- 새롭게 업데이트 되는 수량 최종수량 :NEW - :OLD (값이 음수이면)
       -- (재고 - (-수량)) : 재고 + 수량
        IF INSERTING THEN -- INSERT = 팔림(출고)
           L_QTY:=NVL(:NEW.CART_QTY,0); -- CART테이블에 입력시킨것 삽입
           L_PID:=(:NEW.CART_PROD);
           L_DATE:=TO_DATE(SUBSTR(:NEW.CART_NO,1,8));
        ELSIF UPDATING THEN
           L_QTY:=NVL(:NEW.CART_QTY,0) - NVL(:OLD.CART_QTY,0); -- 구매수량 수정 :NEW - :OLD
           L_PID:=(:NEW.CART_PROD);
           L_DATE:=TO_DATE(SUBSTR(:NEW.CART_NO,1,8));
        ELSIF DELETING THEN
           L_QTY:= -NVL(:OLD.CART_QTY,0); -- 환불
           L_PID:=(:OLD.CART_PROD);
           L_DATE:=TO_DATE(SUBSTR(:OLD.CART_NO,1,8));
        END IF;
           
        UPDATE REMAIN A
           SET A.REMAIN_O=A.REMAIN_O + L_QTY,
               A.REMAIN_J_99=REMAIN_J_99 - L_QTY,
               A.REMAIN_DATE=L_DATE -- 수령받고 혹시 환불할까봐
         WHERE A.REMAIN_YEAR='2020'
           AND A.PROD_ID=L_PID;  
         -- 기본키가 2개인경우 WHERE 조건절에 2개 다 써줘야 한다.
       EXCEPTION
         WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE('예외발생 : '||SQLERRM);
     END;

       -- 새롭게 업데이트 되는 수량 최종수량 :NEW - :OLD (값이 음수이면)
       -- (재고 - (-수량)) : 재고 + 수량

 

 

▶ 상품구매

     insert_cart_proc  
     EXECUTE insert_cart_proc('b001',SYSDATE,'P201000001',10);
--      P201000001 현재재고 28개 - > 12개

 

 

 

▶ 수량변경

 구매수량 변경 
     UPDATE CART
        SET CART_QTY=8
      WHERE CART_PROD='P201000001'
        AND CART_NO='2022120200003';
       
      UPDATE CART
        SET CART_QTY=15
      WHERE CART_PROD='P201000001'
        AND CART_NO='2022120200003';

 

 

 

 

▶ 전체반품했을 때

 전체 반품   
    DELETE FROM CART
     WHERE CART_PROD='P201000001'
       AND CART_NO='2022120200003';

 

 

 

 

함수(User Defined Function : Function)
  - 특징은 procedure와 유사하며, 반환값이 존재함
  - 보통 out 매개변수는 사용하지 않음

(사용형식)
    CREATE [OR REPLACEFUNCTION 함수명[(
       매개변수 [IN|OUT|INOUT] 데이터타입[:=디폴트 값],
                     :
       매개변수 [IN|OUT|INOUT] 데이터타입[:=디폴트 값])
       RETURN 데이터타입
    IS|AS
      선언영역
    BEGIN
      실행영역
      (반드시 하나 이상의
       RETURN expr; 이 존재해야 함)-- 값이나, 수식, 컬럼명 // 동일한 데이터타입을 사용해야함.
    END;
    . 하나의 함수는 하나의 값만 반환 가능
    . 함수의 호출은 SELECT문의 SELECT절, WHERE절 등에서 수행

- OUT 매개변수는 잘 사용하지 않는다.

 

 

사용예) 회원번호를 입력받아 2020년 7월 구매현황을 조회하시오

CREATE OR REPLACE FUNCTION fn_sum_member(
    p_mid IN MEMBER.MEM_ID%TYPE) -- 입력 매개변수 p_mid
    RETURN NUMBER -- 반환타입 : 구매금액합계(숫자)
  IS
    L_SUM NUMBER:=0;
    -- 구매금액을 계산해서 보관할 값
  BEGIN
    SELECT SUM(A.CART_QTY*B.PROD_PRICE) INTO L_SUM
      FROM CART A, PROD B
     WHERE A.CART_PROD=B.PROD_ID
       AND A.CART_MEMBER=p_mid
       AND A.CART_NO LIKE '202007%';      
    RETURN L_SUM; -- 반환 : 구매금액을 계산해서 보관할 값
  END;

 

[실행] 2020년 7월에 구매한 사람만 출력

-- 서브쿼리를 안쓰면 구매안한사람도 나온다. => OUTER조인 

 SELECT MEM_ID AS 회원번호,
        MEM_NAME AS 회원명,
        NVL(fn_sum_member(MEM_ID),0) AS 구매금액
        -- fn_sum_member SELECT절의 SELECT사용
   FROM MEMBER
  WHERE MEM_ID IN(SELECT CART_MEMBER 
                    FROM CART
                   WHERE CART_NO LIKE '202007%');

사용예) 장바구니 테이블에 매출정보를 저장하는 프로시져를 작성하시오
       입력자료는 회원번호,회원명,날짜,상품번호,수량이며 장바구니 번호는 함수를 이용하여 작성하시오.

 

장바구니 생성 함수 작성

- 같은회원, 같은날짜

 CREATE OR REPLACE FUNCTION create_cart_no(
      p_date DATE,
      p_mid IN MEMBER.MEM_ID%TYPE)
      RETURN CHAR
   IS
      -- 임시로 보관하는 장바구니 번호
      L_CART_NO CART.CART_NO%TYPE;
      L_MID MEMBER.MEM_ID%TYPE;
      L_COUNT NUMBER:=0; --행의 수 세기.
      L_FLAG CHAR(9):=TO_CHAR(p_date,'YYYYMMDD')||'%'; -- LIKE 함수 쓰기위해 사용
   BEGIN
      -- 해당일자의 장바구니테이블에 자료존재 판단
      SELECT COUNT(*) INTO L_COUNT
        FROM CART
       WHERE CART_NO LIKE L_FLAG; -- 해당날짜
      -- 없으면 L_FLAG => 00000+1...?
      
      IF L_COUNT=0 THEN -- 첫번째 고객이다.
         L_CART_NO:=TO_CHAR(p_date,'YYYYMMDD')||TRIM('00001');
      ELSE
         -- 해당 날짜의 최대 장바구니 번호를 검색
         SELECT MAX(CART_NO) INTO L_CART_NO
           FROM CART
          WHERE CART_NO LIKE L_FLAG;
         -- 최대 장바구니 번호를 보유한 회원번호
         SELECT DISTINCT CART_MEMBER INTO L_MID -- 중복제거
           FROM CART
          WHERE CART_NO=L_CART_NO;
         
          IF L_MID!=p_mid THEN
             L_CART_NO:=L_CART_NO+1;
          END IF;
     END IF;   
     RETURN L_CART_NO;  
   END;

 

▶ CART 테이블에 자료 입력하는 프로시져

create or replace PROCEDURE insert_cart_proc(
        p_mid IN MEMBER.MEM_ID%TYPE,
        p_date IN DATE,
        p_pid IN PROD.PROD_ID%TYPE,
        p_qty IN NUMBER)
    IS -- 함수사용으로 변수사용이 필요없어짐
    BEGIN
                                   -- 장바구니 번호 함수
        INSERT INTO CART VALUES(p_mid,create_cart_no(p_date,p_mid),p_pid,p_qty);
        COMMIT;
    END;

 

▶ CART 번호 임시생성(COMMIT 안함)

SELECT create_cart_no(TO_DATE('20200415'),'j001') -- 2번 
  FROM DUAL; -- 해당날짜의 마지막 구매고객이라 번호가 같다.
      
SELECT create_cart_no(TO_DATE('20200415'),'f001') -- 3번
  FROM DUAL;

SELECT create_cart_no(TO_DATE('20200414'),'t001') -- 1번
  FROM DUAL;

 

 

▶ 오늘날짜의 신규 장바구니번호 생성

 

 

EXECUTE insert_cart_proc('b001',SYSDATE,'P201000010',5);
EXECUTE insert_cart_proc('b001',SYSDATE,'P302000016',10); -- 번호 1번 
같은사람이 같은날 구매해서 번호가 바뀌지 말아야함
    
EXECUTE insert_cart_proc('t001',SYSDATE,'P101000003',2);

 


 

** 오라클 잘못 COMMIT된 자료 복구
[조회]
 

 SELECT  *  FROM 테이블명
     AS OF TIMESTAMP(SYSTIMESTAMP-INTERVAL 'n' MINUTE);
 . 'n' : 되돌리고자 하는 분


     
-- 48시간 까지는 가능
** 30분 전에 삭제한 자료 복구

INSERT INTO 테이블명
   SELECT  *  FROM 테이블명
       AS OF TIMESTAMP(SYSTIMESTAMP-INTERVAL '30' MINUTE);

 

 


사용예) 년도와 상품코드를 입력받아 해당년도에 해당 상품의 판매횟수를 반환하는 함수를 작성하시오.

 

 CREATE OR REPLACE FUNCTION fn_count_prod(
      -- 입력용 년도를 문자열로
      p_year IN CHAR, p_pid IN PROD.PROD_ID%TYPE)
      -- 반환타입
      RETURN NUMBER
   IS
      L_COUNT NUMBER:=0;  -- 판매 횟수 저장
   BEGIN
      SELECT COUNT(*) INTO L_COUNT
        FROM CART
       WHERE SUBSTR(CART_NO,1,4)=p_year
         AND CART_PROD=p_pid;
      RETURN L_COUNT;
   END;

SELECT PROD_ID AS 상품코드,
          PROD_NAME AS 상품명,
          fn_count_prod('2020',PROD_ID) AS 판매횟수
  FROM PROD
 ORDER BY 1;

    -- COUNT는 값이 없으면 0으로 반환된다. => OUTER 조인

 

 

 

 

 

▶ 예외 처리

 CREATE OR REPLACE FUNCTION fn_count_prod(
      -- 입력용 년도를 문자열로
      p_year IN CHAR, p_pid IN PROD.PROD_ID%TYPE)
      -- 반환타입
      RETURN NUMBER
   IS
      L_COUNT NUMBER:=0;  -- 판매 횟수 저장
   BEGIN
      SELECT COUNT(*) INTO L_COUNT
        FROM CART
       WHERE SUBSTR(CART_NO,1,4)=p_year
         AND CART_PROD=p_pid;
      RETURN L_COUNT;
      EXCEPTION -- 예외발생 / 예외종류
        WHEN OTHERS THEN
           DBMS_OUTPUT.PUT_LINE('예외발생 : '||SQLERRM);
           -- 에러메세지를 출력해주는 변수
         -- SQLERRM : 에러 메세지의 약자
         -- 자바 : 프린트 스텍 트레이스
   END;

예외처리를 많이 하면할수록 그 모듈은 견고한 모듈이다.

 

 

Stored Procedure :  Procedure

 

 - 특정 결과를 산출할 수 있는 모듈을 작성하고 컴파일하여 서버의 프로시져 캐쉬에 저장하여 사용
 - 모든 응용프로그램에서 사용할 수 있도록 캡슐화 기능 제공 => 일관성 제공
 - 데이터베이스 내부 구조에 대한 보안성 제공 -- 뷰... 구조를 사용자가 모르고 결과값만 반환
 - 프로시져는 반환 값이 없음

 

※ function 과 procedure

procedure : procedure  이름을 통한 반환.

  -  procedure 값을 내보낼수 있음. ' out '이라는 매개변수 사용

function  : 함수명을 통해서 결과가 반환됨.

 

모듈화 : 특정한 기능의 모음을 보관

응용프로그램에 의해서 내부 값이 변경될 수 없음

 

(사용형식)

   CREATE [OR REPLACE] PROCEDURE 프로시져명[(
     매개변수 [IN|OUT|INOUT] 데이터타입[:=디폴트 값],--데이터타입 크기는 쓰지말자
                   :
     매개변수 [IN|OUT|INOUT] 데이터타입[:=디폴트 값])
   IS|AS
      선언영역
   BEGIN
      실행역역
   END;

    -- 매개변수 : 프로시져를 수행하기 위해 필요함.
    -- 매개변수가 in 입력용 / out 출력용 / 
    -- inout 둘다가능 = 컴파일할때 결정불가 / 실행할때 결정(DBMS 부담으로 사용자제)
    -- [IN|OUT|INOUT] 생략가능하면 in으로 취급함
    
-- 데이터를 정의하지 않고 프로시져를 호출했을 때 자동으로 입력하는 디폴트 => 사용자제

 

수행예) 직무코드,직무명,최대급여,최소급여를 입력 받아 HR계정의 JOBS테이블에 신규 데이터를
       삽입하는 프로시져를 작성하시오.
 

CREATE OR REPLACE PROCEDURE insert_jobs_proc(
      p_job_id IN HR.JOBS.JOB_ID%TYPE,
      p_job_title IN HR.JOBS.JOB_TITLE%TYPE,
      p_misal IN NUMBER,
      p_mxsal IN NUMBER)
   IS -- (변수 필요없음)
   BEGIN
     INSERT INTO HR.JOBS VALUES(p_job_id,p_job_title,p_misal,p_mxsal);
     COMMIT;
   END;


   
[프로시져의 실행]
 1. 독립적 실행 -- 프로시져에 전달할 값만 기술해준다.
-- OUT 매개변수를 사용하지 않은 경우
-- 프로시져는 셀렉트문에 실행될수 없다. / 독립적 실행뿐... 
   EXEC|EXECUTE 프로시져명(매개변수list); 
   
 2. PL/SQL의 블록에서 실행 -- 변수 사용할 경우
  - OUT 매개변수를 사용한 경우 - OUT매개변수에 의해 전달되는 값을 보관한 변수를 사용 
-- 다른 프로시져나 다른함수 사용 할때 EXEC|EXECUTE 사용안함
-- OUT매개변수가 아니여도 필요에 의해 값을 저장할수 있는 변수 사용
    프로시져명(매개변수list)
    
 

EXECUTE insert_jobs_proc('sample1','SAMPLE JOB TITLE',3000,5000);


       
사용예)직무코드를 입력받아 해당 자료를 JOBS테이블에서 삭제하는 프로시져 작성

 CREATE OR REPLACE PROCEDURE delect_jobs_proc(
    p_job_id IN JOBS.JOB_ID%TYPE)
  IS
    L_CNT NUMBER:=0;
  BEGIN--삭제는 데이터가 있어야 삭제가 된다. 해당 자료의 존재유무 확인
    SELECT COUNT(*) INTO L_CNT
      FROM JOBS
     WHERE JOB_ID=p_job_id; -- 찾은 자료를 L_CNT에 넣어줌
     
    IF L_CNT!=0 THEN
       DELETE FROM JOBS WHERE JOB_ID=p_job_id;
    ELSE 
       DBMS_OUTPUT.PUT_LINE('해당자료가 테이블에 없습니다..');
    END IF;
    COMMIT;
  END;
  
  EXEC delect_jobs_proc('sample1');

 

 

사용예)오늘이 2020년 7월 28일이라고 가정하고 매출정보를 장바구니에 저장하는 프로시져를 작성하시오
      입력값은 회원번호,오늘날짜,상품코드, 수량이다.

       
   CREATE OR REPLACE PROCEDURE insert_cart_proc(
     p_mid IN MEMBER.MEM_ID%TYPE,
     p_date IN DATE,
     p_pid IN PROD.PROD_ID%TYPE,
     p_qty IN NUMBER)
  IS
     L_CART_NO CART.CART_NO%TYPE;
     L_FLAG CHAR(9):=TO_CHAR(p_date,'YYYYMMDD')||'%';
     L_CNT NUMBER:=0;
  BEGIN
     SELECT COUNT(*) INTO L_CNT
       FROM CART
      WHERE CART_NO LIKE L_FLAG;
      
     IF L_CNT=0 THEN
        L_CART_NO:=TO_CHAR(p_date,'YYYYMMDD')||TRIM('00001');
     ELSE
        SELECT MAX(CART_NO)+1 INTO L_CART_NO
          FROM CART
         WHERE CART_NO LIKE L_FLAG;
    END IF;
    INSERT INTO CART VALUES(p_mid,L_CART_NO,p_pid,p_qty);
    COMMIT;
  END;
    
    
      EXECUTE insert_cart_proc('a001',TO_DATE('20200728'),'P201000008',5);

 


사용예) 회원번호와 년도를 입력받아 해당회원의 구매금액합계를 반환받는 프로시져를 작성
입력 2개. 출력 1개

 

CREATE OR REPLACE PROCEDURE sum_cart_proc(
       p_mid IN MEMBER.MEM_ID%TYPE,
       p_year IN CHAR,
       p_sum OUT NUMBER) -- OUT매개변수 선언
    IS
      L_FLAG CHAR(5):=P_YEAR||'%'; 
      --LIKE 미리결합시켜서 사용할수도...
    BEGIN
      SELECT SUM(A.CART_QTY*B.PROD_PRICE) INTO p_sum -- OUT 매개변수로 선언한 값이 자동으로 들어옴
        FROM CART A, PROD B
       WHERE A.CART_PROD=B.PROD_ID
         AND A.CART_NO LIKE L_FLAG
         AND A.CART_MEMBER=p_mid; -- 특정회원으로 제한
    END;

-- Procedure SUM_CART_PROC이(가) 컴파일되었습니다.
-- 실행 : 실행문은 블럭 내부에 있어야 함.

 

 

[실행]

 DECLARE
       L_SUM NUMBER:=0; -- 구매금액합계를 보관할 변수
       L_NAME MEMBER.MEM_NAME%TYPE;
    BEGIN
       SELECT MEM_NAME INTO L_NAME
         FROM MEMBER
        WHERE MEM_ID='c001';
      sum_cart_proc('c001','2020',L_SUM); -- 회원번호,년도, L_SUM <= p_sum
      DBMS_OUTPUT.PUT_LINE('c001, '||L_NAME||' => '||L_SUM);
    END;
    
    ---------------------------------------------------------------
    c001, 신용환 => 19212000

-- 독립실행이 아닌경우 EXECUTE는 안쓴다. // 독립실행이 아닐 땐 프로시져 이름만 쓴다.
-- OUT은 프로시져 안에서 계산된 것을 프로시져 밖으로 꺼내기 위해 사용

 


사용예) 거주지를 입력받아 CUSTOMERS 테이블에서 해당 거주지를 가진 회원정보를 삭제하고
       삭제한 회원의 수를 반환하는 프로시져를 작성하시오

 

-- 프로시져는 반환값이 없다. 반환을 하려면 매개변수가 필요하다.

   

 CREATE OR REPLACE PROCEDURE delete_cust_proc(
     p_add  char, p_cnt  OUT NUMBER)
     IS   
     BEGIN
       SELECT COUNT(*) INTO p_cnt
         FROM CUSTOMERS
        WHERE MEM_ADD1 LIKE p_add||'%';  --입력받은 거주지의 숫자를 COUNT한걸 p_cnt에 넣는다.
      
       DELETE FROM CUSTOMERS
        WHERE MEM_ADD1 LIKE p_add||'%';  --입력받은 거주지와 맞는 사람 삭제
     END;


   
[실행] -- OUT매개변수를 받으려면 변수가 필요하다./EXECUTE 안씀 

ACCEPT P_ADDRESS PROMPT '삭제할 거주지명 입력 : '
   DECLARE
      L_CNT NUMBER:=0;  -- L_CNT는 p_cnt에서 전달받은 것
   BEGIN
      delete_cust_proc('&P_ADDRESS',L_CNT);
  
      DBMS_OUTPUT.PUT_LINE('&P_ADDRESS'||'에 거주한 회원 '||L_CNT||'명의 정보를 삭제함');
  END;

-------------------------------------------------------------
서울에 거주한 회원 1명의 정보를 삭제함




- 오라클의 반복문은 LOOP / WHILE / FOR 문이 제공됨
 - 주로 커서를 위한 명령으로 사용됨


 1. LOOP
  - 반복문의 기본 구조 제공
  - 무한루프

(사용형식)
  LOOP
    반복처리 명령문(들);
    [EXIT WHEN 조건;]
    [반복처리 명령문(들);]
  END LOOP;
   . 'EXIT WHEN 조건' : 조건을 평가하여 결과가 참(true)이면 반복을 벗어남

 

사용예) 구구단의 5단을 출력하는 블록생성

 DECLARE
      L_CNT NUMBER:=0; -- 초기값 0
 BEGIN
   LOOP
      L_CNT:=L_CNT+1;   -- 증가시키는 명령
   EXIT WHEN L_CNT>=10; -- 10보다 크면 안된다. 혹시 10을 벗어나는 값이 올까봐 '>'
      DBMS_OUTPUT.PUT_LINE('5 * '||L_CNT||' = '||5*L_CNT);
   END LOOP;
   END;

 

 


사용예) 회원의 주소지가 '충남'인 회원들의 2020년 상반기 구매액으로 조회하는 익명블록을 작성하시오
       출력사항은 회원번호,회원명,주소,구매금액
      


(PL/SQL을 사용하지 않은 경우)

 

    SELECT M.MEM_ID AS 회원번호,
           M.MEM_NAME AS 회원명,
           M.MEM_ADD1||' '||M.MEM_ADD2 AS 주소,
           A.CSUM AS 구매금액
      FROM MEMBER M,
           (SELECT C.CART_MEMBER AS CID,
                   SUM(C.CART_QTY*P.PROD_PRICE) AS CSUM
              FROM CART C, PROD P
             WHERE C.CART_PROD=PROD_ID
               AND SUBSTR(C.CART_NO,1,6) BETWEEN '202001' AND '202006'
             GROUP BY C.CART_MEMBER)A
     WHERE M.MEM_ID=A.CID
       AND M.MEM_ADD1 LIKE '충남%';

-- 구매금액이 없는사람은 누락시켜서 없음 (내부조인) 

 

 

(PL/SQL)

 DECLARE
        L_MID MEMBER.MEM_ID%TYPE;
        L_MNAME VARCHAR2(100);
        L_ADDR VARCHAR2(500);
        L_SUM NUMBER:=0;
        CURSOR CUR_MEM_ADD IS
          SELECT MEM_ID,MEM_NAME,MEM_ADD1||' '||MEM_ADD2
            FROM MEMBER
           WHERE MEM_ADD1 LIKE '충남%';
  BEGIN
       OPEN CUR_MEM_ADD;
       LOOP
        FETCH CUR_MEM_ADD INTO L_MID,L_MNAME,L_ADDR;
        EXIT WHEN CUR_MEM_ADD%NOTFOUND;
         SELECT SUM(A.CART_QTY*B.PROD_PRICE) INTO L_SUM
           FROM CART A, PROD B
          WHERE A.CART_PROD=B.PROD_ID
            AND SUBSTR(A.CART_NO,1,6) BETWEEN '202001' AND '202006'
            AND A.CART_MEMBER=L_MID;
             
            DBMS_OUTPUT.PUT_LINE('회원번호 : '||L_MID);
            DBMS_OUTPUT.PUT_LINE('회 원 명 : '||L_MNAME);
            DBMS_OUTPUT.PUT_LINE('주   소 : '||L_ADDR);
            DBMS_OUTPUT.PUT_LINE('구매금액 : '||L_SUM);
            DBMS_OUTPUT.PUT_LINE('-----------------');
        END LOOP;
        CLOSE CUR_MEM_ADD;
    END;

-- OUTER JOIN 구매금액이 없어도 결과가 출력이 됨...  

 


2. WHILE문
  - JAVA의 WHILE문과 유사 기능


(사용형식)
  WHILE 조건 LOOP
    반복수행할 명령문(들);
  END LOOP;


   . '조건'이 true 이면 반복을 수행하고, false이면 반복처리를 하지 않음
   . 반복횟수가 중요하지 않거나, 반복횟수를 알수 없는 경우
   . 반복종료의 조건을 알고 있는 경우

 

사용예) 구구단 5단을 출력하시오. (WHILE)

 DECLARE
    L_CNT NUMBER:=0;
 BEGIN
    WHILE L_CNT < 9 LOOP
      L_CNT:=L_CNT+1;
      DBMS_OUTPUT.PUT_LINE('5 * '||L_CNT||' = '||5*L_CNT);
    END LOOP;
  END;

 

사용예) 회원의 주소지가 '충남'인 회원들의 2020년 상반기 구매액으로 조회하는 익명블록을 작성하시오
       출력사항은 회원번호,회원명,주소,구매금액(WHILE문 사용)

 DECLARE
        L_MID MEMBER.MEM_ID%TYPE; --회원번호
        L_MNAME VARCHAR2(100);  --회원명
        L_ADDR VARCHAR2(500);   --주소
        L_SUM NUMBER:=0;        -- 구매금액 합계
        CURSOR CUR_MEM_ADD IS
          SELECT MEM_ID,MEM_NAME,MEM_ADD1||' '||MEM_ADD2
            FROM MEMBER
           WHERE MEM_ADD1 LIKE '충남%';
    BEGIN
        OPEN CUR_MEM_ADD;
        FETCH CUR_MEM_ADD INTO L_MID,L_MNAME,L_ADDR; -- 패치 먼저 수행(첫번째사람)
        WHILE CUR_MEM_ADD%FOUND LOOP          
          SELECT SUM(A.CART_QTY*B.PROD_PRICE) INTO L_SUM
            FROM CART A, PROD B
           WHERE A.CART_PROD=B.PROD_ID
             AND SUBSTR(A.CART_NO,1,6) BETWEEN '202001' AND '202006'
             AND A.CART_MEMBER=L_MID;
             
             DBMS_OUTPUT.PUT_LINE('회원번호 : '||L_MID);
             DBMS_OUTPUT.PUT_LINE('회 원 명 : '||L_MNAME);
             DBMS_OUTPUT.PUT_LINE('주   소 : '||L_ADDR);
             DBMS_OUTPUT.PUT_LINE('구매금액 : '||L_SUM);
             DBMS_OUTPUT.PUT_LINE('-----------------');
        FETCH CUR_MEM_ADD INTO L_MID,L_MNAME,L_ADDR;     -- 두번째 사람 읽기
        END LOOP;
        CLOSE CUR_MEM_ADD;
    END;

 


3. FOR 
  - JAVA의 FOR와 유사한 기능 제공
  - 반복횟수를 알고 있거나 반복횟수가 중요한 경우 사용


(일반 FOR문 사용형식)
   FOR 인덱스 IN [REVERSE초기값.. 최종값 LOOP
     반복처리명령문(들);
   END LOOP;


   . '인덱스' : 시스템에서 자동으로 확보하는 제어변수로 초기값에서 최종값까지
     1씩 변하는 값을 보관
   . 'REVERSE' : 역순으로(최종값-> 초기값) 인덱스값을 운용

 

사용예) 구구단의 5단을 출력

 DECLARE
    BEGIN
      FOR I IN 1..9 LOOP 
      -- I라는 변수는 오라클 시스템에서 알아서 타입명 맞춤 '최초값..최종값'
       DBMS_OUTPUT.PUT_LINE('5* '||I||' = '||5*I);
      END LOOP;
      FOR I IN REVERSE 1..9 LOOP  -- 역순
       DBMS_OUTPUT.PUT_LINE('5* '||I||' = '||5*I);
      END LOOP;
    END;

 

 

 

 

(커서용 FOR문 사용형식)
    FOR 레코드 IN 커서명 | (커서용SELECT문) LOOP
      반복처리명령문(들);
    END LOOP;


    . '커서명|커서용SELECT문' : 선언부에서 선언된 커서나 또는 커서를 구경하는 SELECT문을
      in_line 서브쿼리로 사용할 수 있음
    . '레코드(REC)'는 시스템에서 확보해 주며, 커서 내의 각행을 지칭하는 포인트 역활을 함
    . 커서의 각 열의 참조는 '레코드.열이름' 형태를 사용하여 수행
    . 커서를 FOR문을 이용하면 OPEN, FETCH, CLOSE를 생략한다.

 

사용예) 회원의 주소지가 '충남'인 회원들의 2020년 상반기 구매액으로 조회하는 익명블록을 작성하시오
       출력사항은 회원번호,회원명,주소,구매금액(FOR 사용)

DECLARE    
    L_SUM NUMBER:=0;        -- 구매금액 합계
    CURSOR CUR_MEM_ADD IS
    SELECT MEM_ID,MEM_NAME,MEM_ADD1,MEM_ADD2
      FROM MEMBER
     WHERE MEM_ADD1 LIKE '충남%';
  BEGIN
    FOR REC IN CUR_MEM_ADD LOOP   -- REC : 포인터(참조형 변수)/지칭하는 위치
        SELECT SUM(A.CART_QTY*B.PROD_PRICE) INTO L_SUM
          FROM CART A, PROD B
         WHERE A.CART_PROD=B.PROD_ID
           AND SUBSTR(A.CART_NO,1,6) BETWEEN '202001' AND '202006'
           AND A.CART_MEMBER=REC.MEM_ID;
             
         DBMS_OUTPUT.PUT_LINE('회원번호 : '||REC.MEM_ID);
         DBMS_OUTPUT.PUT_LINE('회 원 명 : '||REC.MEM_NAME);
         DBMS_OUTPUT.PUT_LINE('주   소 : '||REC.MEM_ADD1||' '||REC.MEM_ADD2);
         DBMS_OUTPUT.PUT_LINE('구매금액 : '||L_SUM);
         DBMS_OUTPUT.PUT_LINE('-----------------');
    END LOOP;
--     CLOSE CUR_MEM_ADD; -- 자동으로 FOR문을 벗어남. 생략가능
    END;

-- 커서와 가장 궁합이 잘맞는 반복문은 FOR문이다.

 

변수를 이용해서 하나씩 넣어주는 기능이 필요 없음.변수 대신에 커서에 저장되어있는 컬럼명 그대로 사용

 

구매금액은 커서에 의해서 결정되는게 아니고, 계산에 의해서 필요한 기억공간이기 때문에.

 

-- 커서 이름을 쓰는 대신에 인라인 서브쿼리 기술
 DECLARE    
        L_SUM NUMBER:=0;    
    BEGIN
        FOR REC IN (SELECT MEM_ID,MEM_NAME,MEM_ADD1,MEM_ADD2 -- 인라인서브쿼리 : 스스로
                      FROM MEMBER
                     WHERE MEM_ADD1 LIKE '충남%') LOOP  
            SELECT SUM(A.CART_QTY*B.PROD_PRICE) INTO L_SUM
              FROM CART A, PROD B
             WHERE A.CART_PROD=B.PROD_ID
               AND SUBSTR(A.CART_NO,1,6) BETWEEN '202001' AND '202006'
               AND A.CART_MEMBER=REC.MEM_ID;
             
             DBMS_OUTPUT.PUT_LINE('회원번호 : '||REC.MEM_ID);
             DBMS_OUTPUT.PUT_LINE('회 원 명 : '||REC.MEM_NAME);
             DBMS_OUTPUT.PUT_LINE('주   소 : '||REC.MEM_ADD1||' '||REC.MEM_ADD2);
             DBMS_OUTPUT.PUT_LINE('구매금액 : '||L_SUM);
             DBMS_OUTPUT.PUT_LINE('-----------------');
        END LOOP;
    END;

 

 

PL/SQL에서 제공되는 분기문에는 IF,CASE WHEN ~ THEN이 존재함

 

1) IF문
  - 개발언어의 IF 문과 동일 기능 제공
  
(사용형식-1)
    IF 조건문 THEN
       명령문1;
    [ELSE
       명령문2;]
    END IF;

(사용형식-2) - 병렬 IF
    IF 조건문1 THEN
       명령문1;
    ELSIF  조건문2 THEN
       명령문2;
         :
    ELSE
       명령문N;
    END IF;
    
-- 조건이 거짓일 때 다음 명령을 판단하게 한다.


(사용형식-3) Nested IF
    IF 조건문1 THEN
       IF 조건문2 THEN
          명령문1;
     ELSE  
          명령문2;
    END IF;
    ELSIF 조건문3 THEN
       명령문3;
         :
    ELSE
       명령문N;
    END IF;
    


사용예) 년도를 입력받아 윤년과 평년을 구별하는 블록을 작성하시오
              윤년 : ((4의배수)이면서 (100의 배수가 아니거나)) (400의 배수)가 되는 해

 

    ACCEPT P_YEAR PROMPT '년도입력(YYYY)'
    DECLARE
        L_YEAR NUMBER:=TO_NUMBER('&P_YEAR');
        L_RES VARCHAR2(500);
    BEGIN
        IF (MOD(L_YEAR,4)=0 AND MOD(L_YEAR,100)!=0) OR (MOD(L_YEAR,400)=0) THEN
            L_RES:=L_YEAR||'년은 윤년입니다.';
        ELSE 
            L_RES:=L_YEAR||'년은 평년입니다.';
    END IF;
    DBMS_OUTPUT.PUT_LINE(L_RES);
    END;

 

사용예) 첫 날에 100원, 둘 째날 부터 전날의 2배씩 저축할 때 최초로 100만원을 넘는 날과 저축액수를 구하시오.

DECLARE
   L_SUM NUMBER:=0; -- 총 저축금액
   L_DAMT NUMBER:=100; -- 매일 저축할 액수
   L_DAYS NUMBER:=1; --날수
BEGIN
   LOOP
        L_SUM:=L_SUM+L_DAMT;
     IF L_SUM>1000000 THEN
       EXIT;
     ELSE
        L_DAMT:=L_DAMT*2;
        L_DAYS:=L_DAYS+1;
     END IF;
        END LOOP;       
        DBMS_OUTPUT.PUT_LINE('날수 : '||L_DAYS);
        DBMS_OUTPUT.PUT_LINE('저축액수 : '||L_SUM);  
END;
-------------------------------------------------------------
날수 : 14
저축액수 : 1638300

 


2) CASE WHEN ~ THEN
 - 다중분기문
 - JAVA의 SWITCH CASE문과 유사기능 제공

 

(사용형식-1)
    CASE WHEN 조건 THEN
              명령1;
         WHEN 조건2 THEN
              명령2;
                :
        [ELSE   
              명령N]
    END CASE;

(사용형식-2) -- 하나의 조건이 여려개의 표현으로 정의될 때
    CASE 조건
         WHEN 값1 THEN
              명령1;
         WHEN 값 THEN
              명령2;
                :
        [ELSE   
              명령N]
    END CASE;


              

              

▶ 커서를 쓰는 이유 :

일반적으로 쓰는 SELECT문은... FROM, INTO절이 사용이 됨...

변수는 한순간에 하나의 값만 저장할수 있는데 / SELECT문은 한 건에 여러개 자료를 출력하게 됨.

그 자료를 전달받아서 저장할 변수는 하나만 저장할 수 있음..

SELECT문의 결과를 마치 배열처럼 자동 저장시킴. 저장시킨 것을 한 줄 씩 출력시킴...

 

SELECT문 + INTO / FROM + 변수

SELECT의 결과의 집합을 어느곳에 저장해 놓고 첫번째 행부터 차례대로 가져오는 것.

검색하고 수정하고 삭제할 수 있도록 해주는 것.


커서(CUSOR)

  - 넓은 의미의 커서는 SQL명령의 영향을 받은 행들의 집합이고, 좁은 의미로는 SELECT문의
    결과 집합을 의미 (SELECT의 결과 = 협의의 커서)
  - 묵시적 커서(IMPLICITE CURSOR)와 명시적 커서 (EXPLICITE CURSOR)
  - 커서는 Query의 결과에 대하여 수정,삭제,검색 할 수 있도록 해주는 도구
  - 개발자가 쿼리의 결과를 PL/SQL 블록에서 수동으로 제하할수 있도록 해줌


  - 커서의 사용 단계 (4단계에 걸쳐서 사용됨)
   . 생성(선언영역-변수, 상수 선언) => OPEN (실행영역)

           => FETCH (실행영역 반복문 내부) => CLOSE (실행영역)

 

 

OPEN / CLOSE 명령은 한번 실행되어야 한다.

FETCH  : 커서(결과 집합)에서 읽어오다(가져오다).

                첫번째 행 읽고 / 두번 째 행 읽고 ~ 마지막 행까지

 

  1) 묵시적 커서 (사용자 접근이 불가능)
    . 이름이 없는 커서로 일반적인 SELECT문의 결과 집합이 대표적인 묵시적 커서
    . 묵시적 커서는 결과가 출력되는 순간 OPEN이 되고 출력이 완료되면 자동으로 CLOSE됨
      => 커서 내부에 접근이 불가능

    . 커서 속성

    ------------------------------------------------------------------
       속성               내용            
    ------------------------------------------------------------------
    SQL%ISOPEN         커서가 OPEN 상태이면 true 반환 (묵시적 커서능 항상 false)
    SQL%NOTFOUND       커서 내부에 FETCH할 자료가 있으면 false,없으면 true
    SQL%FOUND          커서 내부에 FETCH할 자료가 없으면 false,있으면 true
                       커서 내부에 FETCH할 자료가 있으면 true,없으면 false
    SQL%ROWCOUNT       커서 내부의 행의 갯수(결과의 수)

 

  2) 명시적 커서
   . 선언영역에서 이름을 부여하여 선언한 커서
   
  3) 선언형식
    CURSOR 커서명 [(변수명 타입명,...)] IS 
      SELECT 문;
    . '(변수명 타입명,...)' : 커서에서 사용될 자료를 전달받는 매개변수로 타입명 기술시 크기를 지정하지 않음
    . 매개변수에 값은 OPEN문에서 설정 함

 

사용예) 부서번호를 키보드로 입력 받아 해당 부서에 근무하는 직원들의 
       사원번호,사원명,입사일,직무코드를 출력하시오.

--  ACCEPT (변수명=바인딩 변수) PROMPT 키보드 입력 받으면 변수명으로 전달됨     
    ACCEPT P_DCODE PROMPT '부서번호 입력(10~110) : '
    DECLARE
        L_EID HR.EMPLOYEES.EMPLOYEE_ID%TYPE; 
        L_ENAME VARCHAR2(80);
        L_HDATE DATE;
        L_JOB_ID HR.JOBS.JOB_ID%TYPE;
        --     커서명         변수명 타입명
        CURSOR CUR_EMP_NAME(DID HR.DEPARTMENTS.DEPARTMENT_ID%TYPE) IS
-- (데이터 타입 모를때 : DID HR.DEPARTMENTS.DEPARTMENT_ID%TYPE / 알 때 : NUMBER)
          SELECT EMPLOYEE_ID,EMP_NAME,HIRE_DATE,JOB_ID
            FROM HR.EMPLOYEES
           WHERE DEPARTMENT_ID=DID;
    BEGIN
    
    END;

-- %TYPE : 데이터의 크기와 타입명은 모르는데 테이블이름만 알 때
-- 변수값을 꺼내올때 '&변수명'
-- 커서 변수를 선언할 때 (변수명 타입명) 타입에는 타임명만 쓰고 크기는 지정하지 않는다.

 

※ ACCEPT에서 받은 변수명 => OPEN문 => 커서

 

 

  4) OPEN 문
   - 커서를 사용하기 위한 명령
(사용형식)
    OPEN 커서명[(값1,...)]
    . '(값1,...)' : 커서 선언문에 사용된 매개변수에 전달된 값은 OPEN문에서 설정함

 

    ACCEPT P_DCODE PROMPT '부서번호 입력(10~110) : '
    DECLARE
        L_EID HR.EMPLOYEES.EMPLOYEE_ID%TYPE; 
        L_ENAME VARCHAR2(80);
        L_HDATE DATE;
        L_JOB_ID HR.JOBS.JOB_ID%TYPE;  
        CURSOR CUR_EMP_NAME(DID HR.DEPARTMENTS.DEPARTMENT_ID%TYPE) IS
          SELECT EMPLOYEE_ID,EMP_NAME,HIRE_DATE,JOB_ID
            FROM HR.EMPLOYEES
           WHERE DEPARTMENT_ID=DID;
    BEGIN
      OPEN CUR_EMP_NAME(TO_NUMBER('&P_DCODE'));
    
    END;

 

 5) FETCH
   . 커서 내의 자료를 읽어 블록에 가져오는 명령
   . 보통 반복문내부에 기술
   
   -- 커서문(이름부여)에 의해서 첫번째부터 반복문이 자동실행되어서 실행됨
(사용형식)
    FETCH 커서명 INTO 변수list;
      - 커서의 SELECT 문의 SELECT절에 기술된 컬럼들을 차례대로 '변수list'에 할당
      - 커서 속성을 이용하여 반복문을 벗어날 수 있음

 

 ACCEPT P_DCODE PROMPT '부서번호 입력(10~110) : '
    DECLARE
        L_EID HR.EMPLOYEES.EMPLOYEE_ID%TYPE; 
        L_ENAME VARCHAR2(80);
        L_HDATE DATE;
        L_JOB_ID HR.JOBS.JOB_ID%TYPE;  
        CURSOR CUR_EMP_NAME(DID HR.DEPARTMENTS.DEPARTMENT_ID%TYPE) IS
          SELECT EMPLOYEE_ID,EMP_NAME,HIRE_DATE,JOB_ID
            FROM HR.EMPLOYEES
           WHERE DEPARTMENT_ID=DID;
    BEGIN
      OPEN CUR_EMP_NAME(TO_NUMBER('&P_DCODE'));
      DBMS_OUTPUT.PUT_LINE('부서번호 : '||'&P_DCODE');
      DBMS_OUTPUT.PUT_LINE('==================');
      LOOP
       FETCH CUR_EMP_NAME INTO  L_EID,L_ENAME,L_HDATE,L_JOB_ID;
       EXIT WHEN CUR_EMP_NAME%NOTFOUND; 
    -- EXIT WHEN: 조건이 붙은 break /NOTFOUND :더이상 데이터가 없으면
       DBMS_OUTPUT.PUT_LINE('사원번호 : '||L_EID);
       DBMS_OUTPUT.PUT_LINE('사 원 명 : '||L_ENAME);
       DBMS_OUTPUT.PUT_LINE('입 사 일 : '||L_HDATE);
       DBMS_OUTPUT.PUT_LINE('직무코드 : '||L_JOB_ID);
       DBMS_OUTPUT.PUT_LINE('-------------------------');
      END LOOP; -- 다시 실행
       DBMS_OUTPUT.PUT_LINE('&P_DCODE'||'번 부서 직원 수 : '||CUR_EMP_NAME%ROWCOUNT||'명');
       -- ROWCOUNT : 가지고있는 전체 행의 수 : 사원의 수
       DBMS_OUTPUT.PUT_LINE(' ');
    END;

-- FETCH를 해야 데이터가 남아있는지 확인할 수 있다. 
-- 순서 : FETCH -> EXIT WHEN CUR_EMP_NAME%NOTFOUND; 
-- CLOSE는 안썼지만 실행은 된다 => 더이상 OPEN할수 없어서 접근 불가능함

 

  6) CLOSE
   . OPEN된 커서는 CLOSE되어야 다시 OPEN 될 수 있음.

(사용형식)
    CLOSE 커서명;
    
    

 

- 숙제 -

문제] 사용자의 사용량을 입력받아 수도요금을 계산하시오(숙제)
     수도사용량
     1 - 10톤 : 단가 1000원
    11 - 20톤 : 1300원
    21 - 30톤 : 1700원
    그 이상    : 2500원
    
   물이용 분담금: 톤당 300원
   
    하수도 요금 : 톤당 350원

(27톤 사용시 요금)
   - 10톤 : 1000 * 10 = 10000
   - 20톤 : 1300 * 10 = 13000
      7톤 : 1700 * 7  = 11900
--------------------------------
                        34900
   물이용 분담금  
              27 * 300 = 8100
     하수도 요금  
              27 * 350 = 9450
--------------------------------
                        52450원

※ 객체지향 : 상속이 되어야 하고, 다형성(형변환이 되어야함), 데이터의 은닉성이 지원되어야함(캡슐화)

오버로딩 : 하나의 메서드에서 이름이 같은 메서드로 다양한데이터를 취급하기 위해

오버라이딩 : 상속관계의 클래스에서 같은 메서드를 재정의 해서 쓰는 것

 

  - PROCEDURAL LANGUAGE SQL
  - 표준 SQL은 분기문, 변수, 제어문 등이 없어 기능이 제한적 
    이를 해결하기 위한 방법으로 제공된 것이 PL/SQL
  - 미리 컴파일되어 서버에 저장되어 실행속도 향상과 네트워크 트래픽 감소
  - 캡슐화, 모듈화 가능
  - 익명블록(Anonymous Block), 프로시져(Stored Procedure), 함수(User Defined Function)
    트리거(Trigger), 패키지(Package) 등이 제공됨

-- 모듈화 : 하나의 결과를 내기 위해 명령들을 모이게 하는 것 (자바의 메소드)

1. 익명블록(1.선언영역/2.실행영역)
  - 이름이 없는 블록
  - 저장되지 않음
  - pl/sql의 기본 구조 제공
  
-- pl(Procedure Language)/sql 

: 절차적 언어(반복문, 제어문,변수,상수,배열,참조형 값 사용)와 함께 사용하는 SQL 조합
  미리 작성해서 필요할때마다 꺼내 쓴다.

 

 

(사용형식)
    DECLARE
    선언 영역 => 변수,상수,커서,사용자 데이터 타입
    BEGIN
    실행영역 => 비지니스 로직을 처리하기 위한 SQL문
    [EXCEPTION
     예외 처리문 -- 필요하다면 예외처리
    ]
    END;

-- 예외처리는 오류를 치유할 수 없다.
-- 에러(심각)-프로그램 종료

-- 예외(미약한 오류)-정상적 종료로 유도 하는 것

 

사용예) 구구단의 7단을 출력하는 익명 블록을 작성하시오

DECLARE
    L_BASE NUMBER:=7;  -- L_BASE : 변수(스칼라)
    L_RESULT NUMBER:=0;
 BEGIN
    FOR I IN 1..9 LOOP
    L_RESULT:=L_BASE*I;
    DBMS_OUTPUT.PUT_LINE(L_BASE||' * '||I||' = '||L_RESULT);
 END LOOP;
 END;

-- 참조형 변수 : 행참조/컬럼참조
-- 바인드 변수 : 데이터가 전달되어지는 통로 역할

-- 특정한 숫자를 계산할때 초기값이 없으면 정상적인종료하기 어렵다.


 

 1) 변수
   . 고급 프로그램 언어의 변수와 같은 기능 제공
   . SCLAR 타입 : 보통의 변수 타입, 하나의 자료만 보관
   . SCLAR, 참조형(REFERENCE), BIND형 COMPOSITE 변수가 제공
   
-- SCLAR : 한순간에 하나의 값만 보관할 수 있다.

 

   (변수[상수] 선언 형식)
    변수[상수]명 [CONSTANT] 데이터타입 [:=초기값]; -- '=' : 동등연산자 / ':=' : 할당, 대입연산자
    -- 상수인 경우 초기값을 반드시 저장해야함.(생략할 수 없다)
     . 상수 선언은 'CONSTANT'을 사용하고 이때 반드시 초기값을 기술해야 함
     . 데이터 타입은 SQL에서 사용하는 표준 데이터 타입과 PLS_INTEGER,BINARY_INTEGER,BOOLEAN 등이 사용됨.
     . 할당 연산자는 ':='임
     . 변수에 초기값이 할당되지 않으면 모든 변수에느 NULL값으로 초기화 됨
     . '데이터 타입' 대신 참조형이 사용될 수 있음
     . 참조형 변수
      - 행참조와 열참조가 제공
      - 열참조 : 테이블명.컬럼명%TYPE
        EX) L_NAME MEMBER.MEM_NAME%TYPE
        
      - 행참조 : 테이블명%ROWTYPE
        EX) L_MEMROW MEMBER%ROWTYPE;
            회원컬럼명은 L_MEMROW.MEM_NAME으로 참조
    
-- 오라클 BOOLEAN의 값은 3개 이다. = TRUE, FALSE, NULL 

 

 

 

사용예)10~110 사이의 임의의 수를 발생시켜 해당부서에 입사일이 가장 빠른 사원의 (입사일이 가장 빠른=1명 고름)
             사원번호,사원명,부서번호,입사일,급여를 출력하는 익명블록을 작성하시오

 DECLARE -- 변수선언
        L_EID HR.EMPLOYEES.EMPLOYEE_ID%TYPE;
        L_ENAME HR.EMPLOYEES.EMP_NAME%TYPE;
        L_DID HR.DEPARTMENTS.DEPARTMENT_ID%TYPE;
        L_DATE DATE;
        L_SAL NUMBER:=0;    
   BEGIN -- 값 입력
        L_DID:=TRUNC(DBMS_RANDOM.VALUE(10,119),-1); --119 확률을 위해 

        SELECT A.EMPLOYEE_ID,A.EMP_NAME,A.HIRE_DATE,A.SALARY
          INTO L_EID,L_ENAME,L_DATE,L_SAL
          FROM (SELECT EMPLOYEE_ID,EMP_NAME,HIRE_DATE,SALARY
                  FROM HR.EMPLOYEES
                 WHERE DEPARTMENT_ID=L_DID
                 ORDER BY 3)A
         WHERE ROWNUM=1;

         DBMS_OUTPUT.PUT_LINE('사원번호 : '||L_EID);
         DBMS_OUTPUT.PUT_LINE('사원명 : '||L_ENAME);
         DBMS_OUTPUT.PUT_LINE('부서번호 : '||L_DID);
         DBMS_OUTPUT.PUT_LINE('입사일 : '||L_DATE);
         DBMS_OUTPUT.PUT_LINE('급여 : '||L_SAL);  
         DBMS_OUTPUT.PUT_LINE('----------------------------');          
    END;

-- 결과를 출력하는 값을 받는 변수를 INTO절에 꼭 써야함!!

-- WHERE 절 ROWNUM=1(상위 한명만 뽑음) 없으면 오류남 : 다수의 값 과 변수 1개의 수가 맞지 않아서 오류남

 

-- 서브쿼리는 INTO절 안쓴다. = 최종결과가 아니기 때문에 / 메인쿼리는 INTO 쓴다.
-- SELECT 컬럼리스트(컬럼1,2,3) INTO 변수리스트(변수1,2,3) -- 컬럼1을 변수1(스칼라)에 저장 /...
-- 컬럼리스트의 컬럼1은 여러개를 저장할 수 있다. => (값 여러개) = 값1개 저장 하는 변수 => 계산 X => 커서 이용

 

 

4. INDEX
  - 인덱스는 자료의 검색효율을 증대시키기 위한 객체
  - DB의 성능은 데이터 검색에 있고, 이 기능을 지원하는 가장 효율적인 방법이 INDEX를 활용한 방법임
  - 인덱스의 용도(장점)
   . SELECT,INSERT,UPDATE 문의 조건절
   . 데이터의 정렬(ORDER BY), 데이터의 그룹화(GROUP BY)에 사용
   . DBMS의 부담을 줄이고 성능을 개선
  - 인덱스 사용시 단점
   . 비용(인적자원)과 추가공간이 필요
   . 인덱스 유지하는데 비용과 시간이 많이 소요됨(삽입,삭제,변경이 잦다면)


  - 종류
   . Unique index.Non-unique Index
   . Normal / Function-Based / Bitmap

 

(사용형식)
    CREATE [UNIQUE|BITMAPINDEX 인덱스명
      ON 테이블명(컬럼명[,컬럼명,...])[ASC|DESC]

 

 

※ 자바 LIST, SET, ,MAP

LIST 순번이 부여되는 기억공간. INDEX가 부여되어 있다. (데이터 중복 허락)

SET 데이터에 인덱스가 없음. 내용을 봐서 내용이 같은 값을 허락하지 않음

        (Equst와 Hash를 재정의 해서 씀)

SET - treeSet

MAP-treeMap

 

바이너리 서치 트리

트래버스. 검색 운행.

 

-- Unique index : 기본키가 아니다. 중복되지 말아야 한다.(NULL 값 포함) 
--                 NULL값을 가질수 있지만 NULL값을 1개만 가진다.

-- Normal 수식어 없이 쓰는 인덱스. 비트리개념
-- Bitmap : 비트별 매핑되어지는 값을 이용해서 나머지 주소를 저장 -> hasing
-- (물리적 주소값 row id)

-- Function-Based (Normal Index)함수가 적용된 노말 인덱스 이다.

-- 인덱스 : 목차 (목차를 이용하면 빨리 찾을 수 있다.)
-- 인덱스는 검색트리를 이용함..Tree 자식 있는 노드 : 간노드 / 자식 없는 트리 : 단노드 /리프노드

 

(사용형식)
    CREATE [UNIQUE|BITMAP] INDEX 인덱스명 -- [UNIQUE|BITMAP]생략되면 Non-unique 인덱스
      ON 테이블명(컬럼명[,컬럼명,...])[ASC|DESC]

사용예) KOR_LOAN_STATUS테이블에서 201112년 강원도 대출현황을 조회하시오

      SELECT * 
        FROM KOR_LOAN_STATUS
       WHERE PERIOD='201112'
         AND REGION='강원';

      CREATE INDEX IDX_KLST ON KOR_LOAN_STATUS(PERIOD,REGION);

 

      -- PERIOD,REGION를 사용해서 인덱스를 만들겠다.
      -- 인덱스를 만들고 다시 실행하면 더 빨리 검색된다.

     CREATE TABLE TEMP01 AS 
            SELECT *
              FROM KOR_LOAN_STATUS,EMPLOYEES; -- 카타시안 PRODUCT 조인 50932개
      
       SELECT * 
        FROM TEMP01
       WHERE PERIOD='201112'
         AND REGION='강원';
         
     CREATE INDEX IDX_TEMP01 ON TEMP01(PERIOD,REGION); -- TEMP01의 인덱스 생성
     
     CREATE INDEX IDX_KLST ON KOR_LOAN_STATUS(PERIOD,REGION);


      
사용예) 회원테이블에서 회원의 첫 번째 주민번호 앞의 2글자로 인덱스를 구성하시오

   CREATE INDEX IDX_MEM_REGNO0 ON MEMBER (SUBSTR(MEM_REGNO1,1,2));
    
    SELECT MEM_ID,MEM_NAME,MEM_ADD1||' '||MEM_ADD2
      FROM MEMBER
     WHERE SUBSTR(MEM_REGNO1,1,2)='01';

 


-- 인덱스는 자동으로 실행되어진다. => 인덱스 재구성

 

** 인덱스 재구성
    ALTER INDEX 인덱스명 REBUILD
      . 많은 자료의 변동이 발생된 직후 
      . 저장공간이 변경된 경우

 

+ Recent posts