분석함수란?

  • - 테이블에 있는 데이터를 특정 용도로 분석하여 결과를 반환하는 함수
  • - 복잡한 계산을 단순하게 처리해주는 함수
  • - 쿼리 결과Set을 대상으로 계산을 수행하는 함수
  • - SELECT 절에서 수행됨
    • FROM, WHERE, GROUP BY 절에서 사용 불가
    • ORDER BY 구문에서는 사용 가능
  • - 오라클 8.1.6 부터 분석함수 지원

집계함수 vs 분석함수

집계함수

집계함수는 여러행 또는 테이블 전체 행으로부터 그룹별로 집계하여 결과를 반환한다.

1
2
3
4
SELECT deptno
     , SUM(sal) s_sal
  FROM emp
 GROUP BY deptno;
  • [그림] 집계함수 실행결과
  • 집계함수 실행 결과

분석함수

분석 함수는 집계 결과를 각 행마다 보여준다.

1
2
3
4
5
SELECT deptno
     , empno
     , sal
     , SUM(sal) OVER(PARTITION BY deptno) s_sal
  FROM emp;
  • [그림] 분석함수 실행결과
  • 분석함수 실행 결과

집계함수 vs 분석함수
  • - 집계함수는 그룹별 최대, 최소, 합계, 평균, 건수 등을 구할 때 사용되며, 그룹별 1개의 행을 반환한다.
  • - 분석함수는 그룹단위로 값을 계산한다는 점에서 집계함수와 유사하지만, 그룹마다가 아니라 결과Set의 각 행마다 집계결과를 보여준다는 점에서 집계함수와 상당한 차이가 있다.
  • - 분석함수는 쉽게 생각해서, 그룹별 계산결과를 각 행마다 보여주는 것이다.

Syntax

1
2
3
4
5
6
SELECT ANALYTIC_FUNCTION ( arguments )
       OVER ( [ PARTITION BY 컬럼List ]
              [ ORDER BY 컬럼List ]
              [ WINDOWING 절 (Rows|Range Between)]
            )
  FROM 테이블 명;

  • - ANALYTIC_FUNCTION : 분석함수명(입력인자)
  • OVER : 분석함수임을 나타내는 키워드.
  • PARTITION BY : 계산 대상 그룹을 정한다.
  • ORDER BY : 대상 그룹에 대한 정렬을 수행한다.
  • - WINDOWING 절 : 분석함수의 계산 대상 범위를 지정한다.
    • ORDER BY 절에 종속적이다.
    • 기본 생략 구문 : 정렬된 결과의 처음부터 현재행까지 [RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]

분석함수의 종류

  • 순위함수 : RANK, DENSE_RANK, ROW_NUMBER, NTILE
  • 집계함수 : SUM, MIN, MAX, AVG, COUNT
  • - 기타함수 : LEAD, LAG, FIRST_VALUE, LAST_VALUE, RATIO_TO_REPORT
  • - 분석함수(?) : KEEP, LISTAGG

태그

문서에 대하여

  • - 강좌 URL : http://www.gurubee.net/lecture/2671
  • - 이 문서를 다른 블로그나 홈페이지에 게재하실 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
  • 구루비의 모든 강좌는 크리에이티브 커먼즈의 저작자표시-비영리-동일조건변경허락(BY-NC-SA) 라이선스에 따라 자유롭게 사용할 수 있습니다.


블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

A CONNECT BY LEVEL  여기서 A는 무시해도 될꺼 같음. 

WITH t ( sd, dt, cd, lv ) AS

(

    SELECT '2014-10-11',    '2014-10-12',    'BGO',    5    FROM DUAL UNION ALL

    SELECT '2014-10-12',    '2014-10-14',    'BGA',    1    FROM DUAL UNION ALL

    SELECT '2014-10-14',    '2014-10-15',    'BGB',    1    FROM DUAL UNION ALL

    SELECT '2014-10-18',    '2014-10-19',    'BGC',    1    FROM DUAL UNION ALL

    SELECT '2014-10-18',    '2014-10-19',    'BGD',    1    FROM DUAL UNION ALL

    SELECT '2014-10-18',    '2014-10-19',    'BGE',    1    FROM DUAL UNION ALL

    SELECT '2014-10-18',    '2014-10-19',    'BGF',    1    FROM DUAL

)

////이렇게 데  T라는 가상의 데이터 테이블 T를 만들어 넣고... 순서대로 보자... 

SELECT A.*,

       T.CD,

       T.lv

  FROM

       (SELECT TO_CHAR(TO_DATE(SD,'YYYY-MM-DD') + (LEVEL-1),'YYYY-MM-DD') SD

         FROM

              (SELECT MIN(SD) SD

                FROM T //2번 최소 날짜를 구함.

              ) A CONNECT BY LEVEL <=

              (SELECT TO_DATE(MAX(DT),'YYYY-MM-DD')+1 - TO_DATE(MIN(SD),'YYYY-MM-DD')

                FROM T                   //1번 DT의 후속날자최대값에서 SD의 최소날짜값을빼면 9일이 나옴.. 

              )

       ) A //3번 최소날짜로부터 LEVEL일수만큼 더해진 날을 구함 LEVEL은 1부터 시작하므로 9까지 총 9번 연산되어 11일부터 19일까지 구해짐.

   LEFT OUTER JOIN T

       ON A.SD BETWEEN T.SD AND T.DT

WHERE T.CD IS NOT NULL; //4번 레프트 아웃트 조인으로 인하여 위에 구하여진 날짜값을 기준으로 그날짜에 포함되어진 값을 구하는데 T.CD 가 널이 아닌경우에만 구함. 



추가로 CONNECT BY LEVEL 에 대해서 검색내용을 추가함. 

Oracle10g 부터 CONNECT BY 절에서 제공하는 CONNECT_BY_ROOT, SYS_CONNECT_BY_PATH, CONNECT_BY_ISLEAF 기능에 대해서 알아보자

CONNECT_BY_ROOT

계층구조 쿼리에서 LEVEL이 0인 최상위 로우의 정보를 얻어 올 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT LPAD(' ', 4*(LEVEL-1)) || ename ename, empno,
CONNECT_BY_ROOT  empno "Root empno", level
  FROM emp
 START WITH job='PRESIDENT'
CONNECT BY PRIOR empno=mgr;
 
ENAME                    EMPNO  Root empno     LEVEL
------------------     ------- -----------   -------
KING                      7839    7839           1
    JONES                 7566    7839           2
        SCOTT             7788    7839           3
            ADAMS         7876    7839           4
        FORD              7902    7839           3
            SMITH         7369    7839           4

CONNECT_BY_ISLEAF

계층구조 쿼리에서 로우의 최하위 레벨(Leaf) 여부를 반환한다. 최하이 레벨이면 1, 아니면 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SELECT LPAD(' ', 4*(LEVEL-1)) || ename ename, empno,
       CONNECT_BY_ISLEAF "leaf", level
  FROM emp
 START WITH job='PRESIDENT'
CONNECT BY NOCYCLE PRIOR empno=mgr;
 
 
ENAME                     EMPNO       leaf      LEVEL
-------------------- ---------- ---------- ----------
KING                       7839          0          1
    JONES                  7566          0          2
        SCOTT              7788          0          3
            ADAMS          7876          1          4
        FORD               7902          1          3
    BLAKE                  7698          0          2
        MARTIN             7654          1          3
        TURNER             7844          1          3
        JAMES              7900          1          3
    CLARK                  7782          0          2
        MILLER             7934          1          3

SYS_CONNECT_BY_PATH

계층구조 쿼리에서 현재 로우 까지의 PATH 정보를 쉽게 얻어 올 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- SQL*Plus에서만 깔끔하게 보기위해서
COL PATH FORMAT A40
 
 
-- SYS_CONNECT_BY_PATH 예제
SELECT LPAD(' ', 4*(LEVEL-1)) || ename ename, empno,
       SYS_CONNECT_BY_PATH(ename, '/') "PATH"
  FROM emp
 START WITH job='PRESIDENT'
CONNECT BY PRIOR empno=mgr;
 
 
ENAME                     EMPNO PATH
-------------------- ---------- ---------------------------
KING                       7839 /KING
    JONES                  7566 /KING/JONES
        SCOTT              7788 /KING/JONES/SCOTT
            ADAMS          7876 /KING/JONES/SCOTT/ADAMS
        FORD               7902 /KING/JONES/FORD
    BLAKE                  7698 /KING/BLAKE
        MARTIN             7654 /KING/BLAKE/MARTIN
        TURNER             7844 /KING/BLAKE/TURNER
        JAMES              7900 /KING/BLAKE/JAMES
    CLARK                  7782 /KING/CLARK
        MILLER             7934 /KING/CLARK/MILLER

아래와 같이 Leaf Node만 전체 PATH 정보가 나오도록 작성 할 수 있다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- SQL*Plus에서만 깔끔하게 보기위해서
COL PATH FORMAT A40
 
 
SELECT LEVEL, SUBSTR(SYS_CONNECT_BY_PATH(ename, ','), 2) path
  FROM emp
 WHERE CONNECT_BY_ISLEAF = 1
 START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr;
 
 
    LEVEL PATH
