트리거(Trigger) 란? 

  • Triggering Event, 즉 Oracle DML 문인 INSERT, UPDATE, DELETE문이 TABLE에 대해 행해질 때 자동으로 수행되는 PROCEDURE
  • 행 트리거
    - 컬럼의 각각의 행의 데이터 행 변화가 생길때마다 실행
    - 실제 그 데이터 행의 실제값 제어가능
    - :old, :new
  • 문장 트리거
    - 트리거 사건에 의해 단 한번 실행되며, 컬럼의 각 데이터 행을 제어 할 수 없음
    - 컬럼의 데이터 값에 관계없이 컬럼에 변화가 일어남을 감지하여 실행되는 트리거
  • 문법

CREATE [OR REPLACE] TRIGGER trigger_name

BEFORE | AFTER

     trigger_event ON table_name

     [FOR EACH ROW]

     [WHEN (condition)]

     PL/SQL block

 

- BEFORE : INSERT, UPDATE, DELETE문이 실행되기 전에 트리거가 실행

- AFTER : INSERT, UPDATE, DELETE문이 실행된 후 트리거가 실행

- trigger_event : INSERT, UPDATE, DELETE 중에서 한 개 이상 올 수 있음

- FOR EACH ROW : 이 옵션이 있으면 행 트리거가 됨

 

  • Procedure와 Trigger의 차이점
Procedure Trigger
생성하면 소스코드와 실행코드(P-CODE)가 생성됨 생성하면 소스코드와 실행코드(P-CODE)가 생성됨
EXECUTE 명령어로 실행 생성 후 자동 실행
COMMIT, ROLLBACK 실행 가능 COMMIT, ROLLBACK 실행 안됨
  • 소스 예제
CREATE OR REPLACE TRIGGER Emp_Sal_Change
BEFORE
INSERT OR UPDATE ON emp
FOR EACH ROW
  WHEN (new.empno > 0)
  DECLARE
    sal_diff number;
BEGIN
  sal_diff := :new.sal - :old.sal;
  DBMS_OUTPUT.PUT_LINE('이전 급여 : ' ||:old.sal);
  DBMS_OUTPUT.PUT_LINE('신 급여 : ' || :new.sal);
  DBMS_OUTPUT.PUT_LINE('급여 차액 : '|| sal_diff);
END;
-- Table 추적 trigger
CREATE OR REPLACE TRIGGER emp_row_aud
  AFTER insert OR update OR delete ON emp
  FOR EACH ROW
BEGIN
  IF INSERTING THEN
    INSERT INTO emp_row_audit
      VALUES(emp_row_seq.NEXTVAL, :new.ename, 'inserting', SYSDATE);
  ELSIF UPDATING THEN
    INSERT INTO emp_row_audit
      VALUES(emp_row_seq.NEXTVAL, :old.ename, 'updating', SYSDATE);
  ELSIF DELETING THEN
    INSERT INTO emp_row_audit
      VALUES(emp_row_seq.NEXTVAL, :old.ename, 'deleting', SYSDATE);
  END IF;
END;

 

 

Ref. http://www.gurubee.net/lecture/1076

Package란? 

  • 오라클 데이터베이스에 저장되어 있는 서로 관련있는 PL/SQL 프로지져와 함수들의 집합
  • 사용방법

선언부 (Java에서 Interface라고 생각하면 이해하기 편함!)

create or replace PACKAGE emp_info AS
  PROCEDURE all_emp_info;
  PROCEDURE all_sal_info;
  -- 특정 부서의 사원 정보
  PROCEDURE dept_emp_info (v_deptno IN NUMBER);
  -- 특정 부서의 급여 정보
  PROCEDURE dept_sal_info (v_deptno IN NUMBER);
END emp_info;

구현부 (Java에서 Implement 라고 이해합시당)

