■ SQL Injection

공격자가 악의적인 의도를 갖는 SQL 구문을 삽입하여, 데이터베이스를 비정상적으로 조작하는 코드 인젝션 공격 기법

 

▶ 해커의 해킹 방식

//아래의 SQL문에 항상 참인 값을 넣으면 모든 정보가 노출이된다.
String sql = "SELECT * FROM test_member WHERE id = 'test' AND pwd = '1234'" + "or 1 = 1";

- 위와 같은 방식으로 SQL구문을 삽입하여 DB를 조작하는 공격을 SQL 인젝션이라 한다.

- 해결 방법 : PreparedStatement를 사용한다.

 

■ Statement와 PreparedStatment의 차이점

  Statement PreparedStatement
차이점 캐시 미사용 캐시 사용
절차 쿼리 문장 분석
컴파일
실행
--> 계속해서 위 세단계를 반복
쿼리 문장 분석
컴파일
실행
-> 처음 한번만, 캐시에 담아 재사용
결론 보안 낮음, 성능 낮음 보안 높음, 성능 높음

 

■ PreparedStatement로 JDBC프로그래밍 하는 순서

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class _00_preparedStatement {

	static {
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
		} catch (ClassNotFoundException e) {
			System.out.println("드라이버 정보를 찾을 수 없습니다.");
		}
	}
	
	public static void main(String[] args) throws Exception{
		//1. 연결
		String url = "  /* url 정보 */";
		String user = "계정명";
		String pwd = "비밀번호";
		
		Connection conn = DriverManager.getConnection(url, user, pwd);
		
		//2. preparedStatement
		//1) SQL문에 '?'를 넣어 값을 넣을 parameter를 형성
		// parameter index   1        2
		//                   id = ?   pwd = ?  
		String sql = "SELECT * FROM test_member WHERE id = ? AND pwd = ?";
		
		//2) PreparedStatement안에 SQL문을 넣기
		PreparedStatement pstmt = conn.prepareStatement(sql);
		
		//3) SQL문의 parameter를 세팅한다.
		pstmt.setString(1, "사용자 아이디");
		pstmt.setString(2, "사용자 비밀번호");
		
		//3. 처리
		ResultSet rs = pstmt.executeQuery();
		
		while (rs.next()) {
			String name = rs.getString("name");
			String id = rs.getString("id");
			String password = rs.getString("pwd");
			
			System.out.printf("[%s]님의 아이디는 %s, 비밀번호는 %s\n",name,id,password);
		}
		
		//4. 닫기
		rs.close();
		pstmt.close();
		conn.close();
		
	}//end main

}

 

 

 

- 출처:

  - SQL인젝션 개념 https://dev-coco.tistory.com/158 [슬기로운 개발생활😃]

  - 캐시에 대한 자세한 내용 https://p829911.tistory.com/11

  - 그 외 ) 국비지원 수업 과정

- 참고 : 여기어때의 해킹 사건 https://show-me-the-money.tistory.com/entry/%EC%97%AC%EA%B8%B0-%EC%96%B4%EB%95%8C-%ED%95%B4%ED%82%B9-%EC%82%AC%EA%B1%B4%EC%9D%84-%ED%8C%8C%ED%97%A4%EC%B9%98%EB%8B%A4

■ JDBC란?
- 자바 APP과 DB를 연결하여 프로그래밍을 하기 위한 API (또는 이클립스에선 라이브러리라고도 부른다)
- 물리적으로는 jdbcXX.jar 파일을 의미한다.
- 우리는 jdbc.jar안의 드라이버를 통해 모든 종류의 데이터베이스를 사용할 수 있다. (EX. 오라클, mySql 등)

출처 : https://dyjung.tistory.com/50

■ 데이터베이스를 연결하기 전에 점검할 사항

1. 현재 내가 만들고자 하는 앱은 무엇인가?
- 데스크탑 앱 : 로컬에서 설치, 실행되는 앱 프로그램
- 웹 앱 : 브라우저를 통해 실행되는 앱 프로그램

2. 현재 내가 접속하고자 하는 DB 서버의 위치는 어디인가?
- 로컬 서버인가?
- 외부 클라우드 서버인가?
* DB 서버와의 연결 시, 서버 url과 사용자명, 비밀번호가 필요하기에 미리 알아두는 것이 좋다.

3. IDE를 사용하고 있고 jdbc.jar파일 경로를 입력한 적이 있다면, 라이브러리 경로가 제대로 입력되어 있는가?
- 이클립스에 jdbc.jar 파일을 import하고 경로를 옮기면 이클립스는 옮긴 jar파일 경로를 인식하지 못한다.
- 이런 경우, 프로젝트의 [properties] - [Libraries] 에서 jar파일 위치를 재설정하는 게 필요하다.
- 자주 발생하는 미스사항이므로 미리 고려해두자.

+ 이클립스에서 처음 프로젝트를 생성할 때, JRE 버전 확인하기
- JRE 버전을 높히고 다시 낮추었을 때, jdbc.jar의 위치를 인식하지 못하는 오류가 있었다.
(이유는 구글링을 해도 나오지 않아 명확하진 않지만...)
- 처음 프로젝트를 생성할 때부터 JRE 버전을 유의해서 생성하는 게 필요하다.


■ JDBC 프로그래밍을 위한 환경설정

- 프로젝트 생성 시, jdbc.jar파일 경로를 입력하기

프로젝트 > [Build Path] > [Configure Build Path]


■ JDBC 프로그래밍 절차

[ Statement를 사용한 경우 ]
- Statement는 보안성 낮다
- Secure Coding을 위해 PreparedStatement를 사용하는게 좋다. (다음 포스팅 참조)