--------- -------------------------
        4 KING,JONES,SCOTT,ADAMS
        3 KING,JONES,FORD
        3 KING,BLAKE,MARTIN
        3 KING,BLAKE,TURNER
        3 KING,BLAKE,JAMES
        3 KING,CLARK,MILLER

ORDER SIBLINGS BY

계층구조 쿼리에서 상관관계를 유지하면서 정렬을 할 수 있게 해준다. 아래 예제를 가지고 ORDER SIBLINGS BY와 ORDER BY로 테스트 해보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
-- SQL*Plus에서만 깔끔하게 보기위해서
COL ename FORMAT A25
COL ename2 FORMAT A10
 
 
-- ORDER SIBLINGS BY 예
-- 정렬이 정상적으로 수행된 것을 확인 할 수 있다.
SELECT LPAD(' ', 4*(LEVEL-1)) || ename ename,
       ename ename2, empno, level
  FROM emp
 START WITH job='PRESIDENT'
CONNECT BY NOCYCLE PRIOR empno=mgr
 ORDER SIBLINGS BY ename2;
 
 
ENAME                ENAME2          EMPNO      LEVEL
-------------------- ---------- ---------- ----------
KING                 KING             7839          1
    BLAKE            BLAKE            7698          2
        JAMES        JAMES            7900          3
        MARTIN       MARTIN           7654          3
        TURNER       TURNER           7844          3
    CLARK            CLARK            7782          2
        MILLER       MILLER           7934          3
    JONES            JONES            7566          2
        FORD         FORD             7902          3
        SCOTT        SCOTT            7788          3
            ADAMS    ADAMS            7876          4
 
 
 
 
-- ORDER BY 예
-- 정렬이 이상하게 수행된 것을 확인 할 수 있다.
SELECT LPAD(' ', 4*(LEVEL-1)) || ename ename,
       ename ename2, empno, level
  FROM emp
 START WITH job='PRESIDENT'
CONNECT BY NOCYCLE PRIOR empno=mgr
 ORDER BY ename2;
 
 
ENAME                ENAME2          EMPNO      LEVEL
-------------------- ---------- ---------- ----------
            ADAMS    ADAMS            7876          4
    BLAKE            BLAKE            7698          2
    CLARK            CLARK            7782          2
        FORD         FORD             7902          3
        JAMES        JAMES            7900          3
    JONES            JONES            7566          2
KING                 KING             7839          1
        MARTIN       MARTIN           7654          3
        MILLER       MILLER           7934          3
        SCOTT        SCOTT            7788          3
        TURNER       TURNER           7844          3

태그

문서에 대하여

  • - 강좌 URL : http://www.gurubee.net/lecture/1903
  • - 이 문서를 다른 블로그나 홈페이지에 게재하실 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
  • 구루비의 모든 강좌는 크리에이티브 커먼즈의 저작자표시-비영리-동일조건변경허락(BY-NC-SA) 라이선스에 따라 자유롭게 사용할 수 있습니다.


블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

출처: http://zent.tistory.com/64

with EMP as (
SELECT 10 AS DEPTNO, 'CLARK  ' AS ENAME FROM DUAL
UNION
SELECT 10 AS DEPTNO, 'KING   ' AS ENAME FROM DUAL
UNION
SELECT 10 AS DEPTNO, 'MILLER ' AS ENAME FROM DUAL
UNION
SELECT 20 AS DEPTNO, 'SMITH  ' AS ENAME FROM DUAL
UNION
SELECT 20 AS DEPTNO, 'ADAMS  ' AS ENAME FROM DUAL
UNION
SELECT 20 AS DEPTNO, 'FORD   ' AS ENAME FROM DUAL
UNION
SELECT 20 AS DEPTNO, 'SCOTT  ' AS ENAME FROM DUAL
UNION
SELECT 20 AS DEPTNO, 'JONES  ' AS ENAME FROM DUAL
UNION
SELECT 30 AS DEPTNO, 'ALLEN  ' AS ENAME FROM DUAL
UNION
SELECT 30 AS DEPTNO, 'BLAKE  ' AS ENAME FROM DUAL
UNION
SELECT 30 AS DEPTNO, 'MARTIN ' AS ENAME FROM DUAL
UNION
SELECT 30 AS DEPTNO, 'JAMES  ' AS ENAME FROM DUAL
UNION
SELECT 30 AS DEPTNO, 'TURNER ' AS ENAME FROM DUAL
UNION
SELECT 30 AS DEPTNO, 'WARD   ' AS ENAME FROM DUAL

SELECT DEPTNO, LTRIM(SYS_CONNECT_BY_PATH(ENAME, ','),',') ENAME
FROM (
SELECT DEPTNO
, ENAME
, ROW_NUMBER() OVER (PARTITION BY DEPTNO ORDER BY DEPTNO) RNUM
, COUNT(*) OVER (PARTITION BY DEPTNO) CNT
FROM EMP
)
WHERE LEVEL = CNT
START WITH RNUM = 1
CONNECT BY PRIOR DEPTNO = DEPTNO 
AND PRIOR RNUM = RNUM - 1

결과

DEPTNO   ENAME 
--------    ----------------------
10             MILLER ,KING   ,CLARK   
20             JONES  ,FORD   ,ADAMS  ,SCOTT  ,SMITH   
30             ALLEN  ,BLAKE  ,JAMES  ,MARTIN ,TURNER ,WARD    

* 다른방법
select deptno, XMLAgg(XMLElement("nm", ename||',')).EXTRACT('//text()').GetStringVal() ename 
from emp
group by deptno

결과

DEPTNO ENAME 
--------  --------------------
10           CLARK  ,KING   ,MILLER , 
20           ADAMS  ,SMITH  ,SCOTT  ,FORD   ,JONES  , 
30           ALLEN  ,JAMES  ,TURNER ,WARD   ,MARTIN ,BLAKE  , 


* Decode 를 사용하는 방법 (컬럼갯수에 따라 달라진다 번거로움..)

SELECT DEPTNO,
            MAX(DECODE(RID, 1, ENAME)) EMP1,
            MAX(DECODE(RID, 2, ENAME)) EMP2,
            MAX(DECODE(RID, 3, ENAME)) EMP3,
            MAX(DECODE(RID, 4, ENAME)) EMP4,
            MAX(DECODE(RID, 5, ENAME)) EMP5,
            MAX(DECODE(RID, 6, ENAME)) EMP6
     FROM ( 
          SELECT DEPTNO, 
                 ROW_NUMBER() OVER (PARTITION BY DEPTNO ORDER BY ENAME) RID, 
                ENAME
          FROM EMP
          )
     GROUP BY DEPTNO

결과

DEPTNO EMP1   EMP2   EMP3      EMP4     EMP5    EMP6 
-------- ------   -----    -----      -----      -----     -----
10         CLARK   KING   MILLER   
20         ADAMS  FORD   JONES   SCOTT    SMITH 
30         ALLEN   BLAKE JAMES   MARTIN   TURNER WARD



블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

출처: http://blog.naver.com/whdahek/70179338216


데이터베이스 트리거

 

트리거란 테이블에 어떤 조작이 가해졌을 때에 미리 지정해 놓은 처리를 자동으로 실행시키는 블록을 말하며,

PL/SQL 블록으로 작성한다.

 

트리거는 테이블, 뷰에 대한 처리 내용, 실행 조건, 실행 시간 등을 설정하고 설정 조건에 따라 자동으로 실행된다.

 

특히 데이터가 변경되는 중요한 테이블에 트리거를 설정하였는데 문제가 발생할 경우 데이터 추적이 가능하다.

하지만 너무 많이 사용하면 성능이 저하되는 문제점도 발생한다.

 

트리거는 위의 설정된 이벤트가 발생하면 실행된다.

이벤트가 발생되는 경우는 다음과 같다.

DML 실행, DDL실행 과 데이터베이스 동작 실행 (ex - logon , logoff, startup, shutdown, severerror)

 

 DML 트리거

DML트리거란 INSERT, UPDATE, DELETE문에 의해 테이블의 내용이 변경될 때마다 자동으로 실행되는

PL/SQL 블록을 말한다.

 

<생성 구문>

CREATE [OR REPLACE] TRIGGER 트리거명 [BEFORE | AFTER]

triggering-event ON 테이블명

[FOR EACH ROW]

[WHERE 조건]

PL/SQL block ;

 

before|after = before는 트리거링 이벤트 발생 전에 PL/SQL block을 실행하고 after는 발생 후에 실행한다.

 

triggering-event = DML INSERT, UPDATE, DELETE를 기술한다. 지정된 컬럼()에 대해서만 이벤트가 발생하며 지정하지 않을 시 모든 컬럼에 적용된다.

 

for each row = 선택이며 각 행이 변할 때마다 실행되는 행 수준의 트리거일 때 지정한다.

 

PL/SQL block = 트리거에서 처리할 블록의 명령문을 기술한다.

 

 

위의 특징들에 따라 DML트리거의 유형은 3가지가 있다.

-------------------------------------------

1. 문장 수준과 행 수준의 트리거

2. DML문장

3. 타이밍

------------------------------------

1. 문장 수준과 행 수준의 트리거 =

문장 수준의 트리거는 한 문장의 실행에 의한 변경/삭제가 되는 행의 수와 관계없이 한번만 이벤트가 발생되고 실행된다.

for each row 구문을 포함하지 않는다.

triggering-event 마다 한번만 실행한다.

 

행 수준의 트리거는 한 문장의 실행에 의한 변경/삭제되는 행의 수만큼 이벤트가 발생되어 실행된다.

for each row 구문을 포함한다.

행이 추가되거나 수정되거나 삭제될 때마다 실행한다.

 

2.DML문장 =

INSERT는 행이 추가될 때 실행된다.

UPDATE는 변경될 때, DELETE는 삭제될 때 실행된다.

 

3. 타이밍 =

before는 테이블에 DML 문장이 실행되기 전에 트리거가 실행되고 after는 후에 실행된다.

 

따라서 테이블에 적용가능한 트리거의 유형은

triggering-event 3종류, for each row 유뮤에 의한 2종류, beforeafter에 대해 2종류

 3 * 2 * 2 종류로 12가지 유형이 된다.

 

DML트리거에서 컬럼 값을 참조하는 방법에 대해서 보자.

 

DML트리거의 PL/SQL 블록에서 테이블에 입력,수정,삭제될 때 테이블에 관련된 값을 참조할 수 있다.

이 값은 DML트리거가 실행될 때 :new , :old 두 종류의 의사 레코드(Pseude Record)를 통하여 DML트리거에 나타난다. 형식은 아래와 같다.

:new.컬럼명 :old.컬럼명

 

예를 들어,

INSERT문이 실행되면 추가할 행의 컬럼 값이 :new.컬럼명 으로 나타나고

UPDATE문이 실행되면 수정전 컬럼 값은 :old.컬럼명 으로 나타나고

수정할 컬럼 값은 :new.컬럼명 으로 나타난다.

DELETE문이 실행되면 컬럼 값은 :old.컬럼명 으로 나타난다.

 

<트리거 작성시 고려사항>

1. DML트리거에서 쩌장된 프로시저나 함수 등을 호출할 수 있다.

2. DML트리거에는 트랜잭션 제어문을 사용할 수 없다.

예외가 발생하거나, 변경된 데이터를 취소할 경우가 발생되었을 때 COMMIT문으로 저장된 값들은

ROLLBACK문으로 취소할 수 없기 때문이다.


 예제.. 

SQL> CREATE OR REPLACE TRIGGER triger_test
       BEFORE
       UPDATE ON dept
       FOR EACH ROW
	   
	   BEGIN
        DBMS_OUTPUT.PUT_LINE('변경 전 컬럼 값 : ' || : old.dname);
        DBMS_OUTPUT.PUT_LINE('변경 후 컬럼 값 : ' || : new.dname);
     END;
     /

-- DBMS_OUTPUT.PUT_LINE을 출력
SQL> SET SERVEROUTPUT ON ; 

-- UPDATE문을 실행시키면.. 
SQL> UPDATE dept SET dname = '총무부' WHERE deptno = 30

-- 트리거가 자동 실행되어 결과가 출력된다. 
변경 전 컬럼 값 : 인사과
변경 후 컬럼 값 : 총무부

1 행이 갱신되었습니다.
    

[출처] 오라클 - 트리거|작성자 정종모


블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

* Pseudo Column
    1) SYSDATE - 날짜
    2) ROWNUM - 행의 순서(가상의 숫자, 따라서 where 조건을 쓸 수 없음)
    3) ROWID - 모든 행에 대한 식별자의 역할