create or replace PACKAGE BODY emp_info AS
  -----------------------------------------------------------------
  -- 모든 사원의 사원 정보
  -----------------------------------------------------------------
  PROCEDURE all_emp_info
  IS
  CURSOR emp_cursor IS
      SELECT empno, ename, to_char(hiredate, 'YYYY/MM/DD') hiredate
      FROM   emp
      ORDER BY hiredate;
  BEGIN
      DBMS_OUTPUT.ENABLE;
      FOR aa IN emp_cursor LOOP
          DBMS_OUTPUT.PUT_LINE('사번 : ' || aa.empno);
          DBMS_OUTPUT.PUT_LINE('성명 : ' || aa.ename);
          DBMS_OUTPUT.PUT_LINE('입사일 : ' || aa.hiredate);
      END LOOP;
  EXCEPTION
      WHEN OTHERS THEN
          DBMS_OUTPUT.PUT_LINE(SQLERRM || '에러 발생');
  END all_emp_info;
   
  -----------------------------------------------------------------
  -- 모든 사원의 급여 정보
  -----------------------------------------------------------------
  PROCEDURE all_sal_info
  IS
    CURSOR emp_sal_cursor IS
        SELECT AVG(sal) avg_sal, MAX(sal) max_sal, MIN(sal) min_sal
        FROM emp;
  BEGIN
    DBMS_OUTPUT.ENABLE;
    
    FOR aa IN emp_sal_cursor LOOP
      DBMS_OUTPUT.PUT_LINE('전체급여 평균 : ' || aa.avg_sal);
      DBMS_OUTPUT.PUT_LINE('최대급여금액 : ' ||  aa.max_sal);
      DBMS_OUTPUT.PUT_LINE('최소급여금액 : ' || aa.min_sal);
    END LOOP;
    
  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');
  END all_sal_info;
  -----------------------------------------------------------------
  -- 특정 부서의 사원 정보
  -----------------------------------------------------------------
  PROCEDURE dept_emp_info (v_deptno IN NUMBER)
  IS
    CURSOR emp_info IS
      SELECT empno, ename, to_char(hiredate, 'YYYY/MM/DD') hiredate
      FROM   emp
      WHERE  deptno = v_deptno;
  BEGIN
    DBMS_OUTPUT.ENABLE;
    
    FOR aa IN emp_info LOOP
      DBMS_OUTPUT.PUT_LINE('사번 : ' || aa.empno);
      DBMS_OUTPUT.PUT_LINE('성명 : ' || aa.ename);
      DBMS_OUTPUT.PUT_LINE('입사일 : ' || aa.hiredate);
    END LOOP;
  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLERRM || '에러 발생');
  END dept_emp_info;
  
  -----------------------------------------------------------------
  -- 특정 부서의 급여 정보
  -----------------------------------------------------------------
  PROCEDURE dept_sal_info (v_deptno IN NUMBER)
  IS
    CURSOR emp_info IS
      SELECT ROUND(AVG(sal),3) avg_sal, MAX(sal) max_sal, MIN(sal) min_sal
      FROM   emp;
  BEGIN
    DBMS_OUTPUT.ENABLE;
    
    FOR aa IN emp_info LOOP
      DBMS_OUTPUT.PUT_LINE('전체 급여 평균 : ' || aa.avg_sal);
      DBMS_OUTPUT.PUT_LINE('최대 급여 금액 : ' || aa.max_sal);
      DBMS_OUTPUT.PUT_LINE('최소 급여 금액 : ' || aa.min_sal);
    END LOOP;
    
  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLERRM || '에러 발생');
  END dept_sal_info;
END emp_info;

Cursor란?

  • Private SQL 작업 영역 (SQL문 실행 -> 처리 -> 저장 하는 작업 영역)
  • Oracle Server에 의해 실행되는 모든 SQL 문은 연관된 각각의 커서를 소유함.
  • 개념을 Java에서의 ResultSet으로 생각하자!

Implicit Cursor (암시적 커서)

  • 모든 DML과 PL/SQL SELECT문에 대해 선언됨
  • Implicit cursor의 attribute
    - SQL%ROWCOUNT : 해당 SQL 문에 영향을 받는 행의 수
    - SQL%FOUND : 해당 SQL 영향을 받는 행의 수가 한 개 이상일 경우 TRUE
    - SQL%NOTFOUND : 해당 SQL 문에 영향을 받는 행의 수가 없을 경우 TRUE
    - SQL%ISOPEN항상 FALSE, 암시적 커서가 열려 있는지의 여부 검색
CREATE OR REPLACE PROCEDURE Implicit_Cursor
(p_empno IN emp.empno%TYPE)
IS
  v_sal emp.sal%TYPE;
  v_update_row NUMBER;