1. DB 드라이버 로딩

2. Connection 객체를 통해 서버와 연결

3. Statement 객체를 통해 SQL명령 처리
가. SQL 명령어 세팅 : String sql = "select * from table_test";
나. SQL 명령 -> DB전송
: conn.executeQuery(sql)
- 조회 제외 : executeUpdate() 메소드 사용
- 조회 명령 : executeQuery() 메소드 사용
다. 명령에 따라 구분 분석(Parsing)
라. 실행
- 조회 제외 : create, alter, update... 등 실행
- 조회 경우 : ResultSet 객체를 통해 조회 결과를 담아 처리

4. close() 메소드를 통해 연결 해제
*연결 해제는 연결한 순서의 역순으로 해제한다.


[1] DB 드라이버 로딩

class Unknown{

    static {
	     Class.forName("  /*  DB 드라이버 이름   */  ");
    }

    /*  메인 메소드 * /

}
  - 오라클 : oracle.jdbc.driver.OracleDriver
  - MySQL : com.mysql.jdbc.Driver

 

▼ 위와 같이 드라이버 로딩을 하는 이유는? - 더보기 선택

더보기

- jdbc.jar 파일을 가져오면 라이브러리를 쓸 수 있는 상태가 된다.

- 자바 라이브러리처럼, jdbc 라이브러리에서 제공하는 클래스들이 있는데,

  그 중에 하나가 OracleDriver이다.

- 라이브러리만 가져온다고 클래스 내의 기능을 쓸 수 있는 것은 아니기 때문에, OracleDriver 클래스를 사용하기 위하여 드라이버 로딩을 해주는 것이다.

 


[2] Connection 인스턴스를 통해 서버와 연결

import java.sql.Connection;
import java.sql.DriverManager;

class Unknown{

    static {
	     Class.forName("  /*  DB 드라이버 이름   */  ");
    }

    public static void main(String args[]){
         //서버 url, user, pwd 정보 가져오기
         String url = " /* 서버 연결 정보 */ ";
         String user = "계정명";
         String pwd = "비밀번호";  //좌측처럼 민감번호는 코드에 노출하면X (시큐어 코딩)
         
         //DriverManager 객체를 통해 Connection 인스턴스 형성(싱글톤 방식)
         Connection conn = DriverManager.getConnection(url, user, pwd);
    }

}

* 이클립스 내에서 아래 서버 연결 정보는 [Database Connections] - [Properties] - [Driver Properties]에서 얻을 수 있다.

양식 jdbc:[DBMS]:[데이터베이스식별자]
Oracle jdbc:oracle:드라이버종류:@호스트명:포트명:SID
MySQL jdbc:sqlserver://호스트명:포트명;databaseName=DB


[3] Statement (또는 PreparedStatement *이 부분은 다음 포스트에서) 인스턴스를 사용하여
- Statement는 보안성 낮다. 중요하니 다시 안내

아래의 코드는 조회를 한 경우 이나, Statement의 execureUpdate() 메소드를 사용하면 그 외의 명령도 수행할 수 있다.
import java.sql.Connection;
import java.sql.DriverManager;

class Unknown{

    static {
	     Class.forName("  /*  DB 드라이버 이름   */  ");
    }

    public static void main(String args[]){
         //1. [연결]
         //서버 url, user, pwd 정보 가져오기
         String url = " /* 서버 연결 정보 */ ";
         String user = "계정명";
         String pwd = "비밀번호";  //좌측처럼 민감번호는 코드에 노출하면X (시큐어 코딩)
         
         //DriverManager를 통해 Connection 인스턴스 형성(싱글톤 방식)
         Connection conn = DriverManager.getConnection(url, user, pwd);
         
         //2. [Statement]
        
         //가. SQL문 세팅
         String sql = " /* SQL문 */ ";
         
         //나. SQL명령 -> DB전송
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(sql); //DB상에서 구분분석 후, 받은 결과
         
         //다. 실행
         //ResultSet는 커서 역할을 하며, 파일의 BOF에서 EOF까지 가리킨다.
         //.next()는 커서가 가리키는 곳에 레코드가 있음을 true or false로 반환한다.
         while(rs.next()){
             String name = rs.getString("name"); // 칼럼의 헤딩
             int score = rs.getInt("score"); 
             
             system.out.println(name + ":" + score);
         }
         
    }

}


[4] 사용한 것을 역순으로 닫는다.

import java.sql.Connection;
import java.sql.DriverManager;

class Unknown{

    static {
	     Class.forName("  /*  DB 드라이버 이름   */  ");
    }

    public static void main(String args[]){
         //1. [연결]
         //서버 url, user, pwd 정보 가져오기
         String url = " /* 서버 연결 정보 */ ";
         String user = "계정명";
         String pwd = "비밀번호";  //좌측처럼 민감번호는 코드에 노출하면X (시큐어 코딩)
         
         //DriverManager를 통해 Connection 인스턴스 형성(싱글톤 방식)
         Connection conn = DriverManager.getConnection(url, user, pwd);
         
         //2. [Statement]
        
         //가. SQL문 세팅
         String sql = " /* SQL문 */ ";
         
         //나. SQL명령 -> DB전송
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(sql); //DB상에서 구분분석 후, 받은 결과
         
         //다. 실행
         //ResultSet는 커서 역할을 하며, 파일의 BOF에서 EOF까지 가리킨다.
         //.next()는 커서가 가리키는 곳에 레코드가 있음을 true or false로 반환한다.
         while(rs.next()){
             String name = rs.getString("name"); // 칼럼의 헤딩
             int score = rs.getInt("score"); 
             
             system.out.println(name + ":" + score);
         }
         
         //3. [역순으로 닫는다]
         rs.close();
         stmt.close();
         conn.close();
         
    }

}