* 검색 - 오라클 명령어에서 대소문자를 가리지는 않지만, 검색시에는 구분함.
    1) select
        a) distinct - 중복행제거
        b) * - all
        c) alias - 해당 컬럼에 다른 이름 부여 : as키워드를 사용하면 명시적이고
                                                                         따옴표로 묶으면, 대소문자 구분 가능
        d) 컬럼값중 숫자형 데이터는 산술연산 가능
        e) || - 연결연산자
        f) 작은따옴표 - 컬럼에 있는 데이터는 아니지만, 따옴표사이의 내용이 모든 행에 표시된다. 연결연산자와
           함께 사용하는 경우가 일반적
    2) where - from 절 다음에 와야 한다.
        a) and - AND 연산일 경우에는 거짓이 앞에 있는 게 좋고, OR 연산자일 경우에는 참이 앞에 있는 것이 좋다
        b) or
        c) between ~ and ~ - 이상, 이하의 개념임. 미만, 보다일경우는 부등호 기호 사용
        d) in ( 'x', 'y', 'z' )
        e) is null / not - 
NOT이 들어간 SQL 연산자는 사용을 하지 않는 것이 좋다. (ex. NOT IN, IS NOT NULL)
            null값으로 들어간 데이터를 확인하는 용도로 쓰인다.
        f) like
            i) %
            ii) _
            iii) like 'aa\%%' escape '\'
    3) order by 칼럼이름 [asc|desc]

    4) group by - 테이블보다 작은 그룹으로 묶어서 값을 얻으려 할 때 사용
        a) ROLLUP
        b) CUBE
    5) having - group by 의 수행결과에 조건을 부여해서 값을 얻으려 할 때 사용
    6) grouping sets
        a) UNION - 합집합
        b) INTERSECT - 교집합
        c) MINUS - 차집합
        d) UNION ALL - 합집합+교집합
    7) subquery - select 문 안에 삽입된 select 문


* SQL함수
    1) 단일행함수
        a) 숫자
        b) 문자
        c) 날짜
        d) 형변환
        e) 기타
    2) 집합함수
    3) 분석함수
    4) 정규표현식


* 숫자함수
    1) MOD(m, n) - m을 n으로 나누었을 때의 나머지를 반환

    2) ROUND(m, n) - m을 소수점 n+1자리에서 반올림한 결과를 반환

    3) WIDTH_BUCKET(대상값, 최소값, 최대값, 버켓수)

    4) CEIL(n) - 올림한 후 정수를 반환

    5) FLOOR(n) - 내림한 후 정수를 반환

    6) ABS(n) - 절대값을 반환

    7) TRUNC(m, n) - m을 n자리까지 절삭. n은 생략가능하며, 기본값은 0.
        n이 양수이면, 소수자리를 절삭, n이 음수이면, 정수자리를 절삭(= 0으로 만든다)

    8) POWER(m, n) - m의 n승값을 반환

    9) SQRT(n) - n의 제곱근 값을 반환

    10) SIGN(n) - n이 양수인지, 음수인지, 0인지를 반환

    11) CHR(n) - 10진수 n의 아스키코드에 해당하는 문자를 반환


* 문자함수
    1) LOWER('문자열') - 문자열을 소문자로 반환

    2) UPPER('문자열') - 문자열을 대문자로 반환

    3) INITCAP('문자열') - 문자열의 첫문자는 대문자로, 나머지 문자는 소문자로 반환

    4) CONCAT('문자열1', '문자열2') - 두개의 문자열1과 2를 연결해서 반환. 매개변수는 2개만 가능.

    5) SUBSTR('문자열', 시작위치값, 시작위치부터뽑아낼 문자열길이)

    6) LENGTH('문자열') - 문자열의 길이를 숫자값으로 반환

    7) INSTR('문자열', '표적문자', m, n) - 문자열중에서 표적문자를 왼쪽부터 m번째, m번째부터 n번째
        의 위치를 숫자값을 반환

    8) LPAD('문자열', 전체문자길이, '나머지문자값') - 전체문자길이중 문자열 길이만큼을 제외한
        공간을 왼쪽부터 나머지 문자값으로 채우고, 문자열을 덧붙여서 반환

    9) RPAD('문자열', 전체문자길이, '나머지문자값') - LPAD와 동일한 개념인데, 다만, 나머지 문자값을
        오른쪽으로 채워서 반환

    10) REPLACE('문자열', 'str1', 'str2') - 문자열중에서 str1에 해당하는 문자를 str2의 문자로 바꿔서 반환

    11) ASCII('문자') - 문자의 아스키코드값을 반환

    12) TRIM('타겟문자' from '문자열') - 문자열중에서 타겟문자를 삭제. 단 문자열의 바깥쪽 문자만이 해당됨.