BEGIN
  DBMS_OUTPUT.ENABLE;
  
  SELECT sal
  INTO v_sal
  FROM emp
  WHERE empno = p_empno;
  
  -- 검색한 데이터가 있을 경우
  IF SQL%FOUND THEN
    DBMS_OUTPUT.PUT_LINE('검색한 데이터가 존재합니다 : ' || v_sal);
  END IF;
  
  UPDATE emp
  SET sal = sal*1.1
  WHERE empno = p_empno;

  -- 변경된 row의 개수
  v_update_row := SQL%ROWCOUNT;
  DBMS_OUTPUT.PUT_LINE('급여가 인상된 사원 수 : ' || v_update_row);

EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('검색한 데이터가 없습니다.');
END;

 

Explicit Cursor (명시적 커서)

  • 프로그래머에 의해 선언되며 이름이 있는 커서
  • 4단계 명령에 의해서 사용됨 (DECLARE -> OPEN -> FETCH -> CLOSE)
    1. Declare Cursor
       - 커서에 이름 부여하고 커서 내에서 수행할 select 문 정의

    2. Open 단계
       - 참조되는 변수 연결하고 Select문 실행
       - Action Set(Select로 검색된 Row들)은 Fetch단계에서 사용
       - Cursor는 Action Set을 생성한 다음, 첫번째 Row에 Pointer를 위치시켜 가리키게 됨.
       - Cursor를 Open할 때 검색된 Row가 없어도 에러 발생X
       - Fetch 한 후에 Cursor의 Attribute를 살펴서 알아볼 수 있음.

    3. Fetch 단계
       - Cursor로부터 Pointer가 존재하는 Record의 값을 변수에 전달해줌.
       - 그리고, pointer는 active set의 다음 record로 이동

    4. Close 단계
       - Record의 Active Set을 닫아주고, 다시 새로운 Active Set을 만들어 Open할 수 있게 해줌.
  • Explicit Cusor의 attribute
    - %ISOPEN : cursor가 open되어 있으면 true
    - %NOTFOUND : fetch문에 의해 검색도니 데이터가 없으면 true
    - %FOUND : fetch문에 의해 검색된 데이터가 있으면 true
    - %ROWCOUNT : fetch한 데이터의 총 개수
  • 예제 소스
CREATE OR REPLACE PROCEDURE Show_Emp3
(p_empno IN emp.empno%TYPE)
IS
  -- 1. Declare cursor
  CURSOR emp_cursor IS
    SELECT ename, job, sal
    FROM emp
    WHERE empno LIKE p_empno||'%';
    
  -- cursor fetch하기 위한 변수 선언    
  v_ename emp.ename%TYPE;
  v_job   emp.job%TYPE;
  v_sal   emp.sal%TYPE;
BEGIN
  DBMS_OUTPUT.ENABLE;
  -- 2. Open cursor
  OPEN emp_cursor;
  DBMS_OUTPUT.PUT_LINE('이름    업무    급여');
  DBMS_OUTPUT.PUT_LINE('------------------');
  
  -- 3. Fetch cursor
  LOOP -- do while과 동일
    FETCH emp_cursor INTO v_ename, v_job, v_sal;
    EXIT WHEN emp_cursor%NOTFOUND;  -- *반드시 exit 조건문 써줘야함*
    DBMS_OUTPUT.PUT_LINE(v_ename || '   ' || v_job || '   ' || v_sal);
  END LOOP;
  
  DBMS_OUTPUT.PUT_LINE(emp_cursor%ROWCOUNT || '개의 행 선택');
  
  -- 4. Close cursor
  CLOSE emp_cursor;
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(SQLERRM || '에러 발생');
END;

 

  • 항상 4단계를 거치기에는 너무 번거롭다....
    => For statement를 사용하면 Open + Fetch + Close 한번에 해줌!
         : 실무에서는 이 방법 사용. 하지만 위의 개념은 반드시 알아야함.
CREATE OR REPLACE PROCEDURE ForCursor_Test
IS
  CURSOR dept_sum IS
        SELECT b.dname, COUNT(a.empno) cnt, SUM(a.sal) salary
        FROM   emp a, dept b
        WHERE  a.deptno = b.deptno
        GROUP BY b.dname;

BEGIN
  DBMS_OUTPUT.ENABLE;
  
  -- Cursor를 for문에 실행
  FOR emp_list IN dept_sum LOOP
        DBMS_OUTPUT.PUT_LINE('부서명 : ' || emp_list.dname);
        DBMS_OUTPUT.PUT_LINE('사원수 : ' || emp_list.cnt);
        DBMS_OUTPUT.PUT_LINE('급여합계 : ' || emp_list.salary);
  END LOOP;
  
EXCEPTION
  WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLERRM || '에러 발생 ');