[출처]
- JDBC https://dyjung.tistory.com/50
- 데스크탑 앱 또는 웹 앱 https://appmaster.io/ko/blog/deseukeutab-aeb-ddoneun-web-aeb-jangdanjeom

■ 데이터의 무결성

- 데이터가 실제 현실 세계의 자료와 차이가 없는 수준의 정확성을 보이는 것

- 여러 사용자가 동일 내용의 테이블을 중복하여 사용할 경우, 데이터에 이상현상 발생 가능성 높음

-> 제약조건으로 데이터 자체의 무결성 유지

-> 정규화를 사용하여 테이블을 분해

-> 동기화

 

■ 조인

- 정규화한 테이블을 역정규화하는 키워드

- 원리 : 각 테이블의 Key를 사용하여 연결한다. 

- 장점 : 중복된 레코드 수를 줄인다. 이상현상을 방지하고 성능이 높아진다.

  1) 오라클 조인      - WHERE를 사용한 방법
  2) 공통 SQL 조인   - ANSI 방식

 

■ 조인의 종류

종류  
INNER  JOIN 조인 조건(두 테이블의 키)에 일치하는 데이터만 가져온다.
OUTER JOIN 조인 조건에 일치하는 데이터 및 일치하지 않는 데이터 모두 가져온다.
*일치하지 않는 데이터는 : NULL로 가져온다.
CROSS JOIN 별도의 조인 조건 없이 두 테이블을 조언 시 가능한 모든 경우의 데이터를 가져온다.
SELF    JOIN 자기 자신을 하나의 테이블로 취급하여 조인하는 방법
*칼럼명이 중복되므로 반드시 ALIAS를 사용하여 조인 필요

 

■ ANSI 조인

 

  INNER JOIN  

SELECT 
  a.APPLY_NO AS 순번,
  c.COM_NAME AS 기업명,
  c.COM_CAT AS 기업분야,
  c.COM_FOCUS AS 주력요소,
  TO_CHAR(a.APPLY_DATE, 'YYYY-MM-DD') AS 지원일자,
  a.APPLY_STATE AS 지원상태
FROM MY_APPLY a INNER JOIN COMPANY c
     ON a.APPLY_NO = c.APPLY_NO;

>> 결과

 

  OUTER JOIN  

 

  LEFT OUTER JOIN    

- 좌측테이블을 기준으로 조건에 일치하는 데이터 추출, 조건에 맞지 않으면 NULL

SELECT
  c.COM_NO AS 순번,
  c.COM_NAME AS 기업명,
  w.SALARY AS 연봉,
  w.OVERTIME_PAY AS 야근수당,
  W.FACILITY AS 시설
FROM COMPANY c LEFT OUTER JOIN WORK_ENV w
     ON c.COM_NO = w.COM_NO;

>> 결과

  RIGHT OUTER JOIN    

- 우측테이블을 기준으로 조건에 일치하는 데이터 추출, 조건에 맞지 않으면 NULL

SELECT
  c.COM_NO AS 순번,
  c.COM_NAME AS 기업명,
  w.SALARY AS 연봉,
  w.OVERTIME_PAY AS 야근수당,
  W.FACILITY AS 시설
FROM WORK_ENV w RIGHT OUTER JOIN COMPANY c
     ON w.COM_NO = c.COM_NO;

>> 결과

 

  CROSS JOIN  

SELECT
  c.COM_NO AS 순번,
  c.COM_NAME AS 기업명,
  w.SALARY AS 연봉,
  w.OVERTIME_PAY AS 야근수당,
  W.FACILITY AS 시설
FROM WORK_ENV w CROSS JOIN COMPANY c

>> 결과

- 심플컴패니2에 해당되는 연봉, 야근수당, 시설을 입력한 적이 없는데, 심플컴패니에 해당되는 부분이 추출됐다.

 

  SELF JOIN  

- 반드시 ALIAS를 써서 자기 자신의 테이블을 구분할 수 있게 해야한다.

SELECT
  c.COM_NO AS 순번,
  c2.COM_NAME AS 회사명,
  c2.COM_CAT AS 카테고리
FROM COMPANY c JOIN COMPANY c2
     ON c.COM_NO = c2.COM_NO;

>> 결과

 

■ 오라클 조인  ---- WHERE를 사용한다.

 

  INNER JOIN  

SELECT
  a.APPLY_NO AS 순번,
  c.COM_NAME AS 기업명,
  c.COM_CAT AS 기업분야,
  c.COM_FOCUS AS 주력요소,
  TO_CHAR(a.APPLY_DATE, 'YYYY-MM-DD') AS 지원일자,
  a.APPLY_STATE AS 지원상태
FROM MY_APPLY a, COMPANY c
WHERE a.APPLY_NO = c.APPLY_NO;

 

  OUTER JOIN  

 

  LEFT OUTER JOIN    

SELECT
  c.COM_NO AS 순번,
  c.COM_NAME AS 기업명,
  w.SALARY AS 연봉,
  w.OVERTIME_PAY AS 야근수당,
  W.FACILITY AS 시설
FROM COMPANY c, WORK_ENV w
WHERE c.COM_NO = w.COM_NO;

  RIGHT OUTER JOIN    

SELECT
  c.COM_NO AS 순번,
  c.COM_NAME AS 기업명,
  w.SALARY AS 연봉,
  w.OVERTIME_PAY AS 야근수당,
  W.FACILITY AS 시설
FROM WORK_ENV w, COMPANY c
WHERE w.COM_NO = c.COM_NO;

 

 

[출처]

- 뉴렉처 강의