* 날짜함수
    1) MONTHS_BETWEEN(date1, date2) - 두 날짜사이의 월수를 계산해서 숫자로 반환
        정수부분은 월을, 소수부분은 일을 의미함.

    2) ADD_MONTHS(date1, n) - n이 양수이면, date1에 더해지고, n이 음수이면, date1에서 뺄 값이 됩니다.

    3) NEXT_DAY(date1, n) - date1의 날짜를 기준으로, 다음번 n에 해당하는 요일을 반환
        ex) NEXT_DAY(sysdate, 'FRIDAY') - 시스템현재날짜로부터 다가올 금요일 날짜를 반환,
              NEXT_DAY(sysdate, '금요일') - 시스템이 한글로 설정되어있을 경우, 한글사용도 가능
              NEXT_DAY(sysdate, 1) - 숫자1은 일요일, 숫자7은 토요일
        cf) 오라클 언어세팅에 의해, 어느 한쪽의 예약어가 거부되는 경우가 있다.
             확인방법 : select * from nls_session_parameters;
             변경방법 : ALTER SESSION SET NLS_DATE_LANGUAGE = 'AMERICAN';

    4) LAST_DAY(date1) - date1날짜가 속한 달의 마지막 날짜를 반환, 윤년,평년은 자동 계산

    5) ROUND(date1, 조건) - date1의 날짜를 월단위, 년단위로 반올림처리해서 반환하고, 조건이 없으면,
        가장 가까운 날짜로 반올림한다.
        ex) ROUND(sysdate, 'MONTH')
              ROUND(sysdate, 'YEAR')
              ROUND(sysdate)

    6) TRUNC(date1, 조건) - date1의 날짜를 가장 가까운 년도 또는 월로 절삭해서 반환. 조건이 없으면,
        가장 가까운 날짜로 절삭


* 변환함수
    1) 묵시적인 형변환
        a) varchar2, char -> number
        b) varchar2, char -> date
        c) number -> varchar2
        d) date -> varchar2
    2) 명시적인 형변환
        a) TO_CAHR
        b) TO_NUMBER
        c) TO_DATE


* 기타함수
    1) NVL(컬럼명, 원하는값) - 특정컬럼의 null값을 원하는 값으로 변환
    2) DECODE(조건, 값1, 처리1, 값2, 처리2, ...... , 디폴트값) - if 분기문과 같은 역할 - 오라클 함수
    3) CASE - DECODE함수와 동일한 처리를 한다. 다만 DECODE함수에서 지원하지않는
        범위비교가 가능하다. - 표준 SQL
    4) NULLIF(m, n) - m과 n이 같으면 null값을 반환하고, 다르면, m을 반환
    5) GREATEST - 나열된 값중 제일 큰 값을 반환
    6) LEAST - 나열된 값중 제일 작은 값을 반환


* 집합함수 - where 절 사용시 주의
    1) AVG - 여러 행으로부터 하나의 결과를 반환
        ex) AVG(컬럼명)
        ex) AVG(컬럼명) OVER(PARTITION BY 컬럼명)
    2) COUNT
    3) MAX
    4) MIN
    5) SUM
    6) RANK - 전체값을 대상으로 각 값의 순위를 반환
        ex) RANK(값) WITHIN GROUP(ORDER BY 컬럼명)
        ex) RANK() OVER (ORDER BY 컬럼명)


블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

출처:  http://jhbench.tistory.com/29

1. Ranking Family

(1) RANK() -  상위 순으로 등수를 부여하는 경우 정렬 결과를 기준으로 전체 순위를 출력

☞사용법

                                                                                                                                      

RANK() OVER(

[PRTITION BY < value expression1>] [,...]

ODER BY<value expression2> [collate clause] [ASC:DESC]

[NULLS FIRST:NULLS LAST])

                                                                                                                                         

OVER : 순위를 부여하기 위한 대상 집합의 정렬 기준과 분할 기준 정의

PARTITION BY : value expression1을 기준으로 분할, 생랼하면 전체 집합을 대상으로 순위부여

ODER BY : 각 분할내에서 데이터를 정렬하는 기준 칼럼 지정

NULLS FIRST|NULLS LAST : 정렬 결과에서 NULL값의 위치 지정

 
(2) DENSE_RANK() - RNAK함수의 변형 동일 순위를 무시한 연속 순위를 출력
  RNAK함수는 1등이 2건인 경우 다음순위를 3등으로 부여 하지만, DENSE_RANK 함수는 다음순위를 2등으로 부여한다. 

질의

 SELECT  id,score,
rank()over(ORDER  BY  score  ASC)as rank,
dense_rank() over(order by score asc)as dense_rank
From ksdb_score;

결과
ID             SCORE       RANK DENSE_RANK
--------- ---------- ---------- ----------
200040394         83          1          1
200020182         88          2          2
200231047         89          3          3
200020182         90          4          4
200020183         90          4          4
200020183         92          6          5
200172058         93          7          6
200040394         95          8          7


(3) CUME_DIST()- 그룹 값 내에서 어떤 값의 cumulative distribution(누적분포)을 계산

☞사용법
                                                                                                                                        

CUME_DIST(expr) 
                                                                                                                                      

 
(4) PERCENT_RANK()-


(5) NTILE() - 출력결과를 사용자가 지정한 그룹 수로 나누어 출력

 ☞사용법

                                                                                                                                        

NITLE(expr) OVER(

[PARTITION BY< value expression1>][,...]

ORDER BY <value espression2> [collate clause] [ASC : DESC]

[NULLS FIRST:NULLS LAST])

                                                                                                                                       

 

질의

 SELECT  Cid,bday,
NTILE(3) OVER(ORDER BY bday) class
FROM ksdb_customer_info;

결과

CID    BDAY          CLASS
------ -------- ----------
100004 60/05/02          1
100010 72/08/02          1
100011 74/09/21          1
100006 75/04/05          1
100001 75/07/01          2
100002 77/02/01          2
100007 80/01/04          2
100003 80/01/25          2

100009 81/01/30          3
100005 82/06/01          3
100008 85/04/04          3

11 개의 행이 선택되었습니다. 

(6) ROW_NUMBER() - 분할별로 정렬된 결과에 대해 순위를 부여하는 기능 분할은 전체 행을 특정 칼럼을 기준으로 분리하는 기능으로 GROUP BY 절에서 그룹화하는 방법과 같은 개념

☞사용법

                                                                                                                                      

 ROW_NUMBER() OVER(

[PARTITION BY< value expression1>][,...]

ORDER BY <value espression2> [collate clause] [ASC : DESC]

[NULLS FIRST:NULLS LAST])

                                                                                                                                        

질의

 SELECT  id,score,
RANK()OVER(ORDER  BY  score  ASC)as rank,
DENSE_RANK()OVER(order by score asc)as dense_rank,
ROW_NUMBER()OVER(order by score asc)as row_number
From ksdb_score;

결과
ID             SCORE       RANK DENSE_RANK ROW_NUMBER
--------- ---------- ---------- ---------- ----------
200040394         83          1          1          1
200020182         88          2          2          2
200231047         89          3          3          3
200020182         90          4          4          4
200020183         90          4          4          5
200020183         92          6          5          6
200172058         93          7          6          7
200040394         95          8          7          8

8 개의 행이 선택되었습니다. 

↑RANK나 DENSE_RANK에서는 점수가 같으면 순위가 같게4,4 나오지만  ROW_NUMBER에서는 순서대로 4,5 로 번호를 부여한다.

2. Aggregate Family  

(1) SUM(), AVG(), MAX(), MIN()  

☞사용법

                                                                                                                                        

AVG([DISTINCT|ALL] expr)

SUM([DISTINCT|ALL] expr) 
                                                                                                                                       

expr의 데이터 타입은 NUMBER 데이터 타입만 가능

 (2) COUNT() - 테이블에서 조건을 만족하는 행의 개수를 반환

     COUNT(*)는 NULL을 가진 행과 중복되는 행을 모두 포함하는 행의 수를 계산

     COUNT(expression)는 NULL을 가진 행을 제외한 행의 수를 계산하여 반환 

(3) STDDEV() VARIANCE() - 인수로 지정된 칼럼에 대해 조건을 만족하는 행을 대상으로 표준편차와 분산을 구하는 함수로 숫자 데이터 타입에만 사용할 수 있으며, NULL 은 계산에서 제외된다. 

(4)RATIO_TO_REPORT()

3. Lead/Lag Family

LEAD() LAG() - 동일한 테이블에 있는 다른 행의 값을 참조하기 위한 함수

LAG 분석함수는 현재 행을 기준으로 이전값을 참조

LEAD 분석함수는 현재 행을 기준으로 이후값을 참조

LEAD LAG 분석함수에서 지정하는 인수는  현재행을 기준으로 몇 번째 행을 참조할 것인지를 지정

음수는 사용할 수 없다.

 ☞사용법

                                                                                                                                        

ROW_NUMBER() OVER(

[PARTITION BY< value expression1>][,...]

ORDER BY <value espression2> [collate clause] [ASC : DESC]

[NULLS FIRST:NULLS LAST])

                                                                                                                                       

 

질의

 SELECT  id,score,
LEAD(score, 1)OVER(ORDER  BY  score)as next_score,
LAG(score,1)OVER(order by score)as prev_score
From ksdb_score;

결과

ID             SCORE NEXT_SCORE PREV_SCORE
--------- ---------- ---------- ----------
200040394         83         88
200020182         88         89         83                 ←88 이후 점수 : 89 
200231047         89         90         88                    88 이전 점수 : 83
200020182         90         90         89
200020183         90         92         90
200020183         92         93         90
200172058         93         95         92
200040394         95                     93

