똑같은 query를 다른 두 시스템에서 실행했다. 헌데 그 결과는 완전 정반대.. 이런 어처구니 없는 경우가 있을 수가.. 아주 황당했다. (사실은 query가 바보였음 -ㅅ-)

(실제 테이블을 내용을 쓸 수 없으니 살짝 테이블 이름과 내용을 바꿔서..) 내가 원하는 결과는 사용자 테이블과 로그인한 시간, IP 등을 기록하는 테이블을 조인해서.. 가장 최근의 기록 1개를 얻고 싶다.
SELECT SUB.USER_ID, SUB.LOGIN_TIME
FROM (SELECT U.USER_ID, H.LOGIN_TIME, ROWNUM AS RNUM
FROM USER U, LOGIN_HIST H
WHERE U.USER_ID = H.USER_ID
ORDER BY H.LOGIN_TIME DESC
AND u.user_name = '바보') SUB
WHERE RNUM = 1;


자.. 이 query의 문제는 뭘까?? 사실 이 문제가 발생하기 전 까지는 누가 만들어 놓은건지도 궁금하지 않았던 저 구석에 있던 query인데.. 문제는 이렇게 발생했다. 다음은 동일한 데이터를 가진 두 서버에서 실행한 이 query의 결과이다.

1번 서버
USER_ID | LOGIN_TIME
--------+-----------
test1 | 2010-10-10 10:10:10

2번 서버
USER_ID | LOGIN_TIME
--------+-----------
test1 | 2010-10-05 10:10:10


왜 이런 결과가 나온걸까?? 우선 가장 바깥에 있는 select 문을 벗겨서 subquery 부분만 실행해 봤다. 그랬더니 이런 결과가 나오는 것이 아닌가??
1번 서버
USER_ID | LOGIN_TIME | RNUM
--------+------------+-----
test1 | 2010-10-10 10:10:10 | 1
test1 | 2010-10-09 10:10:10 | 2
test1 | 2010-10-07 10:10:10 | 3
test1 | 2010-10-05 10:10:10 | 4

2번 서버
USER_ID | LOGIN_TIME | RNUM
--------+------------+-----
test1 | 2010-10-10 10:10:10 | 4
test1 | 2010-10-09 10:10:10 | 3
test1 | 2010-10-07 10:10:10 | 2
test1 | 2010-10-05 10:10:10 | 1


아니!! 같은 query인데 RNUM이 정반대로 붙어있는게 아닌가?? 정말 귀신이 곡할 노릇이라고 하면서 검색을 시작.. 흠.. 검색 시작한지 2분도 안돼서 답 발견 @ㅅ@

역시 문제는 ROWNUM이었다. ROWNUM의 동작 원리를 알지 못 한 상태에서 query를 만드니 저런 query가 만들어진 것 같다. 일단, ROWNUM은 select된 결과 집합에 대한 가상 순번이다. 이 번호는 DB에서 꺼내지는 순서대로 붙게 된다. (optimizer가 어떻게 동작하는지에 따라 그 순서는 달라질 수 있다.) 그리고 결정적으로 잘못 생각한 것은 ORDER BY 구문.. ORDER BY는 결과 집합이 생성된 후에 적용되는 조건이다.

이 두 동작 원리를 가지고 다시 문제를 짚어보자. 내가 원하는 것은 해당 이름의 사용자가 가장 최근에 로그인한 시간을 알고 싶은 것이다. 다시 말해 먼저 로그인한 시간으로 정렬한 후 번호를 붙여서 가장 최근 정보를 가져오는 순서다. 헌데.. ROWNUM과 ORDER BY를 하나의 SELECT문에 넣게 되면서 그 적용 순서가 의도와는 다르게 뒤바뀌게 된 것이다. 결과 집합을 꺼내오면서 ROWNUM이 먼저 적용되고, 모든 결과 집합이 모인 후에 정렬이 된 것이다. 오라클의 optimizer가 동작하는 경우에 따라서 결과 집합을 만드는 순서가 바뀔 수 있고 위의 두 서버와 같이 정반대의 결과가 나올 수도 있는 것이다.