END;

 

PL/SQL 기본 문법

  • 변수 선언: var_name data_type
  • %TYPE Attribute
    : 테이블 컬럼과 같은 데이터 타입으로 설정할 경우 사용
    - 사용법: TABLE__NAME.COLUME_NAME%TYPE
    - ex) v_name emp.name%TYPE -- 데이터 타입을 가져오겠다.
  • %ROWTYPE Attribute
    : 테이블 컬럼명과 데이터 타입을 그대로 가져올 경우 사용
    - ex) v_emp emp%ROWTYPE; -- v_emp.empno 로 데이터 접근
  • IF Statement: 아래 소스 참고
  • FOR LOOP Statement
CREATE OR REPLACE PROCEDURE Table_Test2
IS
  -- dept 테이블의 속성을 테이블 타입으로 설정
  TYPE dept_table_type  IS TABLE OF dept%ROWTYPE INDEX BY BINARY_INTEGER;
  
  -- 테이블 타입으로 변수를 선언해서 사용
  dept_table    dept_table_type;
  i             BINARY_INTEGER := 0;
BEGIN
  DBMS_OUTPUT.ENABLE;
  
  /* dept_list 는 자동선언되는 BINARY_INTEGER형 변수로 1씩 증가한다.
     dept_list 대신 다른 문자열 사용 가능 */
  FOR dept_list IN (SELECT *
                    FROM   dept)
  LOOP
    dept_table(i).deptno  := dept_list.deptno;
    dept_table(i).dname   := dept_list.dname;
    dept_table(i).loc     := dept_list.loc;
    i := i +1;
  END LOOP;
  
  FOR cnt IN 0..i-1 LOOP
    DBMS_OUTPUT.PUT_LINE('부서번호 : ' || dept_table(cnt).deptno || '  부서명 : ' || dept_table(cnt).dname
                          || '  위치 : ' || dept_table(cnt).loc);
  END LOOP;
  
END;

 

 

PL/SQL 에러 처리 방법

1. Predefined Exception - 미리 정의된 에러

: NO_DATA_FOUND, TOO_MANY_ROWS, INVALID_CURSOR, ZERO_DIVIDE, DUP_VAL_ON_INDEX ...

CREATE OR REPLACE PROCEDURE Show_emp
( p_sal   IN  emp.sal%TYPE)
IS
  v_ename   emp.ename%TYPE;
  v_sal     emp.sal%TYPE;
  v_job     emp.job%TYPE;
  
BEGIN
  DBMS_OUTPUT.ENABLE;
  
  SELECT ename,   sal,    job
  INTO   v_ename, v_sal,  v_job
  FROM   emp
  WHERE  sal = p_sal;
 
 DBMS_OUTPUT.PUT_LINE('이름  ' || '급여  ' || '직책 ');
 DBMS_OUTPUT.PUT_LINE('---------------------------');
 DBMS_OUTPUT.PUT_LINE(v_ename || '  ' || v_sal || '  ' || v_job);

EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('해당 급여 받는 사람 없음, 다시 조사하세요');
  WHEN TOO_MANY_ROWS THEN
    DBMS_OUTPUT.PUT_LINE('해당 급여 받는 사람 하나 이상, CURSOR 문 전환 필요');
END;

 

2. Non-Predefined Exception

: Oracle server error 중 미리 정의되지 않은 error를 명시적으로 사용자가 선언하여 사용

CREATE OR REPLACE PROCEDURE DEL_DEPT
(   p_deptno    dept.deptno%TYPE)
IS  
    -- exception 타입인 변수 선언
    FK_ERR      EXCEPTION;    			
    -- Oracle server에서 제공하는 error번호를 EXCEPTION_INIT을 이용하여 연결
    PRAGMA      EXCEPTION_INIT(FK_ERR, -2292);
BEGIN
    DBMS_OUTPUT.ENABLE;
    
    DELETE FROM DEPT
    WHERE  deptno = p_deptno;
    COMMIT;

-- 이 Section에서 error 처리
EXCEPTION
    WHEN FK_ERR THEN
      ROLLBACK;
      DBMS_OUTPUT.PUT_LINE('참조되는 emp가 있어 삭제불가하오니 양해바랍니다.');
END;

 

3. User Defined Exception

: business error - 이런 것들을 log table에 넣어서 업무 프로세스를 개선할 수 있다.

: RAISE문을 사용하여 에러 발생 시킴