8 개의 행이 선택되었습니다.  

'Oracle' 카테고리의 다른 글

오라클 트리거 트리거란 무엇인가?  (0) 2014.12.02
오라클 기본 문접 ㅎㅎ  (0) 2014.12.02
SYNONYM 오라클 ORACLE  (1) 2014.12.02
오라클 오브젝트 LOB에 대해서  (0) 2014.12.02
탑엔분석? TOP N QUERY 탑엔쿼리  (0) 2014.12.02
블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

SYNONYM 오라클 ORACLE

Oracle 2014. 12. 2. 14:46

- SYNONYM은 오라클 객체(테이블, 뷰, 시퀀스, 프로시저)에 대한 대체이름(Alias)를 말합니다.

 

- Synonym은 실질적으로 그 자체가 Object가 아니라 Object에 대한 직접적인 참조 입니다.

 

- Synonym을 사용하는 이유는...

1) 데이터베이스의 투명성을 제공하기 위해서 사용 한다고 생각하면 됩니다.

    Synonym은 다른 유저의 객체를 참조할 때 많이 사용을 합니다.

 

2) 만약에 실무에서 다른 유저의 객체를 참조할 경우가 있을때 Synonym을 생성해서 사용을 하면은 추후에 참조하고 있는 오브젝트가 이름을 바꾸거나 이동할 경우 객체를 사용하는 SQL문을 모두 다시 고치는 것이 아니라 Synonym만 다시 정의하면 되기 때문에 매우 편리 합니다.

 

3) 객체의 긴 이름을 사용하기 편한 짧은 이름으로 해서 SQL코딩을 단순화 시킬 수 있습니다.

 

4) 또한 객체를 참조하는 사용자의 오브젝트를 감출 수 있기 때문에 이에 대한 보안을 유지할 수 있습니다.Synonym을 사용하는 유저는 참조하고 있는 객체에 대한 사용자의 오브젝트의 소유자, 이름, 서버이름을 모르고 Synonym 이름만 알아도 사용할 수 있습니다.

 

                                                                                                                                      

** Synonyms를 사용하는 경우 **

 

오브젝트의 실제 이름과 소유자 그리고 위치를 감춤으로써 database 보안을 개선하는데 사용 됩니다.

- object에의 Public Access를 제공 합니다.

Remote Database의 Table, View, Program Init를 위해 투명성을 제공 합니다.

Database 사용자를 위해 SQL문을 단순화 할 수 있습니다.

 

Synonym에는 두가지 종류가 있습니다.

1) Private Synonym

 - 전용 Synonym은 특정 사용자만 이용할 수 있습니다.

 

2) Public Synonym

 - 공용 Synonym은 고용 사용자 그룹이며 소유하며 그 Database에 있는 모든 사용자가 공유

 

                                                                                                                                      

** Synonym 생성 문법 **

**[ Syntax ]*************************************************

CREATE [PUBLIC] SYNONYM synonym_name

FOR object_name

*************************************************************

 

- Public : 모든 사용자가 접근 가능한 시노님을 생성 합니다.

              Public Synonym의 생성 및 삭제는 DBA만이 할 수 있습니다.

 

                                                                                                                                      

※ scott USER의 emp테이블을 test USER가 사용하는 예제

 

1. 먼저 scott/tiger USER로 접속해서 test USER에게 emp테이블을 조작할 권한을 부여합니다.

 

SQL> GRANT ALL ON emp TO test;

         권한이 부여되었습니다.

 

test user에 대하여 scott의 emp테이블을 조작할 수 있는 권한을 부여 합니다.

권한이 있어야 select하거나 update, insert할 수 있습니다.

 

2. test USER로 접속해 동의어를 생성합니다.

 

SQL> connect test/test

 

SQL> CREATE SYNONYM scott_emp FOR scott.emp;

시노님이 생성되었습니다.

 

scott USER가 소유하고 있는 emp테이블에 대해 scott_emp라는 일반시노님을 생성 했습니다.

scott 사용자의 emp테이블을 test사용자가 scott_emp라는 동의어로 사용 합니다.

 

-- 시노님을 이용한 쿼리

SQL> SELECT empno, ename FROM scott_emp;

 

-- 일반 테이블을 쿼리

SQL> SELECT empno, ename FROM scott.emp;

이 두쿼리의 결과는 같습니다.

 

   EMPNO ENAME
-------- ---------
    7369 SMITH
    7499 ALLEN
    7521 WARD
    7566 JONES
    7654 MARTIN
    7698 BLAKE
          15 개의 행이 선택되었습니다. 

                                                                                                                                      

** 동의어 삭제 **

 

SQL> DROP SYNONYM scott_emp;

시노님이 삭제 되었습니다.

 

SQL> SELECT empno, ename FROM scott_emp;

라인 1에 오류:

ORA - 00942 : 테이블 또는 뷰가 존재하지 않습니다.

 

                                                                                                                                      

 

 

 

  ================================================ 
    * Oracle Community OracleClub.com 
    * http://www.oracleclub.com 
    * http://www.oramaster.net 
    * 운영자 : 김정식 (oramaster _at_ empal.com)
  ================================================
※ 강좌를 다른 홈페이지에 기재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^


'Oracle' 카테고리의 다른 글

오라클 기본 문접 ㅎㅎ  (0) 2014.12.02
RANK DENSE_RANK CUME_DIST PERCENT_RANK NTILE  (0) 2014.12.02
오라클 오브젝트 LOB에 대해서  (0) 2014.12.02
탑엔분석? TOP N QUERY 탑엔쿼리  (0) 2014.12.02
오라클 PL/SQL 후.  (0) 2014.11.14
블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

출처: http://belselios.tistory.com/entry/%EC%98%A4%EB%9D%BC%ED%81%B4PLSQL-%EB%8C%80%EC%9A%A9%EB%9F%89-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%ED%83%80%EC%9E%85-CLOB-BLOB


1. LOB

- LOB은 TEXT, 그래픽, 이미지, 비디오, 사운드 등 구조화되지 않은 대형 데이터를 저장하는데 사용한다.
- 일반적으로 테이블에 저장되는 구조화된 데이터들은 크기가 작지만, 멀티미디어 데이터는 크기가 크다.
- 크기가 큰 데이터는 DB에 저장하기 힘들기 때문에 OS상 존재하는 파일을 데이터베이스가 접근하게 된다.
- LONG, LONG RAW 데이터 유형은 예전에 사용던 것이고, 현재는 대부분 LOB 데이터 유형을 사용한다. 
- TO_LOB 함수를 이용하여 LONG 및 LONG RAW 를 LOB 으로 변경할 수 있다.

종류
  - CLOB : 문자 대형 객체 (Character). Oracle Server는 CLOB과 VARCHAR2 사이에 암시적 변환을 수행한다.
 - BLOB : 이진 대형 객체 (Binary). 이미지, 동영상, MP3 등... 
 - NCLOB : 내셔널 문자 대형 객체 (National). 오라클에서 정의되는 National Character Set을 따르는 문자
 - BFILE : OS에 저장되는 이진 파일의 이름과 위치를 저장. 읽기 전용 모드로만 액세스 가능.

데이터베이스 내부, 외부에 따라

   - 내부 : BLOB, CLOB, NCLOB - Table에 LOB 형식의 컬럼을 생성하고 이곳에 데이터의 실제위치를 가리키는 Locator(위치자) 저장.
  - 외부 : BFILE

특징
- 하나의 테이블에 여러 개의 LOB 열(column) 가능
- 최고 4GB까지 저장
- SELECT로 위치자 반환
- 순서대로 또는 순서없이 데이터 저장
- 임의적 데이터 액세스

LOB 구성
- LOB 값 : 저장될 실제 객체를 구성하는 데이터
- LOB 위치자 : 데이터베이스에 저장된 LOB값의 위치에 대한 포인터
- LOB열에는 데이터가 없고 LOB 위치자만 들어있다.

2. 내부 LOB

- LOB값은 데이터베이스에 저장된다.
- 사용자 정의 유형 속성, 테이블의 열, 바인드 변수, 호스트 변수, PL/SQL변수, 파라미터 또는 결과
- 동시성 방식, 리두 로그 및 복구 방식, 커밋 또는 롤백 트랜잭션 사용 가능
- BLOB 데이터 유형은 Oracle Server 내에서 비트 스트림으로 해석된다.
- CLOB 데이터 유형은 단일 바이트 문자 스트림으로 해석된다.
- NCLOB 데이터 유형은 데이터베이스 national character set 의 바이트 길이에 따라 멀티바이트 문자 스트림으로 해석된다.