사실 이 query는 이런 원리를 알고 보면 다음과 같이 수정해면 간단하게 원하는 결과를 얻을 수 있는 것이다. 처음부터 왜 저렇게 ROWNUM을 붙였는지 잘 모르게뜸 @ㅅ@
SELECT SUB.USER_ID, SUB.LOGIN_TIME
FROM (SELECT U.USER_ID, H.LOGIN_TIME
--, ROWNUM AS RNUM 이 분분은 사실 필요 없기 때문에 제거
FROM USER U, LOGIN_HIST H
WHERE U.USER_ID = H.USER_ID
ORDER BY H.LOGIN_TIME DESC
AND u.user_name = '바보') SUB
WHERE ROWNUM = 1;

'먹고살기 > Oracle' 카테고리의 다른 글

SQL _ HAVING_2  (0) 2011.04.29
SQL _ CASE식  (0) 2011.04.29
오라클 데이터 타입  (0) 2011.04.22
오라클 DECODE  (0) 2011.04.22
인덱스의 정의  (0) 2011.04.22

오라클에서 지원하는 데이터 타입
이 름
최대 길이
타 입
설 명

CHAR

2000 byte
문자열
고정 길이
NCHAR
2000 byte
문자열
다국적 언어 지원
VARCHAR2
4000 byte
문자열
가변 길이
NVARCHAR2
4000 byte
문자열
가변 길이, 다국적 언어 지원
LONG
2 Gb
문자열
가변 길이
RAW
2 Gb
바이트 문자열
가변 길이
LONG RAW
2 Gb
이진 문자열

NUMBER
10^-38 ~ 10^38
숫자

DATE

날짜
BC 4712/1/1 ~ CE 4712/12/31
BLOB
4 Gb
이진 데이터
구조화 되지 않은 데이터
CLOB
4 Gb
문자 데이터
구조화 되지 않은 데이터
NCLOB
4 Gb
문자 데이터
다국적 언어 지원
BFILE
4 Gb
이진 데이터
데이터베이스 외부에 데이터를 저장함
ROWID
10 byte
이진 데이터
로우 어드레스
ROWID
4000 byte
이진 데이터
로우 어드레스

ANSI/ISO SQL 타입과의 비교

ANSI/ISO SQL 데이터 타입
오라클 데이터 타입
CHARACTER(n), CHAR(n)
CHAR(n)
NUMERIC(p,s), DECIMAL(p,s), DEC(p,s)
NUMBER(p,s)
INTEGER, INT, SMALLINT
NUMBER(38)
FLOAT(p)
FLOAT(p), NUMBER
REAL
FLOAT(63), NUMBER
DOUBLE PRECISION
FLOAT(126), NUMBER
CHARACTER VARYING(n), CHAR VARYING(n)
VARCHAR2(n)

SQL/DS, DB2 데이터 타입과 비교

SQL/DS, DB2 데이터 타입
오라클 데이터 타입
CHARACTER(n)
CAHR(n)
VARCHAR(n)
VARCHAR2(n)
LONG VARCHAR
LONG
DECIMAL(p,s)
NUMBER(p,s)
INTEGER, SMALLINT
NUMBER(38)
FLOAT(p)
FLOAT(p), NUMBER
DATE
DATE

SQL/DS, DB2 데이터 타입 중 TIME, TIMESTAMP, GRAPHIC, VARGRAPHIC, LONG VARGRAPHIC들은
오라클 데이터 타입과 대칭되는 것이 없다.

'먹고살기 > Oracle' 카테고리의 다른 글

SQL _ CASE식  (0) 2011.04.29
[Oracle] ROWNUM 사용 시 주의  (0) 2011.04.25
오라클 DECODE  (0) 2011.04.22
인덱스의 정의  (0) 2011.04.22
[ORACLE] 데이터베이스 시작의 3단계 설명  (0) 2011.04.22