- https://tragramming.tistory.com/74

■ 시퀀스란?

  오라클에서 순서를 지정해주는 객체   **오라클에서만 사용 가능

 

[1] 시퀀스의 생성

CREATE SEQUENCE seq_테이블명_idx START WITH 1 INCREMENT BY 1;
-- 또는 
CREATE SEQUENCE seq_테이블명_idx;

 

[2] 데이터 추가 시 시퀀스 추가 - 시퀀스명.nextVal

INSERT INTO sungtb VALUES(seq_sungtb_idx.nextVal, '일길동', 90, 80, 70);

 

[3] 시퀀스의 삭제

DROP SEQUENCE seq_sungtb_idx

 

■ 인덱스란?

  데이터베이스에서 데이터를 빠르게 탐색하기 위해 붙인 색인(포인터)

 

인덱스를 사용해야 하는 경우 

- 데이터 양이 많고 검색이 수정보다 빈번한 경우

- 도메인의 값이 다양한 값을 가지는 경우 

 

인덱스를 사용할 때 주의할 점

- 인덱스를 사용하는 열에 NULL이 지나치게 많으면 효율이 떨어진다.

 

 

■ NVL함수를 통해 인덱스 형성하기

[문법]

NVL(MAX(idx),0)+1
--> (1) Max(idx)는 idx칼럼에서 가장 큰 수를 반환
--> (2) NVL(a , 0)  :  a라는 칼럼에 NULL이 없으면 (or a라는 데이터가 있으면) 해당 데이터 반환
                    -- 그렇지 않고 NULL만 있으면 0으로 데이터를 대체한다.
    --> 곧, idx에 데이터가 없으면 0을 넣고, 있으면 그 데이터에 +1을 하겠다.

SELECT NVL(MAX(idx), 0) + 1 FROM book;
--> book이라는 테이블에서 가장 큰 인덱스 값을 가져오고 +1을 한다.

 

[예시]

CREATE TABLE book
(
   idx   INT,
   name  CHAR(3 CHAR) NOT NULL,
   price INT
);

ALTER TABLE book 
  ADD CONSTRAINT pk_book_idx PRIMARY KEY(idx);
  
-- SAMPLE DATA 입력
INSERT INTO book VALUES((SELECT nvl(MAX(idx), 0) + 1 FROM book),'Java',20000);
INSERT INTO book VALUES((SELECT nvl(MAX(idx), 0) + 1 FROM book),'Oracle',30000);
INSERT INTO book VALUES((SELECT nvl(MAX(idx), 0) + 1 FROM book),'HTML',24000);
INSERT INTO book VALUES((SELECT nvl(MAX(idx), 0) + 1 FROM book),'JSP',22000);

 

 

 

[출처]

- 인덱스 참조

https://ltk3934.tistory.com/67

https://coding-factory.tistory.com/746

- 시퀀스 참조

https://coding-factory.tistory.com/420

https://kerpect.tistory.com/17

 DB모델링

- ERD란? Entity Relationship Diagram, 개체 관계도라 하며,

            개체들 간의 관계를 나타내는 다이어그램이다.

- DB 모델링에 대한 자세한 내용은 아래 포스팅 참조

https://mangkyu.tistory.com/27

 

[Database] 6. 데이터 모델링(Data Modeling)

[ 본 사진은 쉽게 배우는 오라클로 배우는 데이터베이스 개론과 실습 ppt에서 캡처했습니다. ] 이번 장에서는 데이터 모델링(Data Modeling), ER 모델(ER Model)에 대해 알아보겠습니다. 1. 데이터 모델링

mangkyu.tistory.com

- DB 모델링 툴 

- 윈도우용툴 : EXERD, ERWIN (erwin.com) *erwin이 가장 자주 쓰는 툴이라고 한다.
- 맥용툴 : 에이쿼리툴 https://zakkum.tistory.com/76

 

 정규화란?

  데이터 베이스의 불필요한 중복을 줄이고 설계를 더 탄탄하게 재구성하는 테크닉

>>

  제 1 정규화   도메인이 원자 값(하나의 값)을 갖도록 테이블을 분해하는 것
  제 2 정규화   제 1정규화를 마친 테이블에서 완전함수 종속*을 만족하도록 테이블을 분해
  * 기본키의 부분 집합이 결정자가 되어서는 안된다. 

  EX. 테이블 1 ) 기본키 : 학생번호/강좌이름, 데이터 : 강의실, 성적
     * 성적은 학생번호/강좌이름에 종속적이다.
     * 강의실은 강좌이름에 종속적이다.
     
     ---> 테이블 1, 2로 분해
            테이블 1 ) 기본키 : 학생번호, 데이터 : 강좌이름, 성적
            테이블 2 ) 기본키 : 강좌이름, 데이터 : 강의실
  제 3 정규화   제 2정규화를 마친 테이블에 대해 이행적 함수 종속*을 없애도록 테이블을 분해
  * A, B, C 데이터가 있을 때, A -> B -> C 관계만 가능하도록 해야한다.
  * A -> C (X)

  EX. 테이블 1 ) 기본키 : 학생번호, 데이터 : 강좌이름, 수강료
     * 강좌이름은 학생번호에 종속적이다.
     * 수강료는 강좌이름에 종속적이다.

     ---> 테이블 1,2로 분해
     테이블 1 ) 기본키 : 학생번호, 데이터 : 강좌이름
     테이블 2 ) 기본키 : 강좌이름, 데이터 : 수강료
     * 학생번호 -> 강좌이름 -> 수강료
     >> 학생번호로 수강료를 알 수 있으면 안 된다.

 

* 완전 함수 종속 : 기본키가 여러개일 때, 모든 기본키에 종속적이어야 한다는 것