내부 LOB 관리
- LOB 데이터 유형을 포함하는 테이블을 생성한 후 이를 채운다.
- 프로그램에서 LOB 위치자를 선언하고 초기화한다.
- SELECT FOR UPDATE를 사용하여 LOB이 포함된 행을 잠그고 해당 행 값을 LOB 위치자에 넣는다.
- LOB 위치자를 LOB값에 대한 참조로 사용하여 DBMS_LOB 패키지 프로시저, OCI호출, OLE용 오라클 객체, 오라클 선행 컴파일러 또는 JDBC를 통해 LOB을 조작한다.
- SQL을 통해서도 LOB 관리 가능(일부)
- COMMIT을 통해 변경사항 적용.

3. CLOB

lob_test1.sql

CREATE TABLE CAR (
        id      NUMBER(6) PRIMARY KEY NOT NULL,
        name    VARCHAR2(20),
        price   NUMBER(10),
        review  CLOB)
/

 

INSERT INTO CAR VALUES (100, 'SM7', 25000000, '삼성에서 나온 SM7을 타보니 정말 편안했다.')
/
INSERT INTO CAR VALUES (200, 'PORTER', 13000000, '역시 1톤트럭의 대표자!!')
/
INSERT INTO CAR VALUES (300, 'VERNA', 9000000, '싸고 좋은 거 같아용~^^*')
/
INSERT INTO CAR VALUES (400, 'SANTAFE', 33000000, EMPTY_CLOB())
/

- 테이블 생성시 CLOB 타입 컬럼을 선언하고, 데이터 입력.
- 따로 SIZE는 지정하지 않는다.
- EMPTY_CLOB() 함수는 위치자(Locator)를 아무 것도 없는 데이터로 초기화시킨다.

'Oracle' 카테고리의 다른 글

RANK DENSE_RANK CUME_DIST PERCENT_RANK NTILE  (0) 2014.12.02
SYNONYM 오라클 ORACLE  (1) 2014.12.02
탑엔분석? TOP N QUERY 탑엔쿼리  (0) 2014.12.02
오라클 PL/SQL 후.  (0) 2014.11.14
오라클 동의어  (0) 2014.11.13
블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

출처:===

http://www.hanbit.co.kr/network/view.html?bi_id=537


Top N Query는 데이터베이스 상에 있는 자료 중 최상위 몇 개의 자료를 보기 위해 사용하는 자료이다. 예를 들어 학교에서 중간고사를 보았고, 이때 총점이 높은 순서대로 10명의 학생을 보기 위해 사용하는 쿼리가 바로 Top N Query의 예가 되겠다. 


------------------------------------------------------------------------------

중복된 row를 제거하라  - 몸풀기

ID        NUM
---------- ----------
         1          1
         2          2
         3          3
         4          4
         5          1
         6          1

위의 데이터를 가지고 아래의 쿼리를 날리면 이런 결과가 나온다. 

SQL> r
  1  select * from temp t1, temp t2
  2* where t1.rowid > t2.rowid and t1.num = t2.num

        ID        NUM         ID        NUM
---------- ---------- ---------- ----------
         5          1          1          1
         6          1          1          1
         6          1          5          1

SQL>

정확하게 중복된 데이터는 이런쿼리

select * from temp t1
where t1.rowid > (select min(rowid) from temp where t1.num = num)
/


        ID        NUM
---------- ----------
         5          1
         6          1

ㅎㅎ 그럼 삭제해볼까?

 1  delete from temp
  2  where id in
  3  (
  4      select id from temp t1
  5      where t1.rowid >
  6      (
  7            select min(rowid) from temp
  8            where t1.num = num
  9      )
 10* )
 11  /

2 행이 삭제되었습니다.

SQL> select * from temp;

        ID        NUM
---------- ----------
         1          1
         2          2
         3          3
         4          4

SQL>

실전! TOP N Query! 

N 번째 까지 구하기 

자 그러면 TOP N을 구해야 할 대상인 샘플을 보도록 하겠다. 언제나 유명한 우리의 EMP테이블이 대상이 될 것이다.

SQL> SELECT * FROM EMP;

     EMPNO ENAME                JOB                       MGR HIREDATE        SAL       COMM     DEPTNO
---------- -------------------- ------------------ ---------- -------- ---------- ---------- -------
      7369 SMITH                CLERK                    7902 80/12/17        800                    20
      7499 ALLEN                SALESMAN                 7698 81/02/20       1600        300         30
      7521 WARD                 SALESMAN                 7698 81/02/22       1250        500         30
      7566 JONES                MANAGER                  7839 81/04/02       2975                    20
      7654 MARTIN               SALESMAN                 7698 81/09/28       1250       1400         30
      7698 BLAKE                MANAGER                  7839 81/05/01       2850                    30
      7782 CLARK                MANAGER                  7839 81/06/09       2450                    10
      7788 SCOTT                ANALYST                  7566 87/04/19       3000                    20
      7839 KING                 PRESIDENT                     81/11/17       5000                    10
      7844 TURNER               SALESMAN                 7698 81/09/08       1500          0         30
      7876 ADAMS                CLERK                    7788 87/05/23       1100                    20
      7900 JAMES                CLERK                    7698 81/12/03        950                    30
      7902 FORD                 ANALYST                  7566 81/12/03       3000                    20
      7934 MILLER               CLERK                    7782 82/01/23       1300                    10

14 개의 행이 선택되었습니다.

SQL>

멋지다! 자 이제 문제는 SAL이 가장 높은 순으로 10명을 뽑으라는 것이다. 일단 간단히 ORDER BY로 SAL을 정렬하면 될 것이다.

SQL> SELECT * FROM EMP
  2  ORDER BY SAL DESC;

     EMPNO ENAME                JOB                       MGR HIREDATE        SAL       COMM     DEPTNO
---------- -------------------- ------------------ ---------- -------- ---------- ---------- -------
      7839 KING                 PRESIDENT                     81/11/17       5000                    10
      7788 SCOTT                ANALYST                  7566 87/04/19       3000                    20
      7902 FORD                 ANALYST                  7566 81/12/03       3000                    20
      7566 JONES                MANAGER                  7839 81/04/02       2975                    20
      7698 BLAKE                MANAGER                  7839 81/05/01       2850                    30
      7782 CLARK                MANAGER                  7839 81/06/09       2450                    10
      7499 ALLEN                SALESMAN                 7698 81/02/20       1600        300         30
      7844 TURNER               SALESMAN                 7698 81/09/08       1500          0         30
      7934 MILLER               CLERK                    7782 82/01/23       1300                    10
      7521 WARD                 SALESMAN                 7698 81/02/22       1250        500         30
      7654 MARTIN               SALESMAN                 7698 81/09/28       1250       1400         30
      7876 ADAMS                CLERK                    7788 87/05/23       1100                    20
      7900 JAMES                CLERK                    7698 81/12/03        950                    30
      7369 SMITH                CLERK                    7902 80/12/17        800                    20

14 개의 행이 선택되었습니다.

SQL>

자 이 쿼리를 한 번 더 둘러싸주면 간단히 10명을 뽑을 수 있다.

SQL> R
  1  SELECT E.* FROM
  2  (
  3      SELECT * FROM EMP
  4      ORDER BY SAL DESC
  5  ) E
  6* WHERE ROWNUM <= 10

     EMPNO ENAME                JOB                       MGR HIREDATE        SAL       COMM     DEPTNO
---------- -------------------- ------------------ ---------- -------- ---------- ---------- -------
      7839 KING                 PRESIDENT                     81/11/17       5000                    10
      7788 SCOTT                ANALYST                  7566 87/04/19       3000                    20
      7902 FORD                 ANALYST                  7566 81/12/03       3000                    20
      7566 JONES                MANAGER                  7839 81/04/02       2975                    20
      7698 BLAKE                MANAGER                  7839 81/05/01       2850                    30
      7782 CLARK                MANAGER                  7839 81/06/09       2450                    10
      7499 ALLEN                SALESMAN                 7698 81/02/20       1600        300         30
      7844 TURNER               SALESMAN                 7698 81/09/08       1500          0         30
      7934 MILLER               CLERK                    7782 82/01/23       1300                    10
      7521 WARD                 SALESMAN                 7698 81/02/22       1250        500         30

10 개의 행이 선택되었습니다.

SQL>

쿼리가 약간 이상해보이는가? E.* 부분이 그럴지도 모르겠다. 저러한 표현이 가능하다는 것 정도는 알아두자. 고백할 것이 있다. 이 쿼리는 오라클 8i 이상에서만 작동하며 오라클 7이나 오라클 8에서는 동작하지 않는다. 왜냐하면 이들 오라클 7이나 8버전에서는 서브쿼리내에 order by를 사용할 수 없기 때문이다. 이러한 경우에는 약간의 편법을 사용해야 한다.

SQL> GET B
  1  SELECT EMPNO, ENAME, ABS(SAL) FROM
  2  (
  3      SELECT E.EMPNO, -E.SAL SAL, MAX(E.ENAME) ENAME FROM EMP E
  4      GROUP BY -E.SAL, E.EMPNO
  5  )
  6* WHERE ROWNUM <= 10
SQL> /

     EMPNO ENAME                  ABS(SAL)