DECODE

프로그래밍언어의 스위치 문과 같은 기능을 하는 SQL 함수이다.

  • 형식

DECODE (비교값 , CASE1 , 결과 1 , CASE2 , 결과 2 , CASE3 , 결과 3..... , 기본값)

비교값이 CASE1과 같으면 결과 1을 리턴하고,

CASE2와 같으면 결과 2를 리턴한고,

CASE3과 같으면 결과 3을 리턴하고..........

모든 CASE 값들과 일치하는 것이 없으면 기본값을 리턴한다.

만약 기본값이 생략된 상태에서 일치하는 값이 없으면 NULL 이 리턴된다.

  • 주의사항

비교값 과 CASE 값의 데이터 타입이 같아야 한다.

다를 경우에는 자동형변환이 되면 문제없지만 자동형변환이 불가능할 경우 오류를 발생한다.

※ 오라클이 알아서 비교값을 CASE 값과 같은 타입으로 변환시키려고 시도한다.


※ DECODE함수

- 특정 컬럼의 값을 기준으로 마치 IF문을 사용하는 것과 같은 효과를 내는 함수

- 해당 컬럼의 값이 'A'이면 지정한 특정한 값을 출력하고, 'B'이면 또 다른 값을 출력

- 기본값을 정해서 조건을 만족하지 않는 경우의 출력 제어 가능

- 구문

DECODE({column | expression}, search1, result1 [,search2,result2] ...

[,default] )

- 사용 예

① job값에 따른 새로운 직업명 출력

② deptno에 따른 인상급여 출력

[출처] 오라클 DECODE|작성자 하늘아이

'먹고살기 > Oracle' 카테고리의 다른 글

[Oracle] ROWNUM 사용 시 주의  (0) 2011.04.25
오라클 데이터 타입  (0) 2011.04.22
인덱스의 정의  (0) 2011.04.22
[ORACLE] 데이터베이스 시작의 3단계 설명  (0) 2011.04.22
오라클 변환함수 ( TO_CHAR )  (0) 2011.04.22

※ 인덱스란?

인덱스는 테이블이나 클러스트에서 쓰여지는 선택적인 객체로서, 오라클 데이터베이스 테이블내의
원하는 레코드를 빠르게 찾아갈 수 있도록 만들어진 데이터 구조
입니다.

자동 인덱스 : 프라이머리 키 또는 uinque 제한 규칙에 의해 자동적으로 생성되는 인덱스 입니다.

수동 인덱스 : CREATE INDEX 명령을 실행해서 만드는 인덱스들 입니다.

※ Index를 생성하는 것이 좋은 Column

WHERE절이나 join조건 안에서 자주 사용되는 컬럼
null 값이 많이 포함되어 있는 컬럼
WHERE절이나 join조건에서 자주 사용되는 두 개이상의 컬럼들


※ 다음과 같은 경우에는 index 생성이 불필요 합니다.

table이 작을 때
테이블이 자주 갱신될 때

오라클 인덱스는 B-tree(binary search tree)에 대한 원리를 기반으로 하고 있습니다.

B-tree인덱스는 컬럼안에 독특한 데이터가 많을 때 가장 좋은 효과를 냅니다.

이 알고리즘 원리는

① 주어진 값을 리스트의 중간점에 있는 값과 비교합니다.
만약 그 값이 더 크면 리스트의 아래쪽 반을 버립니다.
만약 그 값이 더 작다면 위쪽 반을 버립니다.

② 하나의 값이 발견될 때 까지 또는 리스트가 끝날 때까지 그와 같은 작업을 다른 반쪽에도
반복합니다.



※ 인덱스는 B-tree 구조를 가지며 크게 다음 네 가지로 분류될수 있습니다.


Bitmap 인덱스