----------------------------------------------------------
-- 오라클 저장함수 RAISE_APPLICATION_ERROR를 사용하여 
-- 오류코드 -20000부터 -20999의 범위 내에서 사용자
-- 정의 예외를 만들수 있다.
-- STEP 1 : 예외의 이름을 선언 (선언절)
-- STEP 2 : RAISE문을 사용하여 직접적으로 예외를 발생시킨다(실행절)
-- STEP 3 : 예외가 발생할 경우 해당 예외를 참조한다(예외절)
----------------------------------------------------------
CREATE OR REPLACE PROCEDURE User_Exception
(v_deptno IN emp.deptno%TYPE)
IS
user_define_error EXCEPTION;  -- step 1: 예외 이름 선언
cnt NUMBER;
BEGIN
  DBMS_OUTPUT.ENABLE;
  
  SELECT COUNT(empno)
  INTO cnt
  FROM emp
  WHERE deptno = v_deptno;
  
  IF cnt < 5 THEN
    RAISE user_define_error; -- step 2: RAISE문을 사용하여 직접적으로 예외 발생
  ELSE
    DBMS_OUTPUT.PUT_LINE('조직의 직무 향상 필요');
  END IF;
EXCEPTION
  -- 예외 발생할 경우 해당 예외참조
  WHEN user_define_error THEN
    RAISE_APPLICATION_ERROR(-20001, '부서에 인원 충원 필요');
END;

 

 

'Database' 카테고리의 다른 글

[PL/SQL] 패키지 (Package)  (0) 2019.09.16
[PL/SQL] 커서 (CURSOR)  (0) 2019.09.16
[PL/SQL] Function, Procedure  (0) 2019.09.11
[Oracle] 조건 검색 - WHERE 절  (0) 2019.09.10
[Oracle] ERROR: ORA-00937  (0) 2019.09.10

PL/SQL (Procedural Language/Structed Query Language)

  • 데이터베이스 응용 프로그램을 작성하는데 사용하는 오라클의 SQL 전용 언어
  • 장점: 프로그램 개발의 모듈화 가능, 변수 선언 가능, 절차형 언어 사용, 에러처리, 이식성, 성능 향상 ...
  • PL/SGQ 프로그램 종류
    - Subprogram: Function (Return a Value), Procedure(Do action)
    - 자동실행 program: Trigger
    - 복합 프로그램: Package

Function

  • 반드시 하나의 값을 리턴해야하는 경우 사용
create or replace PROCEDURE p_tax
  (v_num in number,
   v_tax out number)
IS 
BEGIN 
  v_tax := v_num * 0.07;
END;

Procedure

  • 리턴값이 여러 개이거나 0개일 경우 
  • in / out / in out 으로 파라미터 타입과 전달 방식 명시
-- procedure: return 값 없음 대신 out으로 표기
create or replace PROCEDURE p_tax
  (v_num in number,
   v_tax out number)
IS 
BEGIN 
  v_tax := v_num * 0.07;
END;

Command로 Procedure, Function 실행하기

-- FUNCTION Command 실행하는 방법
VARIABLE a    NUMBER;
EXECUTE :a := TAX(100);
PRINT    a;

-- PROCEDURE Command 실행하는 방법
VARIABLE a    NUMBER;
EXECUTE p_tax(100, :a);
PRINT    a;

 

비교 연산자

  • = / != <> / > / >= / > / >=

논리 연산자

  • AND / OR / NOT 

SQL 연산자

  • BETWEEN a AND b : a와 b의 사이 값. a, b 포함
  • IN (a, b, c, ..., n) : a, b, c... n 중 하나와 일치하면 참
  • LIKE : 문자 패턴과 부분적으로 일치하면 참 - 문자패턴 %:길이에 상관없음, _: 한글자
  • IS NULL : NULL 이면 참
  • IS NOT NULL : NULL 이 아니면 참

집합 연산자

집합 연산의 대상이 되는 두 테이블의 컬럼수와 대응되는 컬럼끼리의 데이터 타입이 동일해야 함

  • UNION : 두 집합에 대해 중복되는 행을 제외한 합집합
  • UNION ALL : 두 집합에 대해 중복되는 행을 포함한 합집합
  • MINUS : 두 집합간의 차집합
  • INTERSECT : 두 집합간의 교집합 (공통인 부분)
-- 두 질의의 컬럼수가 같아야함. 두집합 연산의 대상, studno, name
-- UNION: 합집합, UNION ALL: 공통인 부분도 다 보여줘라
SELECT studno , name
FROM   stud_heavy
UNION  ALL
SELECT studno , name
FROM   stud_101;