---------- -------------------- ----------
      7839 KING                       5000
      7788 SCOTT                      3000
      7902 FORD                       3000
      7566 JONES                      2975
      7698 BLAKE                      2850
      7782 CLARK                      2450
      7499 ALLEN                      1600
      7844 TURNER                     1500
      7934 MILLER                     1300
      7521 WARD                       1250

10 개의 행이 선택되었습니다.

SQL>

이번엔 쿼리가 약간 복잡하다. 세세히 뜯어보도록 하자.

3      SELECT E.EMPNO, -E.SAL SAL, MAX(E.ENAME) ENAME FROM EMP E
4      GROUP BY -E.SAL, E.EMPNO

group by를 사용해 e.sal으로 묶어준다. 그러나 이렇게만 묶어버리면 sal이 중복되는 row들이 사라져버린다. 따라서 여기서는 EMPNO(EMP테이블의 primary key이다)까지 묶어서 group by를 해주었다. group by 는 desc구문이 존재하지 않으므로 sal이 큰 순으로 나오려면 -E.SAL 형태로 group by를 해주어야 한다. 그리고 group by에 나타나지 않은 컬럼들인 E.ENAME과 은 단순히 MAX()함수를 취해주었다. 이것은 항상 그룹이 단일 row단위로 들어오므로 언제나 단순히 E.ENAME을 돌려준다.

1  SELECT EMPNO, ENAME, ABS(SAL) FROM
2  (
3      SELECT E.EMPNO, -E.SAL SAL, MAX(E.ENAME) ENAME FROM EMP E
4      GROUP BY -E.SAL, E.EMPNO
5  )
6* WHERE ROWNUM <= 10

이제 전체적인 데이터가 도달한 상태에서 10개의 row를 뽑아주기만 하면 된다. 단, SAL 값은 -에 의해 음수로 변해서 왔으므로 이는 다시 ABS함수를 쓰던가 다시 -를 해주어 원래 양수값으로 바꿔줘야 한다. 여기서 제시한 오라클 7,8에서의 서브쿼리에서의 소팅 방법은 group by를 사용해 서브쿼리에서 소트를 할 수 있다는 것과, 소트할 컬럼을 group by의 제일 앞에 명시하고, 다음에 중복된 row가 사라지는 것을 막기위해 pk를 group by에 명시하며 마지막으로, group by 에 명시되지 않았으면서 select 절에 나타난 컬럼들에 max()를 취해준다는 것이다. 그러나 다음과 같이 해볼 수도 있겠다.

SQL> get b
  1  SELECT EMPNO, ENAME, ABS(SAL) FROM
  2  (
  3      SELECT E.EMPNO, -E.SAL SAL, E.ENAME ENAME FROM EMP E
  4      GROUP BY -E.SAL, E.EMPNO, E.ENAME
  5  )
  6* WHERE ROWNUM <= 10
SQL> /

     EMPNO ENAME                  ABS(SAL)
---------- -------------------- ----------
      7839 KING                       5000
      7788 SCOTT                      3000
      7902 FORD                       3000
      7566 JONES                      2975
      7698 BLAKE                      2850
      7782 CLARK                      2450
      7499 ALLEN                      1600
      7844 TURNER                     1500
      7934 MILLER                     1300
      7521 WARD                       1250

10 개의 행이 선택되었습니다.

SQL>

여기서는 모든 컬럼을 group by에 명시하되 sal컬럼만 앞에 가게 해준다는 것이다. 어느 방법이든 맘에 드는 것을 골라 쓰면 되겠다. 

N번째까지가 아니라 N등까지를 구하라 

앞에서의 TOP N 구하는 방식은 SAL순서대로 소팅한 뒤에 제일 먼저 나오는 10개의 row를 뽑은 것이다. 이번에는 N등까지를 구해보도록 하자. 자, 일단 독자 스스로 구현해보도록 하자. 못하면 못하는 대로 한시간은 고민하기 바란다.

  1  SELECT EMPNO, ENAME, SAL FROM EMP E1
  2  WHERE (
  3            SELECT COUNT(1) FROM EMP E2
  4            WHERE E1.SAL < E2.SAL
  5        ) <= 10
  6* ORDER BY SAL DESC
SQL> /

     EMPNO ENAME                       SAL
---------- -------------------- ----------
      7839 KING                       5000
      7788 SCOTT                      3000
      7902 FORD                       3000
      7566 JONES                      2975
      7698 BLAKE                      2850
      7782 CLARK                      2450
      7499 ALLEN                      1600
      7844 TURNER                     1500
      7934 MILLER                     1300
      7521 WARD                       1250
      7654 MARTIN                     1250

11 개의 행이 선택되었습니다.

SQL>

ORDER BY SAL DESC가 존재하지만 이 ORDER BY 문장은 10개의 데이터에 대해서만 SORT를 하므로 SORT비용이 매우 낮음에 주목하라. 그러나 이에 비해 앞서 했던 10번째까지의 사람 구하기는 GROUP BY나 ORDER BY에 의해서 사용되었으므로 SORT비용이 매우 컸다. 

부서별 N등까지를 구하라 

EMP테이블과 자주 같이 사용되는 테이블은 DEPT테이블로서 EMP에 나타난 DEPTNO와 조인하면 부서명을 얻어올 수 있다.

SQL> SELECT EMPNO, ENAME, SAL, DNAME
  2  FROM EMP E, DEPT D
  3  WHERE E.DEPTNO = D.DEPTNO
  4  /

     EMPNO ENAME                       SAL DNAME
---------- -------------------- ---------- ----------------------------
      7369 SMITH                       800 RESEARCH
      7499 ALLEN                      1600 SALES
      7521 WARD                       1250 SALES
      7566 JONES                      2975 RESEARCH
      7654 MARTIN                     1250 SALES
      7698 BLAKE                      2850 SALES
      7782 CLARK                      2450 ACCOUNTING
      7788 SCOTT                      3000 RESEARCH
      7839 KING                       5000 ACCOUNTING
      7844 TURNER                     1500 SALES
      7876 ADAMS                      1100 RESEARCH
      7900 JAMES                       950 SALES
      7902 FORD                       3000 RESEARCH
      7934 MILLER                     1300 ACCOUNTING

14 개의 행이 선택되었습니다.

SQL>

우리가 하고자 하는 것은 각 부서별로 2등까지(두 번째 까지가 아니다)의 사람을 구하는 것이다. 원하는 데이터 형태는 아래와 같이 나타난다.

DNAME        RANK       EMPNO    ENAME
부서A         1         XXXX     홍길동
              2         YYYY     김길동
부서B         1         NNNN     박길동   <- 부서 B에는 한명의 사원만 존재하는 경우
부서C         1         XXXX     서길동
              2         ZZZZ     황길동

이것은 쉽지 않을 것이다. 머리 싸매고 연구해보자.

  1  SELECT * FROM
  2  (
  3      SELECT DNAME, EMPNO, ENAME, SAL, RANK() OVER ( PARTITION BY E.DEPTNO ORDER BY SAL DESC ) RNK
  4      FROM DEPT D, EMP E
  5      WHERE D.DEPTNO = E.DEPTNO
  6  )
  7* WHERE RNK <= 2
SQL> /

DNAME                             EMPNO ENAME                       SAL        RNK
---------------------------- ---------- -------------------- ---------- ----------
ACCOUNTING                         7839 KING                       5000          1
                                   7782 CLARK                      2450          2
RESEARCH                           7788 SCOTT                      3000          1
                                   7902 FORD                       3000          1
SALES                              7698 BLAKE                      2850          1
                                   7499 ALLEN                      1600          2

6 개의 행이 선택되었습니다.

SQL>

답을 보고 의아하게 생각하는 사람들이 있을지 모르겠다. 그러나 사실 ANSI SQL가지고만 풀기에 이 문제는 난이도가 높다. 위에 등장한 SQL코드에는 8i부터 등장한 analytic function이 사용되었다. 이들을 사용해 이 문제와 같은 매우 복잡한 SQL문장을 단 한번에 한 큐에 날려보낼 수 있으며, 더군다나 이들은 OLAP의 다차원 쿼리에 활용될 수 있는 강력한 무기이다. 

OLAP를 위한 글도 analytic function을 위한 글도 아니므로 간단히만 짚어보도록 하자.

3      SELECT DNAME, EMPNO, ENAME, SAL, RANK() OVER ( PARTITION BY E.DEPTNO ORDER BY SAL DESC ) RNK
4      FROM DEPT D, EMP E
5      WHERE D.DEPTNO = E.DEPTNO

바로 이 부분이 핵심인데 그 중에서도 핵심은 RANK() OVER ( PARTITION BY E.DEPTNO ORDER BY SAL )의 의미를 파악하면 이 쿼리를 완전히 아는 것이다. 먼저 RANK() 함수는 순위를 구하는 함수이며 PARTITION 은 부분을 나누는 구문이다. 그리고 ORDER BY는 각 PARTITION(부분) 내에서의 정렬을 위한 컬럼을 명시하는데 쓰인다. 즉, 위의 코드는 E.DEPTNO 를 기준으로 파티션들을 분할하고 그들 각각을 SAL에 의해서 역순으로 정렬한 순위를 얻어오라는 것이다. 이 코드만 실행하면 아래와 같은 결과를 얻는다.