* 이행적 함수 종속 : 순차적인 이행에 따른 종속 관계 

 

 정규화의 장단점 - 가장 큰 이점 : 이상현상의 제거

장점 단점
[1] 데이터베이스 변경 시 이상 현상 방지
[2] 데이터베이스 구조 확장 시 재 디자인 최소화
[3] 사용자에게 데이터 모델을 더욱 의미있게 제공
테이블의 분해로 인해 테이블간 연산(JOIN)이 많아진다.
이로인해 질의에 대한 응답시간이 늦어질 수도 있다.
(성능저하의 가능성)

 

■ 역정규화를 하는 이유

  정규화를 거치면 릴레이션 간의 연산(JOIN 연산)이 많아지는데, 이로인해 성능이 저하될 우려가 있습니다.
  역정규화를 하는 가장 큰 이유는 성능 문제가 있는(읽기작업이 많이 필요한) DB의 전반적인 성능을 향상시키기 위함입니다.

 

이상현상(Anomaly)의 종류

 - 이상현상이란? 테이블을 잘못 설계하여 데이터를 삽입, 삭제, 수정할 때 생기는 논리적 오류

  삽입 이상    자료를 삽입할 때 특정 속성에 해당하는 값이 없어 NULL을 입력해야 하는 현상
  갱신 이상    중복된 데이터 중 일부만 수정되어 데이터 모순이 일어나는 현상
  삭제 이상    어떤 정보를 삭제하면, 의도하지 않은 다른 정보까지 삭제되어버리는 현상

 

 

[참조]

- 정규화 참조

https://dev-coco.tistory.com/62
https://mr-dan.tistory.com/10

https://programming119.tistory.com/225

 

 

 

 

'Language > SQL' 카테고리의 다른 글

[DATABASE] JOIN  (0) 2022.04.21
[DATABASE] SEQUENCE, INDEX  (0) 2022.04.21
[DATABASE] 뷰 (VIEW), 인라인 뷰  (0) 2022.04.19
[DATABASE] DECODE, CASE~END 문  (0) 2022.04.18
[DATABASE] 정렬(ORDER NY) 및 그룹 통계(GROUP BY)  (0) 2022.04.18

■ 뷰(VIEW)의 정의

- 뷰는 가상 객체로, 하나 이상의 테이블 또는 뷰의 데이터를 볼 수 있게 한다. 

- 실제로 테이블은 아니지만 테이블처럼 사용 가능

- 다른 뷰를 참조해 새로운 뷰를 생성할 수 있다.

 

■ 뷰(VIEW)의 사용 목적

- [1] 편리성 : 복잡한 명령/수식 등을 간결하게 사용 가능

- [2] 보안성 : 중요 데이터를 외부로 제한적으로 제공

  * DBO의 원천데이터를 선별적으로 가공하여 사용자에게 (조회)권한 부여

 

■ 뷰(VIEW)의 실제 형태

- .bat 파일과 같이 실제로는 Select ~ from SQL문이 들어가있는 형태

 

■ 뷰(VIEW)를 생성할 수 있는 권한 부여 *최고 권한자만 권한을 부여할 수 있다.

grant create view to 계정

 

■ 뷰(VIEW)의 생성 및 수정

더보기

>> 아래에서 예제를 사용하기 위한 테이블 생성 코드

CREATE TABLE MEMBER
(
   EIN     NUMBER(5)      NOT NULL, --EMPLOYMENT ID NUMBER
   NAME    CHAR(5 CHAR)   NOT NULL,
   ADDR    VARCHAR2(150)  NOT NULL, 
   SSN     CHAR(15 CHAR) NOT NULL, -- SOCIAL SECURITY NUMBER
   HIREDAY DATE,
   
   CONSTRAINT UNIQUE_MEMBER_EIN UNIQUE(EIN)
);

ALTER TABLE MEMBER
  ADD CONSTRAINT PK_MEMBER_EIN PRIMARY KEY(EIN);
  
INSERT INTO MEMBER VALUES(1, '김길동', '경기도 김포시 장기동', '951221-1354888','1998-07-22');
INSERT INTO MEMBER VALUES(2, '나길동', '서울시 관악구 테헤란로', '931221-2354888','1998-07-22');
INSERT INTO MEMBER VALUES(3, '다길동', '서울시 관악구 문성로', '950821-1354888','1998-07-22');
INSERT INTO MEMBER VALUES(4, '라길동', '천안시 동남구 대흥동', '921221-1354888','1998-07-22');
INSERT INTO MEMBER VALUES(5, '마길동', '천안시 동남구 대흥동', '911221-2354888','1998-07-22');
INSERT INTO MEMBER VALUES(6, '바길동', '삼척시 원덕읍 호산리', '950321-1354888','1998-07-22');
INSERT INTO MEMBER VALUES(7, '사길동', '삼척시 원덕읍 호산리', '650721-2354888','1998-07-22');
INSERT INTO MEMBER VALUES(8, '아길동', '삼척시 원덕읍 호산리', '630121-1354888','1998-07-22');
INSERT INTO MEMBER VALUES(9, '자길동', '서울시 관악구 문성로', '571221-2354888','1998-07-22');
INSERT INTO MEMBER VALUES(10, '차길동', '경기도 김포시 장기동', '881221-1354888','1998-07-22');

SELECT * FROM MEMBER
CREATE OR REPLACE 뷰이름
AS
  /* SQL 문장 */

[예제1 _ 뷰 만들기]

-- 맴버 테이블에서 입사연도/입사월/입사일을 구하는 뷰