비트맵 인덱스는 각 컬럼에 대해 적은 개수의 독특한 값이 있을 경우에 가장 잘 작동합니다.
그러므로 비트맵 인덱스는 B-tree 인덱스가 사용되지 않을 경우에서 성능을 향상 시킵니다.
테이블이 매우 크거나 수정/변경이 잘 일어나지 않는 경우에 사용할수 있습니다.

SQL>CREATE BITMAP INDEX emp_deptno_indx
ON emp(deptno);


Unique 인덱스

Unique 인덱스는 인덱스를 사용한 컬럼의 중복값들을 포함하지 않고 사용할 수 있는 장점이 있습니다.
프라이머리키 와 Unique 제약 조건시 생성되는 인덱스는 Unique 인덱스입니다.

SQL>CREATE UNIQUE INDEX emp_ename_indx
ON emp(ename);


Non-Unique 인덱스

Non-Unique 인덱스는 인덱스를 사용한 컬럼에 중복 데이터 값을 가질수 있습니다.

SQL>CREATE INDEX dept_dname_indx
ON dept(dname);


결합 (Concatenated(=Composite)) 인덱스

복수개의 컬럼에 생성할 수 있으며 복수키 인덱스가 가질수 있는 최대 컬럼값은 16개입니다

SQL>CREATE UNIQUE INDEX emp_empno_ename_indx
ON emp(empno, ename);


※ 인덱스의 삭제


- 인덱스의 구조는 테이블과 독립적이므로 인덱스의 삭제는 테이블의 데이터에는 아무런 영향도 미치지
않습니다.

- 인덱스를 삭제하려면 INDEX의 소유자이거나 DROP ANY INDEX권한을 가지고 있어야 합니다.

- INDEX는 ALTER를 할 수 없습니다.

SQL>DROP INDEX emp_empno_ename_indx ;


인덱스에 대한 정보는 USER_INDEXES 뷰 또는 USER_IND_COLUMNS뷰를 통해 검색할 수
있습니다.

[출처] 인덱스의 정의|작성자 하늘아이


1. 데이타베이스 시작의 3단계

1-1. NOMOUNT

- 인스턴스가 메모리에 할당 및 시작되는 단계이다.
- 데이터베이스 관리에 있어서 데이터베이스 생성 또는 컨트롤 파일을 생성하는 작업은 NOMOUNT 단계에서만 할 수 있다.

1-2. MOUNT

- 컨트롤 파일을 읽고 데이터베이스를 마운트하는 단계이다.
- MOUNT 단계에서만 할 수 있는 작업
1) 데이터 파일의 이름 변경
2) 아카이브, 노아카이브 모드 변경
3) 전체 데이터베이스 복구

1-3. OPEN

- 데이터베이스를 시작하고 사용자들의 접속을 허용하는 단계이다.
- 만약, OPEN 단계에서 데이터 파일 또는 리두 로그 파일이 실제 존재하지 않으면 오라클 서버는 오류를 발생시키고 데이터베이스는 MOUNT 단계에서 멈추게 된다.

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

2. ALTER DATABASE

데이터베이스가 NOMOUNT or MOUNT 단계에 있을때 상위단계로 가기 위해서는 ALTER DATABASE 명령을 사용해야 한다.
하지만 상위단계로만 이동이 가능할 뿐 하위단계로의 이동은 불가능하다.

SQL> shutdown
데이터베이스가 닫혔습니다.
데이터베이스가 마운트 해제되었습니다.
ORACLE 인스턴스가 종료되었습니다.
SQL> startup nomount - 데이터베이스가 시작되었지만 nomount 상태이다.
ORACLE 인스턴스가 시작되었습니다.

Total System Global Area 135338868 bytes
Fixed Size 453492 bytes
Variable Size 109051904 bytes
Database Buffers 25165824 bytes
Redo Buffers 667648 bytes
SQL> alter database mount; - alter database 명령을 이용해서 상위단계인 MOUNT 단계로 상승시킨다.