SQL> GET B
  1  SELECT DNAME, EMPNO, ENAME, SAL, RANK() OVER ( PARTITION BY E.DEPTNO ORDER BY SAL DESC ) RNK
  2  FROM DEPT D, EMP E
  3* WHERE D.DEPTNO = E.DEPTNO
SQL> /

DNAME                             EMPNO ENAME                       SAL        RNK
---------------------------- ---------- -------------------- ---------- ----------
ACCOUNTING                         7839 KING                       5000          1
                                   7782 CLARK                      2450          2
                                   7934 MILLER                     1300          3
RESEARCH                           7788 SCOTT                      3000          1
                                   7902 FORD                       3000          1
                                   7566 JONES                      2975          3
                                   7876 ADAMS                      1100          4
                                   7369 SMITH                       800          5
SALES                              7698 BLAKE                      2850          1
                                   7499 ALLEN                      1600          2
                                   7844 TURNER                     1500          3
                                   7521 WARD                       1250          4
                                   7654 MARTIN                     1250          4
                                   7900 JAMES                       950          6

14 개의 행이 선택되었습니다.

SQL>

보다시피 RANK()가 각 파티션 내의 순위를 구해주고 있다.

  1  SELECT * FROM
  2  (
  3      SELECT DNAME, EMPNO, ENAME, SAL, RANK() OVER ( PARTITION BY E.DEPTNO ORDER BY SAL DESC ) RNK
  4      FROM DEPT D, EMP E
  5      WHERE D.DEPTNO = E.DEPTNO
  6  )
  7* WHERE RNK <= 2

그리고 위에서처럼 SELECT문으로 한번 더 묶고 RNK로 FILTERING을 해주어 원하는 결과를 획득한다. 

실제로 analytic function은 매우 강력한 오라클의 분석 기능이지만, ANSI SQL에 위배되고 또한 RDBMS의 근본적인 관계형 맵핑이라는 기본원칙을 무시한다는 비판도 있는 것은 사실이지만, 이러한 쿼리를 역시 100% 완벽한 관계형 데이터베이스 개념에 입각한 쿼리로 바꾸는 것은 거의 불가능할 뿐만 아니라 anlytic function에 비해서 낮은 성능을 보인다. 

이러한 내용에 대한 논쟁에 관심이 있다면 http://www.oracle.com/의 오라클 매거진이나 오라클 매거진 내의 ask Tom 컬럼을 참고하기 바란다. 

참고자료

  • 오라클 SQL 튜닝 실무사례(대청)
  • expert one on one Oracle(wrox)
  • Oracle Magazine


블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

오라클 PL/SQL 후.

Oracle 2014. 11. 14. 09:38

PL/SQL - Oracle's Procedural Language extension to SQL  

SQL문장에서 변수 정의, 조건 처리(IF),반복처리(LOOP, WHILE,FOR)등을 지원하며, 오라클 내부에 절차적 언어로서(Procedure Language)로서 SQL의 단점을 보완

1.변수 선언할수 있음

2.비교 처리할수 있음

3.반복 처리할수 있음

PL/SQL 은 PASCAL 유사한 구조로 DECLARE~BEGIN~EXCEPTION~END순서를 갖는다. PL/SQL은 블록(BLOCK)구조의 언어로서 3부분을 나뉨

1. DECLARE SECTION(선언부)

2. EXECUTABLE SECTION(실행부)

3. EXCEPTION SECTION(예외 처리부)

1.선언부 (DECLARE SECTION)

   PL/SQL 에서 사용되는 모든 변수나 상수를 선언하는 부분 DECLARE로 시작

2. 실행부(EXECUTABLE SECTION)

   절차적 형식으로 SQL문을 실행할 수 있도록 절차적 언어의 요소인 제어문,반복문,함수 정의 등 로직을 기술할 수 있는 부분으로 BEGIN으로 시작.

3. 예외처리분(EXCEPTION SECTION)

   PL/SQL문이 실행되는 중에 에러가 발생할 수 있는데 이를 예외 사항이고 이런 예외사항을 해결하기 위한 문장을 기술할 수 있는 부분으로 EXCEPTION으로 시작


PL/SQL 작성요령

  - 블록내에서는 한문장이 종료될 때 마다 세미클론(;) 을 사용

  - END뒤에 ; 사용하여 하나의 블록이 끝났다는것을 명시

  - 블록은 파일로 작성할수도 있고프롬프트에서 바로 작성할수도 있음

  - SQL*PLUS 환경에서는 DELCLARE나 BEGIN이라는 키워드로 PL/SQL 블록이 시작하는 것을 알수 있다. 

  - 단일행 주석은 --이고 여러행 주석은 /* */ 이다. 

  - 쿼리문을 수행하기 위해서 /가 반드시 입력되어야 하며 PL/SQL블록은 행에 /가 있으면 종결된 것으로 간주 

-----------------------------------------

--간단한 소스.

SET SERVEROUTPUT ON;

DECLARE

BEGIN 

    DBMS_OUTPUT.PUT_LINE('Hello PL/SQL!');

END;

/

-------------------------------------------


변수 선언 

VNO NUMBER(4);

VNAME VARCHAR2(10);

VNO := 10;

VNAME := 'NICE';

예제)

------------------------------------------------

SET SERVEROUTPUT ON;

DECLARE

 VNO NUMBER(4);

 VNAME VARCHAR2(10);

BEGIN

 VNO := 7;

 VNAME := 'NICE';

 DBMS_OUTPUT.PUT_LINE('번호/이름');

 DBMS_OUTPUT.PUT_LINE('---------------------');

 DBMS_OUTPUT.PUT_LINE(VNO||'/'||VNAME);

END;

/

-------------------------------------------------------

아래의 예제는 이데이터를 기준으로 



스칼라 변수,레퍼런스 변수

스칼라 변수 는 일반적으로 그냥선언해서 쓰는 변수를 스칼라 변수

레퍼런스 변수는 이전엔 선언된 변수나 데이터베이스 컬럼에 맞추서 선언

레퍼런스변수 예제

--------------------------------------------------

SET SERVEROUTPUT ON;

DECLARE

VTEST TEST_02%ROWTYPE; //변수의 컬럼을 그대로 가져옴. 

BEGIN

    VTEST.NO:=10;    //이렇게 변수를 사용하지.

    DBMS_OUTPUT.PUT_LINE(VTEST.NO); //쉽징?

END;

/

-----------------------------------------------------

SELECT INTO

- DB에 있는 값을 변수에 대입할수 있음. 

예제소스 

---------------------------------------------------------

SET SERVEROUTPUT ON;

DECLARE

VTEST TEST_02%ROWTYPE;

BEGIN

    SELECT * INTO VTEST

    FROM TEST_02

    WHERE NO=2;

    DBMS_OUTPUT.PUT_LINE(VTEST.NO);

    DBMS_OUTPUT.PUT_LINE(VTEST.ADDRESS);

END;

/



---------------------------------------------------------------
그러나 쿼리문으로 단일데이터만 받기만하는건 아닐터 
테이블 type;
이건 배열이라고 보면 되겠음. 
---------------------------------------------------------------
SET SERVEROUTPUT ON;
DECLARE
--테이블 타입 정의 --
TYPE TEST_02_TABLE IS TABLE OF TEST_02.NO%TYPE INDEX BY BINARY_INTEGER;
--테이블 타입으로 변수를 선언 
VTEST_T TEST_02_TABLE;
I BINARY_INTEGER:=0;--테이블 인덱스정보 I를 인티저로 선언하고 0을 대입
BEGIN 
--테이블내용 DB가져오기
FOR K IN(SELECT * FROM TEST_02) LOOP
I:=I+1;--인덱스를 1증가 
VTEST_T(I):=K.NO;
END LOOP;
--테이블 내용 출력
FOR J IN 1..I LOOP
    DBMS_OUTPUT.PUT_LINE(RPAD(VTEST_T(J),10));
END LOOP;
END;
/



-------------------------------------------------------------------


RECORD TYPE

TABLE TYPE은 한가지 데이터만 읽어오므로 테이블의 ROW를 읽어올땐 TABLE TYPE를 여러개 선언하여야 한다.

이걸 한번에 읽어올수 있는게. 있는데 에게봐라 RECORD TYPE다. 


IF ~THEN~END IF

IF(VNO=10) THEN 

VNAME='3434';

END IF;

선택문이였고

IF ~ THEN ~ELSE~END IF

IF(VNO=19) THEN

VNAME='11919';

ELSE

VNAME='3939';

END IF;

이것은 IF ELS문이고

IF~THEN ~ELSIF ~THEN ~END IF

이런문도 있음

LOOP

EXIT;

END LOOP;

루프이고.. 

FOR문은

FOR N IN 1..5 LOOP

PRINT N;

END LOOP


WHILE

WHILE N<=5 LOOP

 N

END LOOP;




블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,