2020년 7월 25일 토요일

ORACLE ROWNUM(오라클 ROWNUM 동영상)


 https://youtu.be/LrCAux9_HCY


ORACLE ROWNUM

 

ROWNUM은 SELECT절에 의해 추출되는 WHERE절을 만족하는 행(ROW)에 붙는 순번이다다시 말해 WHERE절 조건을 만족 시킨 행에 대해 1부터 붙는 순번이다.

 

ROWNUM은 의사 칼럼으로 참조만 될 뿐 데이터베이스에 물리적으로 저장되지 않는다.

(DESC 명령으로 보이지 않는 의사 칼럼).

 

SELECT 리스트에 ROWNUM을 이용하는 것도 물론 가능하다. 

 

ORDER BY를 사용한다면 WHERE절까지 만족 시킨 결과에 ROWNUM이 붙은 상태로 ORDER BY가 반영된다.(ORDER BY는 맨 마지막에 실행된다 ROWNUM ORDER BY 실행 전에 부여되며, ORDER BY는 맨 나중에 실행되므로 추출되는 순서대로 ROWNUM이 붙어 있지는 않다.

 

다른 데이터베이스에서 주로 사용되는 top-N 쿼리의 기능을 ROWNUM을 이용하여 구현할 수 있다.

 

- ROWNUM을 변경하기 위해 DML(데이터 조작어, INSERT, UPDATE, DELETE)을 사용할 수 없다.

주로 <, <= 사용하며 >, >= 인 경우 ROWNUM은 동작하지 않는다.

- ROWNUM = 1은 사용 가능 하지만 ROWNUM > 1, ROWNUM=2 인 경우는 데이터가 추출되지 않는다. (ROWNUM WHERE절을 만족하는 레코드에 붙이는 순번이므로 처음 한 건 추출해서 ROWNUM 2인지 비교한다처음 레코드는 ROWNUM 1, 조건에 맞지 않으므로 버리고 다른 다음 레코드 선택 후 또 ROWNUM 2인지 비교하지만 역시 새로 추출되는 레코드는 ROWNUM 1이므로 버리고 새로운 레코드를 추출한다이 과정을 반복해 보면 ROWNUM = 2는 도달할 수 없는 값이 됨을 알 수 있다.)

 

SQL> select rownum,  empno, ename from emp;

 

    ROWNUM      EMPNO ENAME

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

         1       7369 SMITH

         2       7499 ALLEN

         3       7521 WARD

……

 

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

 

-- 4건만 SEELCT

SQL>  select rownum,  empno, ename from emp where rownum < 5;

 

    ROWNUM      EMPNO ENAME

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

         1       7369 SMITH

         2       7499 ALLEN

         3       7521 WARD

         4       7566 JONES

 

SQL>  select rownum,  empno, ename from emp;

 

    ROWNUM      EMPNO ENAME

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

         1       7369 SMITH

         2       7499 ALLEN

         3       7521 WARD

         ……

 

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

 

-- 4건만 SELECT, 먼저 다른 조건없이 4건을 꺼내고 ROWNUM 1부터 부여 (이름으로 정렬해서 4건을 꺼내는 것은 아님 ename으로 정렬하니 ROWNUM이 깨진다.

-- ORDER BY ORDER BY를 제외한 쿼리를 먼저 실행 후 그 결과에 대해 정렬하므로 ROWNUM ENAME기준으로 정렬되게 되어 순차적으로 부여되지 않는다.

SQL> select rownum, empno, ename from emp where rownum < 5 order by ename;

 

    ROWNUM      EMPNO ENAME

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

         2       7499 ALLEN

         4       7566 JONES

         1       7369 SMITH

         3       7521 WARD

 

-- 위 쿼리를 이름순으로 정렬한 후 ROWNUM을 부여하려면 다음과 같이 해야한다.

-- 안쪽 SELECT에서 이름 오름차순으로 데이터를 정렬한 후 하나씩 꺼내면서 ROWNUM을 부여하고 --- 5보다 작은지 확인한다.

SQL> select * from 

( select rownum, empno, ename

           from emp

          order by ename )

           where ROWNUM < 5;

 

SQL>  select rownum, empno, ename from emp where rownum = 1;

 

    ROWNUM      EMPNO ENAME

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

         1       7369 SMITH

 

-- ROWNUM=2인 경우 선택되는 로우()가 없다.

-- WHERE절에 ROWNUM 조건 외에는 없으므로 모든 데이터가 조건을 만족하게 되고최초 한건을 읽으면 ROWNUM 1이되어 ROWNUM = 2 조건에 만족하지 않으므로 버리고다음 레코드를 또 읽어 ROWNUM을 부여하는데 이전에 읽은 데이터는 조건에 맞지 않아 버렸으므로 다시 ROWNUM 1이되어 ROWNUM = 2 조건에 맞지않아 데이터를 버리고 다음 레코드를 읽게 된다이런식으로 하다보면 결국 ROWNUM = 2 조건을 만족하는 데이터는 없게된다.

SQL>  select rownum, empno, ename from emp where rownum = 2;

선택된 레코드가 없습니다.

 

-- 처음 한건을 추출하고 ROWNUM이 1보다 큰지 확인 => 거짓이므로 버림다음 레코드 선택 후 또 ROWNUM이 1보다 큰지 확인하지만 역시 거짓이므로 버린다결국 모두 거짓이 되어 선택된는 레코드는 없다.

SQL> select * from emp where ROWNUM > 1;

선택된 레코드가 없습니다.

 

 

이번에는 게시판 페이지 나누기 쿼리를 해보자개발할 때 흔히 사용되는 쿼리인데 ROWNUM을 이용하고안쪽 SELECT 쿼리에서 ORDER BY를 사용하지 않고 인덱스 및 인덱스와 연관된 힌트를 사용하는 것이 포인트 이다.

 

myemp1 테이블(1000만건)에서 ename 칼럼을 기준으로 적절한 인덱스를 생성 후 게시판 페이지 쿼리를 작성하시오. (이미 ename 기준으로 오름차순 인덱스가 생성되어 있다.)

 

    한페이지당 10개 출력.

    - ename를 기준으로 내림차순으로 Dislplay.

    - 10,000번째 페이지를 출력하는 쿼리를 작성하시오.

 

-- 먼저 myemp1 테이블의 ename컬럼에 걸려 있는 인덱스 이름을 확인

 

SQL> SELECT a.index_name, a.column_name, b.visibility

 FROM USER_IND_COLUMNS a, USER_INDEXES b

 WHERE a.table_name = 'MYEMP1'

   AND a.index_name = b.index_name;

 

IDX_MYEMP1_ENAME

 

-- 인덱스가 오름차순으로 생성되었으니 내림차순으로 결과를 보기 위해 INDEX_DESC 힌트를 사용하자.

 

-- 오라클12C에서 17

-- 실행계획을 살펴보면 안쪽 SELECT FULL TABLE SCAN 한다.

-- ROWNUM은 크거나같다(>=)가 동작하지 않기에 SELECT로 한번더 사서 rnum 이라는 이름으로 비교

-- 맨안쪽 SELECT에서 모든 데이터를 다 읽고 맨 바깥쪽에서 해당 페이지를 추출하므로 느리다.

SELECT *

FROM (

    SELECT a.*, ROWNUM rnum

        FROM (

              SELECT empno, ename, sal

                FROM myemp1 e

              ORDER BY ENAME DESC

        ) a

    ) b

WHERE rnum <= 10000*10

AND    rnum >= 9999*10 +1;

 

-- 오라클12C에서 14

-- 실행계획을 살펴보면 안쪽 SELECT INDEX RABGE SCAN 한다.

SELECT *

FROM (

    SELECT a.*, ROWNUM rnum

        FROM (

              SELECT  /*+ INDEX_DESC(e IDX_MYEMP1_ENAME)*/

                         empno, ename, sal

                FROM myemp1 e

              WHERE ename > ''

        ) a

    ) b

WHERE rnum <= 10000*10

AND rnum >= 9999*10 +1;

 

-- 0

-- 안쪽 SELECT에서 ROWNUM으로 한계치를 미리 지정하여 건수를 줄임

SELECT *

FROM (     

        SELECT a.*, ROWNUM rnum

        FROM (

              SELECT empno, ename, sal

                FROM myemp1 e

              --WHERE ROWNUM <= 10000*10

              ORDER BY ENAME DESC -- order by 때문에 rownum있어서 한번더 싼 SELECT에서 ROWNUM비교

                ) a

        WHERE ROWNUM <= 10000*10

)

WHERE rnum >= 9999 * 10 +1;

 

 

-- 0

-- 안쪽 SELECT에서 ROWNUM으로 한계치를 미리 지정하여 건수를 줄임

-- ORDER BY를 사용 안하니 직접 ROWNUM으로 비교 가능하므로 SELECT 절이 하나 빠짐

select empno, ename, sal

  from

        ( 

          select /*+ index_desc(e IDX_MYEMP1_ENAME) */

                rownum rnum,

                empno,

                ename,

                sal

          from  myemp1 e

          where  rownum <= 10000*10

        )

 where rnum >= 9999*10+1;

 

 

 

 


PL/SQL Record Type


 

PL/SQL Record Type

 

Record Type

 

n  서로 다른 종류의 데이터 아이템을 모아서 만든 데이터 구조로 데이터 테이블의

레코드()과 유사하게 다른 필드로 구성되어 있으며 프로그래밍 언어의 구조체와 유사하다.

n  하나의 레코드 타입안에는 Scalar Data Type, 다른 Record Type, Table Type들이 들어올 수

있으며 먼저 TYPE을 정의 후 변수의 데이터 타입으로 할당하여 사용한다.

 

TYPE emp_record_type is record (

  empno emp.empno%TYPE,

  ename emp.ename%TYPE,

  sal   emp.sal%TYPE

);

emp_record emp_record_type;

 

n  PL/SQL은 다음 세가지 형태의 레코드를 다룰 수 있다.

User-Definded records : 사용자가 서로 다른 필드로 레코드 구조를 정의

Table-Based records : %rowtype 이용

Cursor-Based redords : %rowtype 이용

 

1. User-Definded Records

 

SQL> edit ojc9

 

SET SERVEROUTPUT ON

CREATE OR REPLACE PROCEDURE ojc9(p_empno IN NUMBER)

IS

TYPE emp_record_type is record (

  empno emp.empno%TYPE,

  ename emp.ename%TYPE,

  sal   emp.sal%TYPE

);

emp_record emp_record_type;

BEGIN

   SELECT empno, ename, sal

     INTO emp_record.empno, emp_record.ename, emp_record.sal

     FROM emp

    WHERE empno = p_empno;

 

   DBMS_OUTPUT.PUT_LINE(emp_record.empno || ' : ' || emp_record.ename || ' : ' || emp_record.sal);

 

EXCEPTION

    WHEN NO_DATA_FOUND THEN

      DBMS_OUTPUT.PUT_LINE('no data...');

    WHEN TOO_MANY_ROWS THEN

      DBMS_OUTPUT.PUT_LINE('to many rows...');

END ojc9;

/

 

-- 위에서 작성한 ojc9.sql 파일을 실행하여 ojc9 라는 프로시저를 생성한다.

-- 오류가 발생하면 show errors로 확인 후 디버깅 해야 한다.

SQL> @ojc9

프로시저가 생성되었습니다.

 

SQL> exec ojc9(7788)

7788 : SCOTT : 3000

 

-- EMP 테이블에 없는사원번호를 입력, SELECT되는 데이터가 한건도 없으므로 오류발생.

-- EXCEPTION절로 빠져 WHEN NO_DATA_FOUND 처리부가 실행된다.

-- NO_DATA_FOUND는 오라클서버에 미리 정의된 예외명이며오라클 서버 오류인 ORA-01403에러에 대해 부여된 예외 명칭이다.

SQL> exec ojc9(1234)

No data…

 

2. Table Based Records(%rowtype 이용)

 

DECLARE

   emp_rec emp%rowtype;

BEGIN

   SELECT * into emp_rec

   FROM emp

   WHERE empno = 7369;

 

   dbms_output.put_line('사번 : ' || emp_rec.empno);

   dbms_output.put_line('이름 : ' || emp_rec.ename);

   dbms_output.put_line('급여 : ' || emp_rec.sal);

END;

/

 

사번 : 7369

이름 : SMITH

급여 : 4500

 

3. Cursor-Based Records(%rowtype 이용)

 

DECLARE

   CURSOR emp_cur is

      SELECT empno, ename, sal

      FROM emp;

     

   emp_rec emp_cur%rowtype;

BEGIN

   OPEN emp_cur;

   LOOP

      FETCH emp_cur into emp_rec;

      EXIT WHEN emp_cur%notfound;

      DBMS_OUTPUT.put_line(emp_rec.empno || ':' || emp_rec.ename

                                   || ':' || emp_rec.sal);

   END LOOP;

   CLOSE emp_cur;

END;

/

 

7369:SMITH:4500

7499:ALLEN:1600

……

7900:JAMES:950

7902:FORD:3000

7934:MILLER:1300

 


 

4. Table Type, Record Type을 같이 이용하는 예문

 

DECLARE

   TYPE empRecordType IS RECORD (

      empno  NUMBER(4),

      ename  VARCHAR2(20)

    );

   TYPE empTableType IS TABLE OF empRecordType  -- PL/SQL Table Type

      INDEX BY BINARY_INTEGER;

  

   emp_rec empRecordType;

   emp_table empTableType;

BEGIN

    emp_rec.empno := 1111;

    emp_rec.ename := '1길동';

    emp_table(1) := emp_rec; 

   

    for i in emp_table.first .. emp_table.last Loop

      dbms_output.put_line(emp_table(i).empno || ':' || emp_table(i).ename);

    End Loop;

   

END;

/

 

1111:1길동

 

 

(C#교육동영상)C# ADO.NET 실습 ODP.NET/ODAC 설치 오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원

  (C#교육동영상)C# ADO.NET 실습  ODP.NET/ODAC 설치  오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원 https://www.youtube.com/watch?v=qIPU85yAlzc&list=PLxU-i...