데이타베이스가 변경되었습니다.

SQL> alter database open; - alter database 명령을 이용해서 상위단계인 OPEN 단계로 상승시킨다.

데이타베이스가 변경되었습니다.

SQL> shutdown
데이터베이스가 닫혔습니다.
데이터베이스가 마운트 해제되었습니다.
ORACLE 인스턴스가 종료되었습니다.
SQL>

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

3. RESTRICT 모드

데이터베이스를 RESTRICT 모드로 오픈하고 RESTRICTED SESSION 권한을 보유한 사용자들만이 접속할 수 있도록 하는 방법

SQL> startup restrict - RESTRICT 모드로 데이터베이스를 오픈 한다. 이때 RESTRICT는 데이터베이스의 접속에 대한 모든 사용자를 제한하는 명령어이다.
ORACLE 인스턴스가 시작되었습니다.

Total System Global Area 135338868 bytes
Fixed Size 453492 bytes
Variable Size 109051904 bytes
Database Buffers 25165824 bytes
Redo Buffers 667648 bytes
데이터베이스가 마운트되었습니다.
데이터베이스가 열렸습니다.
SQL> conn scott/tiger - SCOTT 계정으로 접속하려고 하지만 RESTRICTED SESSION 권한이 없기 때문에 접속이 안된다.
ERROR:
ORA-01035: ORACLE only available to users with RESTRICTED SESSION privilege


경고: 이제는 ORACLE에 연결되어 있지 않습니다.
SQL> conn /as sysdba - RESTRICTED SESSION 권한을 주기 위해 관리자로 접속한다.
연결되었습니다.
SQL> grant restricted session to scott; - RESTRICTED SESSION 권한 부여

권한이 부여되었습니다.
SQL> conn scott/tiger
연결되었습니다.
SQL> conn /as sysdba
연결되었습니다.
SQL> revoke restricted session from scott; - RESTRICTED SESSION 권한을 취소한다.

권한이 취소되었습니다.

SQL> startup force - RESTRICT 명령어를 풀어주려면 force 명령어를 통하여 재시작해주면 된다.
ORACLE 인스턴스가 시작되었습니다.

Total System Global Area 135338868 bytes
Fixed Size 453492 bytes
Variable Size 109051904 bytes
Database Buffers 25165824 bytes
Redo Buffers 667648 bytes
데이터베이스가 마운트되었습니다.
데이터베이스가 열렸습니다.

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

4. ALTER SYSTEM 명령을 이용한 사용자의 접속을 강제로 종료

SQL> select sid, serial# from v$session - 데이터베이스에 연결되어 있는 사용자를 확인한다.
2 where username='SCOTT';

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

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

스캇 세션

시작 \프로그램\Oracle - OraHome92\Application Development\sqlplus 실행 -> 스캇 계정으로 로그인

SQL> select * from tab;

TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
BONUS TABLE
DEPT TABLE
EMP TABLE
SALGRADE TABLE

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

SQL> select sid, serial# from v$session
2 where username='SCOTT';

SID SERIAL#
---------- ----------
10 8

SQL> alter system kill session '10,8'; - KILL 명령어를 통해 session을 종료시킨다.

시스템이 변경되었습니다.

SQL> select sid, serial# from v$session
2 where username='SCOTT';

SID SERIAL#
---------- ----------
10 8

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

스캇 세션

SQL> select * from tab;
select * from tab
*
1행에 오류:
ORA-00028: 세션이 종료되었습니다

'먹고살기 > Oracle' 카테고리의 다른 글

오라클 DECODE  (0) 2011.04.22
인덱스의 정의  (0) 2011.04.22
오라클 변환함수 ( TO_CHAR )  (0) 2011.04.22
오라클 접속 리스너 설정하기.(tnanames.ora 설정)  (0) 2011.04.22
오라클 메모리  (0) 2011.04.22