CREATE OR REPLACE VIEW MEMBER_HIREDATES
AS
	SELECT
	  MB.*,
	  TO_NUMBER(TO_CHAR(HIREDAY, 'YYYY')) AS HIRE_YEAR,
	  TO_NUMBER(TO_CHAR(HIREDAY, 'MM')) AS HIRE_MONTH,
	  TO_NUMBER(TO_CHAR(HIREDAY, 'DD')) AS HIRE_DAY
	FROM MEMBER MB
	WHERE SAHIRE IS NOT NULL
    
-- 생성한 뷰를 통해 입사연도별 인원수 구하기
SELECT
  HIRE_YEAR,
  COUNT(*) 인원수
FROM MEMBER_HIREDATES
GROUP BY HIRE_YEAR

[예제2_ 뷰의 보안성을 이해하는 예제]

-- 주민 등록번호 마스킹 처리한 MEMBER테이블을 뷰로 저장
CREATE OR REPLACE VIEW SSN_MASKED_VIEW
AS
	SELECT
	  EIN, NAME, ADDR, 
	  (
	    SUBSTR(SSN, 1, 7)
	    ||
	    '*******'
	  )AS SSN,
	  HIREDAY
	FROM MEMBER

SELECT * FROM SSN_MASKED_VIEW;

>> 결과

 

■ 인라인 뷰 사용하기

- 테이블/뷰에서 모든 데이터 + 별도의 함수식에 의한 데이터를 불러올 때나,

- 코드를 간소화하고 싶을 때에 인라인 뷰를 사용한다.

SELECT
  임시별칭.*,
  CASE문 또는 수식
FROM (SELECT * FROM 테이블/뷰) 임시별칭

[예제]

-- 맴버 테이블에서 계절만 따로 뽑아오기

SELECT
  MB.*,
  CASE FLOOR(TO_NUMBER(TO_CHAR(HIREDAY,'DD')) / 3) 
     WHEN 1 THEN '봄'
     WHEN 2 THEN '여름'
     WHEN 3 THEN '가을'
     ELSE '겨울'
  END AS 생일
FROM (SELECT * FROM MEMBER) MB   -- MB라는 뷰가 임시로 하나 생성된 것과 같은 원리(실제로 생성x)

 

 

■ 뷰(VIEW)의 사용 권한 부여

- DBO(실제 테이블 소유 계정)는 뷰를 통해 DBA(테이블을 사용하는 계정)에게 일부의 데이터만 선택하거나 보안화하여 제공한다.

- 사용 권한을 부여하는 코드는 아래와 같다.

GRANT SELECT ON 뷰이름 TO 계정명

 

[예제]

-- [새로운 계정에 조회 권한만 부여할 때]
-- SQLPLUS에서 아래와 같이 작성

-- 1. 최고 권한자로 로그인
SQLPLUS SYSTEM/ORACLE;

-- 2. 새로운 계정 생성
CREATE USER MYTEST1 IDENTIFIED BY MYTEST1;

-- 3. 새로운 계정에 연결 권한 부여
GRANT CONNECT TO MYTEST1;

-- 4. 사용할 뷰를 가진 DBO계정으로 연결
CONN TEST1/TEST1;

-- 5. 새로운 계정에 뷰 권한 부여
GRANT SELECT ON SSN_MASKED_VIEW TO MEMBER_TEST;

-- 6. 새로운 계정으로 연결
CONN MYTEST1/MYTEST1;

-- 7. 뷰 조회
SELECT * FROM SSN_MASKED_VIEW;

 

 

[출처]

- https://coding-factory.tistory.com/417

- 국비지원 수업 내용 참조

■ DECODE 문

형식 : DECODE(필드(연산식), 비교값1, 결과값1,
                            비교값2, 결과값2,
                            비교값3, 결과값3,
                            기본값)
CREATE TABLE MEMBER
(
   IDX NUMBER(38),
   DEPTNO NUMBER(10),
   NAME CHAR(4 CHAR),
   GENDER CHAR(3 CHAR)
);

/* INSERT 생략 */

SELECT
   IDX, DEPTNO, NAME,
   DECODE(DEPTNO, 10, '총무부',
                  20, '영업부',
                  30, '전산실',
                  40, '관리부',
                  '경리부') AS DEPTNAME;

 

■ CASE ~ END 문

CASE ~ END

-- 형식 1 : 하나의 조건에 대한 값을 비교하여 결과를 얻는다.
    CASE 필드(연산식)
      WHEN 비교값1 THEN 결과값1
      WHEN 비교값2 THEN 결과값2
      WHEN 비교값3 THEN 결과값3
      ELSE 기본값
    END
    
-- 형식2 : 각각의 조건에 대한 별개의 결과를 얻는다.
   CASE 
     WHEN 조건1 THEN 결과값1
     WHEN 조건2 THEN 결과값2
     ELSE 기본값
   END