-- INTERSECT: 교집합, 공통 요소만 get
SELECT studno , name
FROM STUD_HEAVY
INTERSECT
SELECT studno , name
FROM STUD_101;

-- MINUS: 차집합
SELECT studno , name
FROM STUD_HEAVY
MINUS
SELECT studno , name
FROM STUD_101;

 

정렬

  • ASC: 오름차순(기본), DESC: 내림차순
  • NULL일 경우 오름차순에서는 맨 마지막에 출력, 내림차순에서는 맨 처음 출력
  • 다중 컬럼으로 정렬 가능

 

그림으로 쉽게 이해하자!

 

 

 

 

 

 

Ref. 오라클 중심의 sql 배움터

ORA-00937: 단일 그룹의 그룹 함수가 아닙니다

00937. 00000 -  "not a single-group group function"



1. SELECT LIST에 그룹함수를 사용하는 경우, 그룹함수를 적용하지 않은 단순 컬럼은 올 수 없다.

-- error
SELECT MAX(sal), ename
FROM   emp;

-- correct
SELECT MAX(sal)
FROM   emp;

-- correct
SELECT MAX(sal), ename
FROM   emp
GROUP BY ename;

 

 

2. 그룹 함수가 중첩된 경우 GROUP BY절에 기술한 컬럼도 출력 할 수 없다.

-- error
SELECT deptno, MAX(AVG(weight)) max_weight
FROM   student
GROUP BY deptno;

-- correct 
SELECT MAX(AVG(weight)) max_weight
FROM   student
GROUP BY deptno;

 

SELECT AVG(weight) max_weight
FROM   student
GROUP BY deptno;

에 대한 결과는 

 

위와 같으므로 당연히 deptno를 가져올 수 없다!

 

 

 

 

 

 

Ref. 

https://wickedmagic.tistory.com/214 

https://keep-cool.tistory.com/37

서브쿼리(Sub Query) 란?

두 개 이상의 SQL 명령문을 하나의 SQL명령문으로 연결하여 처리하는 방법

 

서브 쿼리 종류

단일행 서브쿼리

- 단하나의 행만 검색하여 반환

- 단일행 비교 연산:  =, >, >=, <, <>, <=

 

다중행 서브쿼리

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

 

  • IN: 하나라도 일치하면 참
  • ANY, SOME: 하나이상 일치하면 참
  • ALL: 모든값이 일치하면 참, max와 같은 뜻
  • EXISTS: 하나라도 존재하면 참 -> 성능개선할 수 있음. 한행이라도 존재하면 나타나고, 아니면 안나타줌.
  • NOT EXISTS: EXISTS의 상반되는 연산자
-- IN
SELECT name, grade, deptno
FROM   student
WHERE  deptno IN ( SELECT deptno
                   FROM   department
                   WHERE  college = 100);
-- ANY            
SELECT studno, name, height, grade
FROM   student
WHERE  height > ANY (SELECT height 
                     FROM   student
                     WHERE  grade = '4');

-- ALL: max와 같은 의미,
SELECT studno, name, height, grade
FROM   student
WHERE  height > ALL (SELECT height 
                     FROM   student
                     WHERE  grade = '4');
-- EXISTS
SELECT profno, name, sal, comm, position
FROM   professor
WHERE  EXISTS (SELECT position
               FROM   professor
               WHERE  sal > 450);

 

PAIRWISE 다중 컬럼 SubQuery

SELECT name, grade, weight
FROM   student
WHERE  (grade, weight) IN (SELECT grade, MIN(weight)
                           FROM   student
                           GROUP BY grade);

 

UNPAIRWISE 다중 컬럼 SubQuery

칼럼을 쌍으로 묶어서 동시에 비교하는 방식

SELECT name, grade, weight
FROM   student
WHERE  grade IN (SELECT grade
                 FROM   student
                 GROUP BY grade)
AND   weight IN (SELECT MIN(weight)
                 FROM student
                 GROUP BY grade);

 

상호연관 SubQuery

칼럼별로 나누어서 비교한 후, AND 연산을 하는 방식

SELECT name, deptno, height
FROM   student s1
WHERE  height > ( SELECT AVG(height)
                  FROM   student s2
                  WHERE  s2.deptno = s1.deptno)
ORDER BY deptno;

 

+ Recent posts