-- 형식 1 : 하나의 조건에 대한 값을 비교하여 결과를 얻는다.
        CASE 필드(연산식)
          WHEN 비교값1 THEN 결과값1
          WHEN 비교값2 THEN 결과값2
          WHEN 비교값3 THEN 결과값3
          ELSE 기본값
        END
        
  --CASE ~END문 이용해서 부서명 출력
        
  SELECT 
     SABUN,
     SANAME,
     DEPTNO,
     CASE DEPTNO
       WHEN 10 THEN '총무부'
       WHEN 20 THEN '영업부'
       WHEN 30 THEN '전산실'
       WHEN 40 THEN '관리부'
       ELSE '경리부'
     END AS DNAME
  FROM SAWON
 
 ---형식2 : 각각의 조건에 대한 별개의 결과를 얻는다.
       CASE 
         WHEN 조건1 THEN 결과값1
         WHEN 조건2 THEN 결과값2
         ELSE 기본값
       END

  --CASE ~END 문 이용해서 성별 추출
  SELECT
    GOBUN,
    GONAME,
    GOJUMIN,
    CASE
      WHEN SUBSTR(GOJUMIN,8,1) IN ('1', '3', '5', '7', '9') THEN '남자'
      WHEN SUBSTR(GOJUMIN,8,1) IN ('2', '4', '6', '8', '0') THEN '여자'
    END AS GENDER
  FROM GOGEK;
  
  SELECT
    GOBUN,
    GONAME,
    GOJUMIN,
    CASE
      WHEN TO_NUMBER(SUBSTR(GOJUMIN,8,1)) IN (1,3,5,7,9) THEN '남자'
      WHEN TO_NUMBER(SUBSTR(GOJUMIN,8,1)) IN (2,4,6,8,0) THEN '여자'
    END AS GENDER
  FROM GOGEK;
  
  
 -- Q1. 사원테이블에서 사번, 이름, 입사시즌(입사계절)을 추출
  SELECT
    SABUN AS 사번,
    SANAME AS 이름,
    CASE FLOOR(TO_NUMBER(TO_CHAR(SAHIRE,'MM'))/3)
      WHEN 1 THEN '봄'
      WHEN 2 THEN '여름'
      WHEN 3 THEN '가을'
      ELSE '겨울'
    END AS 입사시즌
  FROM SAWON
  
 -- Q2. 고객테이블에서 고객번호, 고객이름, 고객의 출생 계절 추출
  
  -- [형식1]
  SELECT
    GOBUN,
    GONAME,
    CASE FLOOR(TO_NUMBER(SUBSTR(GOJUMIN,3,2))/3)
      WHEN 1 THEN '봄'
      WHEN 2 THEN '여름'
      WHEN 3 THEN '가을'
      ELSE '겨울'
    END AS 출생계절
  FROM GOGEK;
  
  -- [형식2]
  SELECT
    GOBUN,
    GONAME,
    GOJUMIN,
    CASE 
      WHEN SUBSTR(GOJUMIN,3,2) IN ('03','04','05') THEN '봄'
      WHEN SUBSTR(GOJUMIN,3,2) IN ('06','07','08') THEN '여름'
      WHEN SUBSTR(GOJUMIN,3,2) IN ('09','10','11') THEN '가을'
      ELSE '겨울'
    END 출생계절
  FROM GOGEK

■ 정렬 (ORDER BY)

ORDER BY 필드 [asc | desc]  필드를 기준으로 정렬
ORDER BY 필드1 [asc | desc], 필드2 [asc | desc] 필드1을 기준으로 1차 정렬 후, 필드2를 기준으로 2차 정렬

* ASC = ASCENDING (오름차순), DESC = DESCENDING (내림차순)

 

■ 그룹 통계 (GROUP BY)

GROUP BY 필드 필드의 분류를 기준으로 그룹핑

* SELECT ~ FROM 사이에 들어올 수 있는 항목, GROUP BY 컬럼, 통계함수(COUTN,SUM,AGV,MIN,MAX..)

 

[예제1 _ 부서별 인원수, 급여 합계를 구하는 쿼리]

SELECT 
  DEPTNO 부서번호,
  AVG(SALARY) 급여평균,
  MAX(SALARY) 최고급여,
  MIN(SALARY) 최소급여
FROM MEMBER
GROUP BY DEPTNO
ORDER BY DEPTNO

 

[예제2 _ 연봉 3000이상인 부서간 성별 인원수, 평균급여]

SELECT
   DEPTNO 부서번호,
   GENDER 성별,
   COUNT(*) 인원수,
   AVG(SALARY) 급여평균
FROM MEMBER
WHERE SALARY >= 3000
GROUP BY DEPTNO, GENDER
ORDER BY DEPTNO, GENDER;

 

'Language > SQL' 카테고리의 다른 글

[DATABASE] 뷰 (VIEW), 인라인 뷰  (0) 2022.04.19
[DATABASE] DECODE, CASE~END 문  (0) 2022.04.18
[DATABASE] SELECT쿼리 진행 순서, 서브쿼리  (0) 2022.04.17
[DATABASE] 중복 값 제거 _ DISTINCT  (0) 2022.04.16
[DATABASE] 함수  (0) 2022.04.15

■ SELECT 쿼리 진행 순서

  FROM, JOIN > WHERE, GROUP BY, HAVING > SELECT > ORDER BY > DISTINCT > LIMIT

 

▼ 자세한 진행 순서 [더보기]

더보기

1. FROM과 JOIN

- JOIN이 먼저 실행되어 데이터가 SET으로 모아지게 된다. 서브쿼리도 함께 포함되어 임시 테이블을 만들 수 있게 도와준다.

2. WHERE

- 데이터셋을 형성하게 되면 WHERE의 조건이 개별 행에 적용된다. WHERE절의 제약 조건은 FROM절로 가져온 테이블에 적용될 수 있다.

3. GROUP BY

- WHERE의 조건 적용 후 나머지 행은 GROUP BY절에 지정된 열의 공통 값을 기준으로 그룹화된다. 쿼리에 집계 기능이 있는 경우에만 이 기능을 사용해야 한다.

4. HAVING

- GROUP BY절이 쿼리에 있을 경우 HAVING 절의 제약조건이 그룹화된 행에 적용된다.

5. SELECT

- SELECT에 표현된 식이 마지막으로 적용된다.

6. DISTINCT

- 표현된 행에서 중복된 행은 삭제

7.ORDER BY

- 지정된 데이터를 기준으로 오름차순, 내림차순 지정

8. LIMIT

- LIMIT에서 벗어나는 행들은 제외되어 출력된다.



출처: https://dev-coco.tistory.com/158 [슬기로운 개발생활😃]

 

 

■ 서브 쿼리

단일 행(Sing-Row) 서브쿼리 SELECT 문장으로 부터 오직 하나의 행 만을 검색하는 질의
다중 행(Multiple-Row) 서브쿼리 SELECT 문장으로부터 하나 이상의 행을 검색하는 질의
다중 열(Multiple-Column) 서브쿼리 SELECT 문장으로부터 하나 이상의 컬럼을 검색하는 질의
FROM절상의 서브쿼리(INLINE VIEW) FROM절상에 오는 서브쿼리로 VIEW처럼 작용
상관관계 서브 쿼리 바깥쪽 쿼리의 컬럼 중의 하나가 안쪽 서브쿼리의 조건에 이용되는 처리 방식

 

[단일행 서브쿼리 예시]

-- Q1. 사원 테이블에서 이미자와 동일한 계절에 입사한 직원 추출
SELECT * FROM SAWON 
  WHERE TRUNC(TO_NUMBER(TO_CHAR(sahire,'MM')) / 3, 0) = (
                                                            SELECT
                                                               TRUNC(TO_NUMBER(TO_CHAR(sahire,'MM')) / 3, 0)
                                                               FROM SAWON
                                                               WHERE SANAME = '이미자'
                                                         );


-- Q2. 사원 테이블에서 총 급여 평균 보다 더 급여를 많이 받는 직원 추출
SELECT * FROM SAWON
  WHERE SAPAY > (SELECT AVG(SAPAY) FROM SAWON);


-- Q3. 사원 테이블에서 최초 입사한 직원을 추출
SELECT * FROM SAWON
 WHERE SAHIRE = (SELECT MIN(SAHIRE) FROM SAWON)


-- Q4. 사원 테이블에서 가장 최근 입사한 직원을 추출 
SELECT * FROM SAWON
 WHERE SAHIRE = (SELECT MAX(SAHIRE) FROM SAWON)

-- Q5. 사원 테이블에서 일자를 기준으로 최초입사자, 최근입사자를 모두 추출
SELECT * FROM SAWON
  WHERE SAHIRE = (SELECT MIN(SAHIRE) FROM SAWON)
        or
        SAHIRE = (SELECT MAX(SAHIRE) FROM SAWON);

 

[다중행 연산자]

IN 연산자 하나의 컬럼이 여러개의 '=' 조건을 가지는 경우 사용. 포함된다는 의미
ANY 연산자 Subquery의 여러 결과값 중 어느 하나의 값만 만족이 되면 행을 반환
ALL 연산자 Subquery의 여러 결과값 중 모든 결과 값을 만족해야 행을 반환
EXISTS 연산자  Subquery 데이터가 존재하는가를 체크해 존재 여부(TRUE,FALSE)를 결과로 반환
-- IN은 같냐를 바꾼 것
SELECT * FROM sawon
 WHERE deptno IN (SELECT deptno FROM sawon WHERE saname = '최불암')
   
-- ANY를 붙이면 다중행 있을 때,
-- 3000이상 받는지 OR 3500이상 받는지
-- (이미자가 둘 이상 있을 떄) 사원 테이블에서 이미자와 같거나 더 큰 급여를 받는 직원 추출
SELECT * FROM sawon
 WHERE sajob >= ANY (SELECT sajob FROM sawon WHERE saname = '이미자')

-- ALL를 붙이면 다중행 있을 때.
-- 3000이상이면서 35000이상이어야한다. 
-- (이미자가 둘 이상 있을 떄) 사원 테이블에서 이미자와 같거나 더 큰 급여를 받는 직원 추출
SELECT * FROM sawon
 WHERE sajob >= ALL (SELECT sajob FROM sawon WHERE saname = '이미자')

 

[상관쿼리]

- 주 쿼리의 정보를 서브쿼리가 이용해서 추출된 결과를 주쿼리를 선택하는데 이용한다.

-- 사원 테이블에서 각 부서별 최고 급여자를 추출한다고 했을 때

[방법1] 조건을 하나씩 달아준다.
-- >> 아래 코드의 경우, 부서가 증가하거나, 부서번호가 변경되면 일일히 수정해야한다.
SELECT * FROM SAWON
  WHERE SAPAY = (SELECT MAX(SAPAY) FROM SAWON WHERE DEPTNO = 10) and DEPTNO = 10
        OR
        SAPAY = (SELECT MAX(SAPAY) FROM SAWON WHERE DEPTNO = 20) and DEPTNO = 20
        OR
        SAPAY = (SELECT MAX(SAPAY) FROM SAWON WHERE DEPTNO = 30) and DEPTNO = 30
        OR
        SAPAY = (SELECT MAX(SAPAY) FROM SAWON WHERE DEPTNO = 40) and DEPTNO = 40
  ORDER BY DEPTNO ASC;  -- 부서번호별 오름차순

[방법2] 상관쿼리르 사용한다.
-- 위의 SQL문의 문제점(코드가 너무 길다) 해결 
-- >> 상관쿼리 : 주쿼리의 정보를 서브쿼리가 이용해서 추출된 결과를 주쿼리를 선택하는데 이용한다. 
SELECT * FROM SAWON S1
  WHERE SAPAY = (SELECT MAX(SAPAY) FROM SAWON WHERE DEPTNO = S1.DEPTNO)
  ORDER BY S1.DEPTNO ASC;

 

 

[출처]

- https://dev-coco.tistory.com/158

- https://myjamong.tistory.com/172

- http://www.gurubee.net/

- 국비지원과정 수업 참조

+ Recent posts