샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.




No. 01

 

 

컬럼 추가/수정/삭제


테이블에 대한 수정은 특별한 경우를 제외하고는 전혀 사용하지 않아도 된다. 테이블의 구조가 운영 중에 바뀐다는 것은 사실상 모델링에 문제가 있다는 의미이다. 이런 작업을 하지 않도록 하는 것이 가장 좋은 방법이다. 그러나 실제로 현장에서 종종 일어나는 일이라고 한다. 이런 명령어도 있다는 정도만 알아두자.


컬럼 추가/수정

ALTER TABLE 테이블

[ADD | MODIFY] 컬럼 데이터타입;

  • ADD : 컬럼을 추가한다.
    • 추가된 컬럼에는 NULL값이 저장된다.
  • MODIFY : 컬럼을 수정한다.
    • CHAR와 VARCHAR2는 컬럼의 크기를 변경하지 않으면 서로 수정 교환 가능
    • 컬럼의 크기는 늘리는 것만 가능하다.
    • 대상 컬럼에 데이터가 없을 때만 크기를 줄이거나 타입을 수정하는 것이 가능


◈ 예제

▶ 컬럼 추가

create table example1 (

depno number(2),

dname varchar2(14)

);

Table EXAMPLE1이(가) 생성되었습니다.


alter table example1

add loc varchar2(13);

Table EXAMPLE1이(가) 변경되었습니다.


alter table example1

add start_date date default sysdate;

Table EXAMPLE1이(가) 변경되었습니다.


desc example1



▶ 컬럼 수정

alter table example1

modify loc char(13);

Table EXAMPLE1이(가) 변경되었습니다.



alter table example1

modify depno number(5);

Table EXAMPLE1이(가) 변경되었습니다.


desc example1



컬럼 삭제

ALTER TABLE 테이블

DROP COLUMN 컬럼;

  • 컬럼을 삭제한다.
  • SYS 유저가 소유한 테이블의 컬럼은 DROP 할 수 없다.

컬럼에 데이터가 있어도 삭제가 되므로 삭제시 주의 해야 한다.



◈ 예제

alter table example1

drop column loc;

Table EXAMPLE1이(가) 변경되었습니다.


alter table example1

drop column dname;

Table EXAMPLE1이(가) 변경되었습니다.


desc example1




No. 02

 

 

서브 쿼리를 사용한 테이블 생성


서브 쿼리의 결과를 테이블로 저장하는 방법이다. 주로 테스트용 테이블이나 임시 테이블 또는 분석용 테이블을 생성하는데 이용된다.


기본 구조

CREATE TABLE 테이블[(컬럼, 컬럼, )]

AS (SELECT 문장 : subquery 문);

  • 서브 쿼리문의 결과를 테이블로 생성한다.
  • 데이터 타입과 길이는 서브 쿼리 문에 의해서 결정된다.
  • 컬럼의 리스트와 서브 쿼리의 컬럼은 반드시 1:1대응돼야 한다.
  • 컬럼명을 기술하지 않으면 서브 쿼리의 검색 결과 출력되는 헤더를 컬럼명으로 사용
  • 서브 쿼리에는 ORDER BY절을 사용할 수 없다.
  • 기존 테이블의 제약 조건 중 NOT NULL은 새로운 테이블에 상속된다.
    • 다른 제약 조건은 상속되지 않는다.


◈ 예제

create table dept80

as select employee_id, last_name, salary*12 ANNUAL, hire_date

from employees

where department_id = 80);

Table DEPT80이(가) 생성되었습니다.


select * from dept80;


서브 쿼리에 수식이나 함수 등이 사용되는 경우 이들을 새롭게 생성되는 테이블의 컬럼명으로 부적절함으로 생성되는 테이블의 컬럼 명을 직접 지정해줘야 한다.

'CREATE TABLE'절에 직접 정의 하거나 서브 쿼리문의 SELECT절에 별명을 지정하면 된다.



create table emp_dept(emp_id, ename, dept_id, dept_name)

as ( select employee_id, first_name, e.department_id, department_name

from employees e, departments d

where e.department_id = d.department_id);

Table EMP_DEPT이(가) 생성되었습니다.


select * from emp_dept;




샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.


제약 조건을 생성하는 방법은 테이블을 생성할 때 같이 생성하거나 나중에 추가하는 방법이 있다. 그리고 생성된 제약 조건은 작업 환경에 따라 활성화 되거나 비활성화 될 수 있다.




No. 01

 

 

제약 조건 추가


기본 구조

ALTER TABLE 테이블

ADD CONSTRAINT 제약_조건_이름 constraint_type (컬럼);

  • NOT NULL을 제외한 제약 조건을 추가한다.
  • constraint_type : 제약 조건 타입, 제약 조건별 설정 방법과 동일하다.
    • PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK
  • NOT NULL 제약 조건은 MODIFY 명령을 이용해서 추가한다.


ALTER TABLE 테이블

MODIFY 컬럼 CONSTRAINT 제약_조건_이름 NOT NULL;

  • NOT NULL 제약 조건을 추가한다.

NOT NULL의 경우 ADD를 이용하는 것이 아니라 MODIFY를 이용하는 특징이 있음을 기억한다.



◈ 예제

예제용 테이블에 입력된 데이터를 확인하고 제약 조건을 추가한다. 데이터가 입력되어 있는 테이블이 이미 있다고 가정한다.

▶ 예제용 테이블 생성

class(강좌)

 Column Name

cno

(강좌번호) 

cname

(강좌명) 

 

 

 Key Type

PK

UK

 

 

 Nulls/Unique

 

UNIQUE

 

 

 참조 테이블

 

 

 

 

 참조 컬럼 

 

 

 

 

 CHECK

 

 

 

 

 Data Type

VARCHAR2

VARCHAR2

 

 

 Length

2

50

 

 


create table class(

cno varchar2(2),

cname varchar2(50)

);

Table CLASS이(가) 생성되었습니다.


insert into class values ('01', '데이터베이스');

1 행 이(가) 삽입되었습니다.


insert into class values('02', '프로그래밍');

1 행 이(가) 삽입되었습니다.



                                                         student(학생)

 Column Name

sno

(학번)

sname

(이름)

cno

(소속강좌)

 

 Key Type

PK 

 

FK 

 

 Nulls/Unique

 

NOT NULL 

 

 

 참조 테이블

 

 

class

 

 참조 컬럼

 

 

cno

 

 CHECK

 

 

 

 

 Data Type

VARCHAR2

VARCHAR2

VARCHAR2

 

 Length

4

50

2

 


create table student(

sno varchar2(4),

sname varchar2(50),

cno varchar2(2)

);

Table STUDENT이(가) 생성되었습니다.


insert into student values ('0414', '홍길동', '01');

1 행 이(가) 삽입되었습니다.


insert into student values ('0415', '임꺽정', '02');

1행 이(가) 삽입되었습니다.


insert into student values ('0416', '이순신', '03');

1행 이(가) 삽입되었습니다.


commit;

커밋 완료.



▶ 제약 조건 추가

alter table class

add constraint class_cno_pk primary key (cno);

Table CLASS이(가) 변경되었습니다.


alter table class

add constraint class_cname_uk unique (cname);

Table CLASS이(가) 변경되었습니다.


alter table student

add constraint student_sno_pk primary key (sno);

Table STUDENT이(가) 변경되었습니다.


alter table student

modify sname constraint student_sname_nn not null;

Table STUDENT이(가) 변경되었습니다.


alter table student

add constraint student_cno_fk foreign key (cno) references class (cno);

오류 보고 -

ORA-02298: cannot validate (HR.STUDENT_CNO_FK) - parent keys not found


STUDENT 테이블에서 참조하려는 CLASS 테이블의 cno 컬럼에 없는 강좌번호인 '03'이 STUDENT 테이블에 있기 때문에 참조 무결성이 위배되기 때문에 FK 제약 조건 추가가 실패했다.

테이블에 잘못된 데이터가 입력된 경우 제약 조건 추가가 실패할 수 있다.



update student set cno = '01'

where sno = '0416';

1 행 이(가) 업데이트되었습니다.


commit;

커밋 완료.


alter table student

add constraint student_cno_fk foreign key (cno) references class (cno);

Table STUDENT이(가) 변경되었습니다.


제약 조건에 맞게 데이터를 수정한 후에 제약 조건을 추가할 수 있다.



▶ 제약 조건 확인

select c.table_name, c.constraint_name, c.constraint_type, s.column_name

from user_constraints c, user_cons_columns s

where c.constraint_name = s.constraint_name

and c.table_name in ('CLASS', 'STUDENT')

order by c.table_name;


제약조건이 잘 생성되어 있는 것을 볼 수 있다.




No. 02

 

 

제약 조건 삭제


기본 구조

ALTER TABLE 테이블

DROP PRIMARY KEY | UNIQUE (컬럼) | CONSTRAINT 제약_조건_이름 [CASCADE];

  • 제약 조건을 삭제한다.
  • CASCADE : PK나 UK가 삭제될 경우 이를 참조하는 FK도 삭제된다.


◈ 예제

alter table class

drop constraint class_cno_pk;

오류 보고 -

ORA-02273: this unique/primary key is referenced by some foreign keys


FK에 의해 참조되는 PK나 UK는 삭제되지 않는다.


alter table class

drop constraint class_cno_pk cascade;

Table CLASS이(가) 변경되었습니다.


cascade 옵션을 기술한 경우 FK도 같이 삭제된다.


alter table class

drop unique (cname);

Table CLASS이(가) 변경되었습니다.


alter table student

drop constraint student_sname_nn;

Table STUDENT이(가) 변경되었습니다.


alter table student

drop primary key;

Table STUDENT이(가) 변경되었습니다.


PK나 UK같은 경우는 제약_조건_이름을 기술하지 않아도 간단히 삭제할 수 있다. PK, UK도 다른 제약조건과 마찬가지로 제약_조건_이름을 기술하고 삭제도 할 수 있다.




No. 03

 

 

제약 조건 활성화/비활성화


제약 조건 활성화

ALTER TABLE 테이블

ENABLE CONSTRAINT 제약_조건_이름;

  • 비 활성화된 제약 조건을 활성화 한다.
  • PK나 UK 제약 조건이 활성화 되면 인덱스가 만들어진다.


제약 조건 비활성화

ALTER TABLE 테이블

DISABLE CONSTRAINT 제약_조건_이름 [CASCADE];

  • 제약 조건을 비활성화 한다.
  • PK나 UK인 경우 인덱스가 삭제된다.
  • 비활성화 된 제약조건은 제약조건을 검사하지 않는다.



◈ 예제

alter table class

add constraint class_cno_pk primary key (cno);

Table CLASS이(가) 변경되었습니다.


alter table student

add constraint student_cno_fk foreign key (cno) references class (cno);

Table STUDENT이(가) 변경되었습니다.


select table_name, constraint_name, status

from user_constraints

where table_name in ('CLASS', 'STUDENT');


insert into student values ('0417', '장보고', '03');


CLASS 테이블에 03반이 없으므로 입력되 에러가 발생한다.

제약 조건이 활성화 된 경우 반드시 부모 테이블로부터 데이터가 있어야 한다.


alter table student

disable constraint student_cno_fk;

Table STUDENT이(가) 변경되었습니다.


select table_name, constraint_name, status

from user_constraints

where table_name in ('CLASS', 'STUDENT');


insert into student values ('0417', '장보고', '03');

1 행 이(가) 삽입되었습니다.


FK를 임시로 비활성화 하면 제약 조건과 무관하게 데이터를 입력할 수 있다. 하지만, 위처럼 제약 조건에 위배되는 데이터를 삽입 후 제약 조건을 다시 활성화 시키는 것은 오류가 발생한다.



insert into class values ('03', '소프트웨어');

1 행 이(가) 삽입되었습니다.


alter table student

enable constraint student_cno_fk;

Table STUDENT이(가) 변경되었습니다.


drop table student cascade constraint;

Table STUDENT이(가) 삭제되었습니다.


drop table class cascade constraint;

Table CLASS이(가) 삭제되었습니다.



제약 조건은 데이터베이스의 무결성을 보장하는 기능이지만 작업의 순서를 반드시 제약 조건에 따라 계획해야 하는 문제가 있다. 위 예제의 경우 제약 조건을 비활성화 하고 데이터를 삽입하고 다시 제약 조건을 활성화 하는 작업을 했다. 사실 CLASS 테이블에 ('03', '소프트웨어') 데이터를 먼저 삽입하고 STUDENT 테이블에 ('0417', '장보고', '03') 데이터를 삽입하면 제약 조건을 활성화/비활성화 하는 귀찬은 작업은 하지 않아도 된다. 그럼에도 불구하고 제약 조건 활성화/비활성화를 사용하여야 할 때가 있다.

만일 입력 정보가 이미 무결성이 확인된 정보라면 이렇게 작업을 계획하는 과정은 매우 불편한 과정임에 틀림없다. 이런 경우 관련 제약 조건을 모두 비 활성화환 이후에 작업을 수행하면 테이블의 상하 관계와는 무관하게 작업 진행이 가능할 뿐 아니라 데이터 량이 많아 질수록 작업 시간을 더 많이 절약 할 수 있다. 특히, 여러 사람이 여러 테이블에 데이터를 삽입하는 작업을 할 때 유용하게 사용된다. 제약 조건이 활성화 돼 있으면 순서대로 데이터를 삽입해야 하므로 여러 사람이 같이 작업을 하는 의미가 없다.




 TIP    대규모 배치 작업을 효율적으로 수행하는 방법


1. 다이렉트 로드를 이용 작업을 수행한다.

2. 입력 테이블과 관련된 모둔 제약 조건을 비 활성화 한다.


● 제약 조건을 비 활성화 하면 작업의 순서를 지키지 않아도 되는 것 뿐 아니라 데이터를 삽입할 때 제약 조건을 검사하지 않고 삽입하기 때문에 입력 속도가 더 빠르다.

● 제약 조건에 맞지 않은 데이터는 입력이 끝난 이후 EXCEPTION 기능을 이용하여 걸러낼 수 있다.





샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.


PK(Primary Key)와 FK(Foreign Key)는 테이블의 필수 요소로써 모든 테이블은 이들 둘 중 하나 이상을 반드시 포함하고 있다.




No. 01

 

 

PRIMARY KEY(PK)


Primary Key 설정

CREATE TABLE 테이블(

CONSTRAINT 제약_조건_이름 PRIMARY KEY (컬럼)

);

CREATE TABLE 테이블(

컬럼 데이터타입 CONSTRAINT 제약_조건_이름 PRIMARY KEY,


  • 테이블을 생성할 때 PK를 정의한다.
  • PK는 각 행을고유하게 식별하는 역할을 담당한다.
  • 테이블당 하나만 정의 가능하다.
  • 지정된 컬럼에는 중복된 값이나 NULL값이 입력될 수 없다.
    • NOT NULL + UNIQUE(UK)를 한 것과 같은 기능을 한다.
  • PK로 지정 가능한 컬럼이 여러 개 있을 때는 검색에 많이 사용되고 간단하고 짧은 컬럼을 지정한다.
  • 주 식별자, 주키 등으로 불린다.
  • 고유 인덱스(Unique index)가 자동으로 생성된다.

◈ 예제

create table dept(

dno varchar2(14),

dname varchar2(14),

loc varchar2(8),

director varchar2(4),

constraint dept_dno_pk primary key (dno)

);

또는

create table dept(

dno varchar2(2) constraint dept_dno_pk primary key,

dname varchar2(14),

loc varchar2(8),

director varchar2(4)

);

Table DEPT이(가) 생성되었습니다.




No. 02

 

 

FOREIGN KEY(FK)


Foreign Key 설정

CREATE TABLE 테이블(

CONSTRAINT 제약_조건_이름 FOREIGN KEY (컬럼)

REFERENCES 참조할_테이블 (참조할_컬럼)

[ON DELETE CASCADE | ON DELETE SET NULL]

);

CREATE TABLE 테이블(

컬럼 데이터타입 CONSTRAINT 제약_조건_이름 FOREIGN KEY

REFERENCES 참조할_테이블 (참조할_컬럼)

[ON DELETE CASCADE | ON DELETE SET NULL]


  • 테이블을 생성할 때 FK를 정의한다.
  • FK가 정의된 테이블이 자식 테이블이다.
  • 참조되는 테이블을 부모 테이블이라고 한다.
  • 부모 테이블은 미리 생성되어 있어야 한다.
  • 부모 테이블의 참조되는 컬럼에 존재하는 값만을 입력 할 수 있다.
  • 부모 테이블은 FK로 인해 삭제가 불가능하다.
  • REFERENCES : 참조할 부모 테이블과 부모 테이블에 있는 컬럼을 정의한다.
  • ON DELETE CASCADE : 참조되는 부모 테이블의 행에 대한 DELETE를 허용한다.
    • 부모 테이블의 행이 지워지면 자식 테이블의 행도 같이 지워진다.
  • ON DELETE SET NULL : 참조되는 부모 테이블의 행에 대한 DELETE를 허요한다.
    • 부모 테이블의 행이 지워지면 자식 테이블의 행은 NULL 값으로 설정된다.
  • 데이터 타입이 반드시 일치해야 한다.
  • 참조되는 컬럼은 PK이거나 UK(Unique key)만 가능하다.
  • 외부키, 참조키, 외부 식별자 등으로 불린다.



insert into countries values ('KR', 'Korea', 3);

1 행 이(가) 삽입되었습니다.


insert into countries values ('NK', 'North Korea', 5);

오류 보고 -

ORA-02291: integrity constraint (HR.COUNTR_REG_FK) violated - parent key not found


COUNTRIES 테이블의 REGION_ID 컬럼은 REGIONS 테이블의 REGION_ID 컬럼을 참조한다.

즉, REGIONS 테이블이 부모 테이블이고 COUNTRIES 테이블이 자식 테이블이다. 자식 테이블에 데이터를 삽입할 때 부모 테이블의 참조하는 컬럼에 없는 값을 삽입하면 위와 같이 오류가 발생한다.



 TIP 

일반적으로 데이터베이스를 설계할 때 자식 테이블의 FK 컬럼은 부모 테이블의 참조하는 컬럼과 같은 이름을 사용한다. 다른 이름을 사용해도 무관하지만, 되도록이면 같은 이름을 사용하는 것이 테이블 간의 관계를 한 번에 알아볼 수 있기 때문에 좋다.



◈ 예제

create table emp(

eno varchar2(4),

ename varchar2(10),

job varchar2(6),

mgr varchar2(4),

hdate date,

sal number,

dno varchar2(2),

constraint emo_eno_pk primary key (eno),

constraint emp_eno_fk foreign key (dno) references dept (dno)

);

또는

create table emp(

eno varchar2(4) constraint emp_eno_pk primary key,

ename varchar2(10),

job varchar2(6),

mgr varchar2(4),

hdate date,

sal number,

dno varchar2(2) constraint emp_dno_fk references dept (dno)

);

Table EMP이(가) 생성되었습니다.


컬럼 레벨로 정의할 때는 foreign key를 생략해도 무관하다.


drop table dept cascade constraint;

Table DEPT이(가) 삭제되었습니다.


drop table emp cascade constraint;

Table EMP이(가) 삭제되었습니다.




테이블 생성 예제


create table employees(

employee_id number(6),

first_name varchar2(20),

last_name varchar2(25) constraint emp_last_name_nn not null,

email varchar2(25) constraint emp_email_nn not null,

phone_number varchar2(20),

hire_date date constraint emp_hire_date_nn not null,

job_id varchar2(10) emp_job_nn not null,

salary number(8, 2)

commission_pct number(2, 2),

manager_id number(6),

department_id number(4),

constraint emp_employee_id primary key (employee_id),

constraint emp_email_uk unique (email),

constraint emp_salary_ck check (salary > 0),

constraint emp_dept_fk foreign key (department_id)

references departments (department_id)

);

우리가 사용하고 있는 HR 스키마의 EMPLOYEES 테이블은 위와 같은 문장으로 생성되었다.





No. 03

 

 

제약 조건 조회


오라클에서만 해당되는 내용이다. DB 종류마다 다르므로 오라클을 사용할 경우만 참고 하자.



제약 조건 조회

SELECT c.table_name, c.constraint_name, c.constraint_type, s.column_name

FROM user_constraints c, user_cons_columns s

WHERE c.constraint_name = s.constraint_name

AND c.table_name in (검색_대상_테이블_목록)

ORDER BY c.table_name;

  • 테이블에 정의된 제약 조건을 검색한다.
  • constraint_name : 제약 조건 이름
  • constraint_type : 제약 조건 타입
    • P(PK), R(FK), U(UK), C(NOT NULL, CHECK)


◈ 예제

select c.table_name, c.constraint_name, c.constraint_type, s.column_name

from user_constraints c, user_cons_columns s

where c.constraint_name = s.constraint_name

and c.table_name in ('EMPLOYEES', 'DEPARTMENTS')

order by c.table_name;



참조하는 테이블 목록 조회

SELECT p.table_name 부모테이블, p.constraint_name 부모제약조건,

c.table_name 자식테이블, c.constraint_name 참조제약조건 

FROM user_constraints p, user_constraints c

WHERE c.r_constraint_name = p.constraint_name

AND p.table_name in (검색_대상_테이블_목록)

ORDER BY p.table_name;

  • 지정한 테이블을 참조하는 테이블의 목록을 확인한다.
  • 자식 테이블이 지정되어 있으면 삭제가 불가능하다.
    • ON DELETE 옵션이 설정되어 있는 경우는 삭제 가능


◈ 예제

select p.table_name 부모테이블, p.constraint_name 부모제약조건,

c.table_name 자식테이블, c.constraint_name 참조제약조건

from user_constraints p, user_constraints c

where c.r_constraint_name = p.constraint_name

and p.table_name in ('EMPLOYEES', 'DEPARTMENTS', 'LOCATIONS')

order by p.table_name;



CHECK와 NOT NULL 제약 조건 검색

SELECT constraint_name, search_condition

FROM user_constraints

WHERE table_name = '테이블';

  • NOT NULL과 CHECK 제약 조건의 내용을 검색한다.
  • 이외 제약 조건은 널 값이다.


◈ 예제

select constraint_name, search_condition

from user_constraints

where table_name = 'EMPLOYEES';




샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.




No. 01

 

 

제약 조건


제약 조건이란 테이블 단위에서 데이터의 무결성을 보장해주는 규칙이다. 제약 조건은 테이블에 데이터가 입력, 수정, 삭제되거나 테이블이 삭제, 변경될 경우 잘못된 트랜잭션이 수행되지 않도록 결함을 유발할 가능성이 있는 작업을 방지하는 역할을 담당한다.


◆ 테이블 단위에서 정의되고 적용된다.

◆ 속성이 존재하는 겨우 테이블의 삭제를 막아준다.

◆ 자료가 삽입, 갱신, 삭제될 때마다 규칙이 적용된다.

◆ 일시적으로 활성화하거나 비활성화하는 것이 가능하다.

◆ USER_CONSTRAINTS, USER_CONS_COLUMNS 딕셔너리에서 검색한다.

◆ 제약 조건은 개체처럼 관리되므로 반드시 이름이 필요하다. 제약 조건에 이름을 정의하지 않으면 오라클 서버가 자동으로 SYS_Cn형태의 이름을 붙인다.

◆ 제약 조건은 다음 두 경우에 생성할 수 있다.

 - 테이블이 생성될 때 동시에

 - 테이블이 만들어진 후


오라클에서 제공되는 제약조건

  • NOT NULL
  • UNIQUE KEY(UK)
  • PRIMARY KEY(PK)
  • FOREIGN KEY(FK)
  • CHECK


기본 구조

CREATE TABLE [스키마.]테이블이름(

컬럼 데이터타입 [DEFAULT default값] [컬럼 레벨 제약조건],

[테이블 레벨 제약조건],

);

컬럼 레벨 제약조건

컬럼 [CONSTRAINT 제약_조건_이름] 제약조건,

테이블 레벨 제약조건

컬럼, …

[CONSTRAINT 제약_조건_이름] 제약조건 (컬럼),

제약 조건을 정의하는 방법은 두가지 방법이 제공된다.

· 컬럼 레벨 정의 : 하나의 컬럼을 정의하면서 같이 정의하는 방법

· 테이블 레벨 정의 : 테이블 생성 명령어 마지막에 기술하는 방법

둘 중 어떤 것을 사용해도 상관없지만 테이블 레벨 정의를 사용하는 것이 가독성이 높다.

주의할 것은 NOT NULL 제약조건은 컬럼 레벨 정의만 할 수 있다.


제약 조건 이름

제약조건에 이름을 붙이지 않아도 오라클 서버가 자동으로 생성하지만 직접 이름을 붙여주는 것이 좋다. 제약조건 이름에는 테이블이름, 컬럼이름, 제약조건이 포함되도록 하는 것이 좋다. 나중에 제약 조건 이름만 보더라도 어떤 테이블의 어떤 컬럼에 제약조건이 있는지 한눈에 알아 볼 수 있도록 하기 위해서이다. '테이블이름_컬럼이름_제약조건'과 같이 짓는 것이 좋다.

위처럼 이름을 지으면 여러 사람이 같이 작업할 때 다른 사람이 만든 테이블이라도 한 번에 알아 볼 수 있다. 제약조건이름을 꼭 지어야 한다고 하지는 않지만 실무에서는 암묵적으로 위와 같은 형태로 이름을 지어놓는 것이 예의이다.


◈ 예제

▶ 컬럼 레벨 제약조건 :

create table emp(

eno number(6) constraint emp_eno_pk primary key,

first_name varchar2(20),

gender varchar2(2) constraint emp_gender_nn not null

);

Table EMP이(가) 생성되었습니다.


drop table emp cascade constraint;

Table EMP이(가) 삭제되었습니다.



▶ 테이블 레벨 제약조건 :

create table emp(

eno number(6),

first_name varchar2(20),

gender varchar2(2) constraint emp_gender_nn not null,

constraint emp_eno_pk primary key (eno)

);

Table EMP이(가) 생성되었습니다.


drop table emp cascade constraint;

Table EMP이(가) 삭제되었습니다.




No. 02

 

 

NOT NULL


NOT NULL 설정

CREATE TABLE 테이블(

컬럼 데이터타입 CONSTRAINT 제약_조건_이름 NOT NULL,


  • 테이블 레벨 정의가 불가능하다.
  • 지정된 컬럼은 널 값을 허용하지 않는다.
  • 반드시 널 값이 필요한 컬럼을 제외하고 가능한 모든 컬럼에 널 값을 허용하지 않는 것이 좋다.



insert into departments(department_id, manager_id, location_id)

values (400, 200, 1700);

오류 보고 -

ORA-01400: cannot insert NULL into ("HR"."DEPARTMENTS"."DEPARTMENT_NAME")


DEPARTMENT_NAME 컬럼에는 NOT NULL 제약조건이 정의되어 있다.

그러므로 DEPARTMENT_NAME 컬럼에는 값을 넣지 않으면 위와 같은 오류가 발생한다.




No. 03

 

 

UNIQUE KEY(UK)


Unique Key 설정

CREATE TABLE 테이블(

CONSTRAINT 제약_조건_이름 UNIQUE (컬럼) );

CREATE TABLE 테이블(

컬럼 데이터타입 CONSTRAINT 제약_조건_이름 UNIQUE,


  • UK(Unique Key)를 정의한다.
  • 중복된 값을 허용하지 않지만 여러 개의 널 값은 허용한다.
    • 널 값은 비교가 불가능함으로 여러 개가 있어도 중복된 값이 아니다.
  • 고유키로 불린다.
  • 고유 인덱스(Unique Index)가 자동으로 생성된다.


위와 같이 테이블에 UNIQUE 제약조건이 정의되어 있는 컬럼이 있다고 할 때, 위의 두 데이터를 순서대로 넣는다고 하자. 첫 번째 데이터는 오류없이 삽입될 것이지만, 두 번째 데이터는 오류가 발생할 것이다. 첫 번째 데이터를 삽입했을 때 이미 'JSMITH'라는 EMAIL이 존재하기 때문에 두 번째 데이터의 'JSMITH' EMAIL은 UNIQUE 제약조건에 위배되기 때문이다.


◈ 예제

create table emp(

employee_id number(6),

last_name varchar2(20) not null,

email varchar2(25),

constraint emo_email_uk unique (email)

);

또는

create table emp(

employee_id number(6)

last_name varchar2(20) not null,

email varchar2(25) constraint emo_email_uk unique

);

Table EMP이(가) 생성되었습니다.


insert into emp values (208, 'Smith', 'JSMITH');

1 행 이(가) 삽입되었습니다.


insert into emp values (209, 'Asumin', 'JSMITH');

오류 보고 -

ORA-00001: unique constraint (HR.EMO_EMAIL_UK) violated


drop table emp cascade constraint;

Table EMP이(가) 삭제되었습니다.




No. 04

 

 

CHECK


Check 설정

CREATE TABLE 테이블(

CONSTRAINT 제약_조건_이름 CHECK (조건) );

CREATE TABLE 테이블(

컬럼 데이터타입 CONSTRAINT 제약_조건_이름 CHECK (조건),


  • 행에 입력될 데이터의 조건을 정의한다.
  • 조건은 WHERE 절에 기술하는 조건 형식과 동일하다.
  • 다음과 같은 표현은 허용되지 않는다.
    1. CURRVAL, NEXTVAL, LEVEL, ROWNUM pseudocolumn 에 대한 참조
    2. SYSDATE, UID, USER, USERENV 함수에 대한 호출]
    3. 다른 행의 다른 값을 참조하는 질의


◈ 예제

create table emp(

employee_id number(6),

sal number(8, 2),

last_name varchar2(20) not null,

constraint emp_sal_ck check (sal > 1000)

);

또는

create table emp(

employee_id number(6),

sal number(8, 2) constraint emp_sal_ck check (sal > 1000),

last_name varchar2(20) not null

);

Table EMP이(가) 생성되었습니다.


insert into emp values (208, 1200, 'SMAT');

1 행 이(가) 삽입되었습니다.


insert into emp values (209, 900, 'ARIS');

오류 보고 -

ORA-02290: check constraint (HR.EMP_SAL_CK) violated


drop table emp cascade constraint;

Table EMP이(가) 삭제되었습니다.




NOT NULL, UK, CHECK 사용 예제


create table emp(

eno number(4),

ename varchar2(20) constraint emp_ename_nn not null,

gno varchar2(13),

gender varchar2(5),

constraint emp_gno_uk unique (gno),

constraint emp_gno_ck check (length(gno) = 13),

constraint emp_gender_ck check (gender in ('woman', 'man'))

);

Table EMP이(가) 생성되었습니다.


drop table emp cascade constraint;

Table EMP이(가) 삭제되었습니다.




샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.


DB에서 데이터를 저장하는 가장 기본적인 구조는 테이블(TABLE)이다. 오라클에는 테이블 이외에 뷰(VIEW), 인덱스(INDEX), 시퀀스(SEQUENCE)등 여러 개체가 있다.




No. 01

 

 

테이블 생성에서 이름 규칙


  1. 문자로 시작한다.
  2. 30자 이내로 한다.
  3. 영문(A-Z, a-z), 숫자, _, $, #만을 사용한다.
    • 한글 사용은 가능하지만 되도록 사용하지 않는 것이 좋다.
  4. 객체는 동일한 유저(스키마) 안에서 유일해야 한다.
  5. 예약어는 사용이 불가능하다.
    • 오라클에서 명령어로 사용하고 있는 INSERT, DELETE 등 …
  6. 대소 문자를 구별하지 않는다.
    • 생성할 때 사용한 문자와는 관계없이 모든 이름은 대문자로 저장된다.
    • 예) eMPloyees, eMpLOYEES 등으로 지정하여도 EMPLOYEES로 저장된다.



No. 02

 

 

테이블 생성


기분 구조

CREATE TABLE [스키마.]테이블이름(

컬럼 데이터타입 [DEFAULT default값] [컬럼 레벨 제약조건],

컬럼 데이터타입 [DEFAULT default값] [컬럼 레벨 제약조건],

[테이블 레벨 제약조건],

);

  • 데이터 타입
    • 컬럼에 입력될 데이터의 종류와 크기글 결정한다.
  • DEFAULT
    • 입력이 누락됐을 때 기본 입력 값을 정의한다.
    • Default 값을 지정하지 않으면 널 값이 저장된다.
  • 컬럼 레벨 제약 조건
    • PK, FK, UK, CHECK, NOT NULL 등을 지정한다.
  • 테이블 레벨 제약 조건
    • PK, FK, UK, CHECK만 지정한다.
    • NOT NULL은 정의할 수 없다.

제약 조건의 설정 및 관리 방법은 다음 포스트부터 자세히 다룬다.


◈ 예제

create table example1 (

depno number(2),

dname varchar2(14),

loc varchar2(13),

start_date date default sysdate

);

Table EXAMPLE1이(가) 생성되었습니다.


desc example1;




No. 03

 

 

데이터 타입


오라클은 다양한 데이터 타입을 제공하는데 다음은 그 중 많이 사용되는 데이터 타입들이다.

♠ 문자 타입

 - VARCHAR2, CHAR, LONG ,CLOB

♠ 숫자 타입

 - NUMBER

♠ 날짜 타입

 - DATE

♠ 이진 타입

 - RAW, LONG RAW, BLOB, BFILE

♠ ROWID 타입

 - ROWID

이 중에서도 VARCHAR2, NUMBER, DATE 이 세 가지가 99%이상 사용된다.


 Data Type

설  명 

 VARCHAR2(size)

 가변 길이 문자 타입 (1 < n < 4000 byte)

 CHAR(size) 

 고정 길이 문자 타입 (1 < n < 2000 byte)

 NUMBER(n,p)

 숫자 타입, n : 전체 자리수  p : 소수점 이하 자리수

 전체 자리수를 초과하는 경우 입력 거부되지만 소수점 이하 자리수가 초과되면 반올림되어 입력된다.

 소수점 이하의 값이 없는 경우는 길이를 지정하지 않는 것이 좋다.  

 DATE

 날짜 타입, 출력이나 입력 형식과 무관하게 YYYY/MM/DD:HH24:MI:SS 형태로 저장된다.

 (기원전 4712년 1월1일 ~ 서기 9999년 12월 31일)

 LONG

 2GB까지 저장 가능한 가변길이 문자 타입, 단 조건 검색할 수 없다.

 테이블에는 하나의 LONG 컬럼만 정의할 수 있다. 

 CLOB

 LONG를 개선한 타입, 최대 4G까지 저장 가능하고 한 테이블의 여러 컬럼에 정의할 수 있다. 

 RAW(size)

 가변길이 이진 타입 (n < 2000) 

 LONG RAW

 가변길이 이진 타입, 최대 2G까지 저장 가능하다. 

 BLOB

 4G까지 저장 가능한 가변길이 이진 타입 

 BFILE

 외부 파일 저장을 위한 이진 타입, 4G 이내 파일을 저장한다. 

 ROWID

 ROWID를 저장하기 위한 데이터 타입

 주로 PLSQL 프로그래밍에서 많이 사용되며 각 문자는 62진수로 엔코딩(encoding) 되어있다.


Guidelines

  • LONG 타입 컬럼은 서브쿼리를 사용하여 테이블을 생성할 때 복사되지 않는다.
  • LONG 타입 컬럼은 GROUPY BY나 ORDER BY절에 포함될 수 없다.
  • LONG 타입 컬럼에는 제약조건이 정의하지 못한다.



 TIP 


CHAR vs. VARCHAR2

CHAR와 VARCHAR2는 둘 다 문자를 저장하지만 저장된 결과는 서로 다를 수 있다. CHAR(10)와 VARCHAR2(10)에 'ABC'를 저장하면 CHAR(10)에는 10Byte가 저장되고 VARCHAR2(10)에는 3byte만 저장된다. CHAR 타입은 저장되는 입력 값과 상관없이 고정된 길이의 문자열을 갖는 반면에 VARCHAR2 타입은 입력 데이터만큼의 길이만 저장된다.


create table comp (

char_col char(4),

varchar2_col varchar2(4)

);

Table COMP이(가) 생성되었습니다.


insert into comp values ('AA', 'AA');

1 행 이(가) 삽입되었습니다.


insert into comp values ('AAAA', 'AAAA');

1 행 이(가) 삽입되었습니다.


select * from comp;


CHAR_COL 과 VARCHAR2_COL 모두에 동일한 데이터가 저장된 것처럼 보이지만 다음 예제를 보면 다르다는 것을 알 수 있다.


select * from comp where char_col = 'AA';


select * from comp where varchar2_col = 'AA';


select * from comp where char_col = varchar2_col;


두 컬럼이 동일한 데이터가 저장된 행을 검색했지만 'AA'인 행은 검색되지 않는다.



CHAR 타입은 빈 공간을 차지하고 있어 위의 경우는 'AA  '와 같다.


문자열을 비교할 때 CHAR 타입과 문자열을 비교하거나 CHAR 타입 간에 비교하는 경우는 이런 문제가 나타나지 않는다. 길이가 짧은 쪽을 긴 쪽과 길이를 맞추어 비교하기 때문이다. 예를 들어 CHAR_COL의 'AA  '를 문자열 'AA'와 비교하면 문자열에 공백 두 개를 추가하여 'AA  '으로 맞추고 비교한다.

그러나 CHAR와 VARCHAR2형과 비교하는 경우는 이런 과정을 거치지 않고 비교한다. 그래서 같은 문자열이라도 다르다고 판단한다.

이런 이유로 CHAR 타입은 고정된 데이터 길이를 가지고 컬럼에만 지정하는 것을 추천한다.

예) 주민등록번호, 학번 …


DATE 타입의 이해

데이터 타입이 DATE타입인 경우 날짜를 입력하는 가장 흔한 방법은 sysdate를 이용하는 것이다.

그러나 이와 같은 날짜 입력은 나중에 특정일을 검색할 때 문제를 일으킬 수 있다.


insert into example1(start_date) values (sysdate);

1 행 이(가) 삽입되었습니다.


select start_date from example1;


select start_date from example1 where start_date = '2017/12/15';


위와 같이 입력에 sysdate를 이용하는 경우 화면에 대부분 YYYY/MM/DD 만을 검색하지만 실제 저장된 데이터는 YYYY/MM/DD/HH:24MI:SS 이므로 start_date='2017/12/15'와 같은 조건 검색을 수행할 때 원치 않는 결과가 나올 수 있다. 이것은 오라클이 start_date='2017/12/15'을 해석할 때 stat_date='2017/12/15:00:00:00'으로 해석하기 때문이다.

실제 저장된 데이터를 보면


select to_char(start_date, 'YYYY/MM/DD:HH24:MI:SS') from example1;



▶ 검색 1: '2017/12/15:00:00:00' 부터 '2017/12/16:00:00:00'까지 검색한다.

select start_date

from example1

where start_date between '2017/12/15' and '2017/12/16';


▶ 검색2

select start_date

from example1

where start_date like '17/12/15%';


 위와 같은 두 가지 방법으로 특정일을 검색할 수 있다. 그러나 검색2의 방법 같은 경우 날짜형인 start_date 컬럼에 LIKE와 같은 문자형 연산을 수행하면 데이터를 문자형으로 자동 형 변환하기 때문에 대용량 테이블에서 검색의 성능을 저해할 가능성이 있다.

이런 문제를 해결하는 방법은 여러 가지가 있지만 가장 간단한 해결 방법은 데이터 타입을 DATE로 정의하지 않고 VARCHAR2(10)이나 CHAR(10)으로 정의하는 것이다. 이렇게 정의하고 sysdate로 날짜를 입력하는 경우 시분초는 입력되지 않는다. 단, 시분초가 중요한 의미가 잇는 경우는 사용하지 않는 것이 좋다. 그리고 또 한가지 방법은 데이터 타입을 DATE 그대로 이용하고 날짜를 입력할 때 항상 TRUNC(sysdate)를 이용하는 것이다. 시분초가 항상 00:00:00으로 결정됨으로 위의 여러 가지 문제를 피할 수 있다.





No. 04

 

 

테이블 삭제


기본 구조

DROP TABLE [스키마.]테이블이름 [CASCADE CONSTRAINT];

  • 테이블을 삭제한다.
  • CASCADE CONSTRAINT : 테이블이 다른 테이블로부터 참조되는 경우 해당 제약조건을 먼저 삭제한 후 테이블을 삭제한다.
  • 한번 실행되면 roll back 되지 않기 때문에 사용에 주의한다.


◈ 예제

drop table example1;

Table EXAMPLE1이(가) 삭제되었습니다.




샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.




No. 01

 

 

서브 쿼리를 사용한 INSERT 문 & 다이렉트 로드(Direct load)


기본 구조

INSERT INTO [/*+APPEND */] 테이블 [NOLOGGING] [(컬럼, 컬럼, …]

SELECT 문장

  • 한 번에 여러 행을 입력할 수 있다.
  • 서브 쿼리에 검색된 행을 입력 값으로 사용한다.
  • 컬럼 목록과 SELECT 문의 컬럼이 1:1 대응돼야 한다.
  • /*+APPEND */ : 다이렉트 로드, 대량의 입력 작업을 더 빠르게 수행한다.
  • [NOLOGGING] : 로그 정보를 남기지 않음으로 입력 작업이 빨라진다. (장애 발생시 복구 불가능)

◈ 예제

  desc departments

  desc copy_departments

 

 


 


 


insert into copy_departments(department_id, department_name, manager_id)

select department_id, department_name, manager_id

from departments

where department_name = 'IT Helpdesk';

1 행 이(가) 삽입되었습니다.


INSERT 문장을 실행할 때는 항상 테이블의 구조를 파악하고 작업해야 한다.

위의 경우는 같은 구조의 테이블을 만들었기 때문에 제약조건을 제외하고는 컬럼의 이름, 데이터 타입이 같지만 컬럼의 이름은 달라도 상관없다. 그러나 반드시 데이터 타입은 같아야 한다. 데이터 타입은 같아야 하지만 데이터의 크기는 달라도 상관없다. 다만 주의할 것은 입력되는 데이터 크기보다 입력될 컬럼의 크기는 같거나 더 커야한다.



▶ 다른 테이블의 전체 데이터 삽입

insert into copy_departments

select *

from departments;

29개 행 이(가) 삽입되었습니다.


서브 쿼리에서 WHERE 절을 생략하면 그 테이블의 모든 데이터를 삽입한다.



다이렉트 로드(Direct load)

DML 작업은 모두 메모리에서 이루어진다. 오라클의 경우 SGA라는 메모리 영역 내에 데이터베이스 버퍼 캐시(Database Buffer Cache)라는 영역을 작업 공간으로 이용하여 이를 통해 디스크 접근 횟수와 량을 줄여 성능을 향상시킨다. 쉽게말해 DML 작업은 메모리에서 먼저 이러우진 후 트랜잭션이 종료된 이후에 실제 디스크에 저장시킨다.(메모리의 속도가 더 빠르기 때문에 성능 향상을 위해) 그러나 대량의 DML 작업을 할 경우 오히려 이 방식이 성능을 저하시킬 때가 있다.

예를 들어 메모리의 용량보다 더 큰 DML 작업을 할 경우 메모리의 용량만큼 작업이 이루어지고 이를 디스크에 반영한 후 다시 메모리를 정리하고 작업을 하는 비효율적인 작업이 이루어진다. 이런 문제를 피하기 위해 메모리를 통하지 않고 디스크에 직접 작업을 수행하는 것을 다이렉트 로드라 한다. DML작업을 다이렉트 로드하기 위해서는 '/*+ APPEND */'를 문장에 삽입한다. 그리고 디스크에 직업 저장시킨다고해서 트랜잭션이 자동으로 종료되는 것이 아니기 때문에 INSERT 작업이 끝나면 반드시 트랜잭션을 종료(COMMIT) 하자.

다이렉트 로드로 INSERT 작업을 할 경우에는 좋은 점만 있는 것은 아니다. 다음과 같은 단점도 존재한다. 다이렉트 로드를 할 때 테이블에 할당되었지만 현재는 빈영역이 있더라도, 테이블에 새롭게 영역을 할당해 INSERT한다. 이로 인해, 통상의 INSERT문보다 많은 디스크 영역을 사용하는 일이 있다. 그리고 ISNERT 작업이 종료할 때까지 다른 트랜잭션에서 INSERT/UPDATE/DELETE 작업을 동시에 할 수 없다.


NOLOGGING 옵션

오라클은 안전하게 시스템을 운영을 하기 위해 장애 발생 시 복구할 수 있도록 데이터베이스에 변경을 가하는 모든 작업의 내역을 Redo log 영역에 로그 정보로 보관한다. 예를 들면 INSERT 작업을 하면 데이터를 삽입하는 작업도 하지만 동시에 Redo log 영역에 INSERT 작업이 실행되었다는 정보를 저장하는 작업도 한다. 그러나 다이렉트 로드처럼 대량의 DML 작업의 효율을 위해 이를 수행하지 않을 수 있다. 작업 성능을 위해 로그 정보를 남기지 않겠다고 'NOLOGGING' 옵션을 이용하면 된다. 그러나 장애가 발생하여 작업한 데이터들이 손상된 경우에 Redo log 영역의 로그 정보를 이용하여 복구를 하는데 로그 정보를 남기지 않았다면 복구가 불가능하다. 작업 성능은 빨라지지만 작업의 안전도는 떨어진다고 할 수 있다. 그러므로 복구가 불가능하도록 작업한 이후에는 장애에 대비한 백업을 잊지 않아야 한다.



truncate table copy_departments;

Table COPY_DEPARTMENTS이(가) 잘렸습니다.


insert /*+ APPEND */ into copy_departments nologging (department_id, location_id)

select department_id, location_id

from departments;

29개 행 이(가) 삽입되었습니다.


commit;

커밋 완료.




No. 02

 

 

서브 쿼리를 사용한 UPDATE 문


기본 구조

UPDATE 테이블

SET 컬럼명 = (SELECT 문장),

 컬럼명 = (SELECT 문장), …

[WHERE 조건];

UPDATE 테이블

SET (컬럼, 컬럼, …) = (SELECT 문장)

[WHERE 조건];

  • 서브 쿼리의 결과 행은 반드시 하나여야 한다.
  • 서브 쿼리에서 검색되는 데이터는 반드시 수정되는 컬럼과 1:1 대응돼야 한다.


◈ 예제

▶ 사원번호 115사원의 직업과 급여를 205 사원의 직업, 124 사원의 급여와 동일하게 수정한다.

update employees

set job_id = (select job_id

      from employees

      where employee_id = 205),

     salary = (select salary

            from employees

            where employee_id = 124)

where employee_id = 115;

1 행 이(가) 업데이트되었습니다.


select employee_id, first_name, job_id, salary

from employees

where employee_id in (115, 124, 205);


rollback;

롤백 완료.



▶ 사원번호 115사원의 직업과 급여를 205 사원의 직업, 급여와 동일하게 수정한다.

[방법1]

update employees

set job_id = (select job_id

      from employees

      where employee_id = 205),

      salary = (select salary

      from employees

      where employee_id = 205)

where employee_id = 115;


[방법2]

update employees

set (job_id, salary) = (select job_id, salary

      from employees

      where employee_id = 205)

where employee_id = 115;

1 행 이(가) 업데이트되었습니다.




No. 03

 

 

서브 쿼리를 사용한 DELETE 문


기본 구조

DELETE FROM 테이블

WHERE 컬럼명 = (SELECT 문장);

  • 서브 쿼리의 결과 행은 반드시 하나여야 한다.


◈ 예제

delete from copy_departments

where department_id = (select department_id

  from employees

  where first_name = 'Bruce');

1 행 이(가) 삭제되었습니다.


Bruce가 속한 부서를 copy_departments 테이블에서 삭제하는 작업이다.


DML 문장은 서브 쿼리를 사용하여 다른 테이블의 데이터들도 이용할 수 있다.


HR 스키마에 원래 존재하던 테이블에 대해서 DML 작업을 하는 것은 많은 제약 조건들 때문에 에러를 발생시키는 경우가 많다. DML 작업을 할 때는 어떤 제약 조건이 걸려있는지 등을 판단하여 수행해야지만 순조롭게 작업을 진행할 수 있을 것이다. 제약조건과 이 제약 조건들의 관리에 대해서는 곧 배워보도록 하겠다.




샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.


DML(Date Manipulation Language)

데이터베이스에 데이터를 입력, 수정, 삭제하는 명령어이다.

데이터베이스의 내용을 변경하는 작업을 수행합으로 주의 깊게 실행해야 한다.




No. 01

 

 

INSERT


기본 구조

INSERT INTO 테이블 [(컬럼, 컬럼, …)]

VALUES (값, [값, …]);

  • 하나의 행 만을 삽입한다.
  • 각 컬럼과 VALUES 절의 값은 반드시 1:1 대응된다.
  • 테이블의 모든 컬럼에 값을 입력하는 경우 컬럼 목록을 생략 할 수 있다.
  • INSERT문장에서 생략된 컬럼은 널 값이 입력된다.
  • 값 대신 DEFAULT를 기술하면 테이블에 정의된 DEFAULT 값이 입력된다.
  • 입력되는 값이 숫자가 아닌 경우 단일 인용부호(')를 사용한다.


◈ 예제

insert into departments(department_id, department_name, manager_id, location_id)

values (300, 'Game', 100, 1700);

insert into departments(manager_id, location_id, department_id, department_name)

values (100, 1700, 300, 'Game');

1 행 이(가) 삽입되었습니다.


위 두 예제는 동일한 INSERT문장이다.

컬럼 목록의 순서대로 쓰지 않아도 상관없지만, 컬럼 목록과 VALUES절의 값은 1:1 대응되어야 한다.


commit;

커밋 완료.


select * from departments order by department_id desc;


INSERT 문장이 성공적으로 완료됐음을 볼 수 있다.

물론 COMMIT 하지 않아도 현재 메모리상에서 SELECT 되지만, COMMIT하지 않고 데이터베이스를 껐다가 키면 검색되지 않을 것이다.



▶ 모든 컬럼에 값을 입력하는 경우

insert into departments

values (301, 'Game_1', 100, 1700);

1 행 이(가) 삽입되었습니다.


commit;

커밋 완료.


모든 컬럼에 값을 입력하는 경우 컬럼 목록을 생략 할 수 있다.

VALUES절에 기술하는 값의 순서는 DESC명령으로 확인된 테이블의 컬럼 순서와 일치해야 한다.

desc departments;



▶ 일부 컬럼의 생략

insert into departments(department_id, department_name)

values (302, 'DB_Executive');

1 행 이(가) 삽입되었습니다.


commit;

커밋 완료.


위의 결과를 보기 위해서 검색해보면


생략했던 manager_id, location_id 컬럼의 값이 자동으로 널 값이 입력된 것을 볼 수 있다.



▶ 특별한 값의 삽입

insert into employees(employee_id, last_name, email, hire_date, job_id)

values (207, 'Lee', 'KeepCool', sysdate, 'IT_PROG');

1 행 이(가) 삽입되었습니다.


select * from employees order by department_id desc;


위의 경우에는 hire_date 컬럼에 날짜대신 SYSDATE 함수를 기술하였다. 검색해보면 알겠지만 함수의 기능대로 현재의 날짜가 삽입되었다.

VALUES절의 값 대신 함수를 사용할 수 있다. 단 컬럼의 Type에 맞게 기술해야 한다.




No. 02

 

 

UPDATE


기본 구조

UPDATE 테이블

SET 컬럼 = 값 [, 컬럼 = 값, …]

[WHERE 조건];

  • 컬럼의 값을 수정한다. (기존의 행의 값 수정)
  • 여러 행의 값을 동시에 수정할 수 있다.
  • WHERE절을 생략하면 지정된 컬럼의 모든 행의 값이 수정된다.
  • 값 대신에 DEFAULT를 기술하면 테이블에 정의된 DEFAULT 값으로 변경된다.


◈ 예제

update departments

set location_id = 1800

where department_id = 302;

1 행 이(가) 업데이트되었습니다.


select * from departments order by department_id desc;


아까 삽입한 302번 부서의 location_id는 널 값이었지만, 1800으로 수정된 것을 볼 수 있다.

추가로 여기서 ROLLBACK을 실행하고 결과를 확인해보자.


rollback;

롤백 완료.


select * from departments order by department_id desc;


ROLLBACK을 통해 수정 작업을 취소했음으로 데이터가 다시 복원된다.

[SQL 19]에서도 설명했듯이 ROLLBACK은 트랜잭션 시작전으로 돌아가므로 COMMIT 되지 않은 다른 명령까지 다시 복원되므로 주의하도록 하자.



▶ 모든 행의 수정

update departments

set manager_id = 100;

30개 행 이(가) 업데이트되었습니다.


select * from departments order by department_id desc;


WHERE 절을 생략할 경우 지정된 컬럼의 모든 행의 값이 수정된다.


rollback;

롤백 완료.



▶ 여러 행의 수정

update employees

set job_id = 'ST_CLERK', salary = 5000

where employee_id = 114;

1 행 이(가) 업데이트되었습니다.


사원번호 114 사원의 job_id, salary를 수정했다. 위 예제와 같이 여러 컬럼의 행의 값을 수정할 수 있다.


select employee_id, job_id, salary from employees where employee_id = 114;


rollback;

롤백 완료.




No. 03

 

 

DELETE


기본 구조

DELETE FROM 테이블

[WHERE 조건];

  • WHERE 절의 조건에 해당하는 행단위로 데이터를 삭제한다.
  • 조건이 없는 경우 테이블의 모든 행을 삭제한다.
  • 테이블을 삭제해도 테이블의 물리적인 구조는 변하지 않는다.


◈ 예제

delete from departments

where department_id = 301;

1 행 이(가) 삭제되었습니다.


commit;

커밋 완료.



▶ 조건이 없는 경우

현재의 테이블은 제약조건 때문에 전체데이터를 삭제하지 못하기 때문에 임시로 만들어서 해보도록 하겠다. 나중에 배울 것이기 때문에 지금은 일단 따라서 테이블을 만들어 보도록 하자. 테이블을 만드는 문장은 아직은 이해할 필요는 없다. 간단히만 설명하자면, DEPARTMENTS 테이블과 같은 구조지만 제약조건이 없는 테이블을 만들고 DEPARTMENTS 테이블의 모든 데이터를 삽입한 것이다.


create table copy_departments ( department_id number(4,0),

  department_name varchar2(30 byte),

  manager_id number(6,0),

  location_id number(4,0));

Table COPY_DEPARTMENTS이(가) 생성되었습니다.


insert into copy_departments

select * from departments;

29개 행 이(가) 삽입되었습니다.


commit;

커밋 완료.


select * from copy_departments;




delete from copy_departments;

29개 행 이(가) 삭제되었습니다.


select * from copy_departments;


아무것도 검색되지 않는다.

조건을 사용하지 않으면 테이블의 모든 행이 삭제된다.

착각하지 말아야 할 것은 테이블은 삭제되는 것이 아니고, 테이블은 그대로 남아있지만 테이블의 데이터만 삭제된 것이다!

테이블이 남아 있다는 것은 데이터를 다시 삽입할 수 있다는 뜻이다.


rollback;

롤백 완료.




No. 04

 

 

TRUNCATE


기본 구조

TRUNCATE TABLE 테이블;

  • 테이블의 모든 행이 삭제되지만, 테이블의 구조는 변하지 않는다.
  • DML 문장이므로, ROLLBACK으로 복원되지 않는다.
  • 조건이 없는 DELETE 문장보다 속도가 빠르다. (Rollback 정보를 만들지 않기 때문에)


◈ 예제

truncate table copy_departments;

Table COPY_DEPARTMENTS이(가) 잘렸습니다.


select * from copy_departments;


조건이 없는 DELETE문과 마찬가지로 아무것도 검색되지 않지만, 테이블 구조는 남아있다.


rollback;

롤백 완료.


select * from copy_departments;


Rollback 했음에도 불구하고 DELETE문과 다르게 데이터가 복원되지 않았다. 이유는 TRUNCATE가 DDL문장이기 때문이다. 전에도 말했지만, DML 문장을 제외하고는 COMMIT, ROLLBACK의 사용이 무의미하다. DML 문장을 제외한 나머지는 문장이 실행됨과 동시에 COMMIT 된 상태와 같다.



DELETE vs. TRUNCATE

TRUNCATE 문장은 조건이 없는 DELETE 문장과 동일한 역할을 한다. 그렇다면 테이블 전체가 아닌 행의 삭제도 가능한 DELETE 만 쓰면되지 왜 TRUNCATE를 쓰는지 의문이 들것이다.

DELETE와 TRUNCATE는 내부적으로 조금 다르다. 테이블에 행이 추가될 때 실제디스크 공간은 추가되는 행의 데이터 크기만큼 할당된다. 100개의 행을 가지고 있는 테이블이 있고 데이터들이 필요없어져서 테이블은 남겨놓고, 데이터들만 모두 삭제했다고 치자. 이 때, 삭제를 DELETE 문을 사용하여 삭제한 경우와 TRUNCATE로 삭제한 경우 두 가지로 볼 수 있다. 각 경우는 테이블에 다시 데이터를 삽입했다고 한다면 다음과 같을 것이다.


위의 그림을 하드디스크 상태라고 가정하면, DELETE로 데이터를 삭제한 테이블은 100개의 행이 사용했떤 디스크 공간이 그대로 할당된채로 남아 있고, TRUNCATE로 삭제한 경우는 할당되었던 디스크 공간까지 다시 반환하고, 깨끗한 상태로 다시 시작하게 된다. 새로 추가되는 행의 개수가 100개 이상이라면 상관없겠지만 예를 들어 20개만 추가되었다고 한다면 SELECT 문으로 검색하면 성능의 저하를 일으킬 수 있다. 성능의 저하를 일으키는 이유는 실제 SELECT문으로 데이터를 검색하면 디스크는 테이블이 할당되어 있는 디스크 공간을 모두 검색하게 되는데, DELETE를 사용하여 삭제한 테이블은 데이터가 없음에도 불구하고 1번부터 100번까지 모두 검색한 후 검색 결과를 사용자에게 보여주지만, TRUNCATE를 사용하여 삭제한 경우는 추가된 20개의 행에 해당하는 디스크 공간만 검색하게 된다. 데이터의 양이 많지않은 데이터베이스에서는 성능차이는 거의 없겠지만, 데이터의 양이 몇 년씩 쌓여있는 데이터베이스에서는 성능차이가 심할 것이다.

결론적으로 말하자면, 테이블의 데이터를 모두 삭제하는 경우 성능의 향상을 위해 TRUNCATE를 권하지만 Rollback으로 복원할 수 없으니 신중하게 사용하도록 하자.

물론 Rollback이 아닌 실제 복구를 통해서 데이터를 되살릴 수 있지만, 이 같은 방법은 반드시 복구를 보장하는 것은 아니다.




샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.




No. 01

 

 

트랜잭션이란?


트랜잭션이란 데이터베이스에서 하나의 논리적 기능을 수행하는 연산자들의 집합이다. 즉, 사용자의 의도에 따라 여러 개의 문장으로 구성된 트랜잭션은 반드시 동시에 실행(COMMIT) 되거나 취소(ROLLBACK)된다. 트랜잭션을 통해 작업의 단위를 결정함으로써 작업 결과의 신뢰성이 확보된다.

예를 들어 계좌 이체를 한다고 해보자. A라는 사람이 소유 계좌의 금액 중 1000만원을 B라는 사람의 계좌로 이체한다면 취소 두 가지 작업으로 분리할 수 있다. 첫 번째 작업은 A의 계좌에서 1000만원이 출금되는 것이고, 두 번째는 B의 계좌에 1000만원이 입금되는 것이다. 그런데 만약 A의 계좌에서 1000만원이 출금된 이후 두 번째 작업이 수행되기 전에 시스템이 다운되면 B의 계좌에 돈이 입금되지 않고 A의 계좌에서만 1000만원이 없어지게 될 수 있다. 이런 문제를 방지하기 위해 두 개의 작업을 하나의 트랜잭션으로 묶어 놓으면 두 개의 작업이 모두 실행되지 않으면 모두 취소되도록 하여 거래의 신뢰도를 높일 수 있다. 즉 두 번째 작업인 입금이 실행되지 않으면 첫 번째 작업은 자동으로 취쇠되도록 하는 것이다.




No. 02

 

 

트랜잭션의 시작과 종료



I. 시작

 - 이전 트랜잭션이 종료된 이후 DML(INSERT, UPDATE, DELETE) 문장이나 DDL(CREATE, ALTER, DROP, TRUNCATE), DCL(GRANT, REVOKE) 문장이 실행됐을 때 시작된다.

II. 종료

 - COMMIT이나 ROLLBACK 명령이 실행될 때 종료된다.

 - DDL이나 DCL 문장은 실행이 완료되면 자동으로 종료된다.

 - 사용자의 정상 종료 시에 종료된다.

 - 데드락(Deadlock)이 걸리면 트랜잭션의 일부만 종료된다.

 

트랜잭션은 DDL이나 DCL의 경우 문장이 실행되고 난 후 자동으로 종료된다.

하지만, DML인 경우 반드시 COMMIT이나 ROLLBACK으로 종료해야 한다.

오라클의 경우 SAVEPOINT 명령을 이용하여 트랜잭션의 과정을 제어할 수 있지만 실용적이지 않아 거의 사용하지 않는다.




No. 03

 

 

TCL(COMMIT, ROLLBACK, SAVEPOINT)


① COMMIT

사용:

commit;

  • 한 작업의 논리적 단위가 성공적으로 끝났고, 데이터베이스가 다시 일관된 상태에 잇으며 이 트랜잭션이 행한 갱신 연산이 완료된 것을 트랜잭션 관리자에게 알려주는 연산
  • 작업을 완료한다.
  • SQL 명령어로 수행된 결과를 실제 물리적 디스크로 저장하는 SQL 명령
  • 모든 DML 문장을 수행한 후 작업을 완료할 때 반드시 필요하다.
DML 문장을 수행한 후 테이블의 정보를 검색해보면 문장의 수행이 반영된 것을 볼 수 있다. 하지만, 이것은 메모리상에서만 처리된 것이다. COMMIT 명령을 수행해야지만 실제 하드디스크에 저장된다.
예를 들어 '한글 2010' 프로그램에서 문서작업을 하고 있다고 하자. 문서를 작성하는 것이 실시간으로 화면에 보여지고 프린트로 출력도 가능하다. 하지만 저장하지 않은 상태에서 프로그램을 껏다가 다시 작업하던 파일을 열면 지금까지 작업한 내용은 남아 있지 않다. 예와 같은 이치로 COMMIT을 하지 않고 데이터베이스를 종료했다가 다시 키면 이전에 했던 DML 작업은 안한 것과 같다. COMMIT 명령을 해야만 실제 디스크에 저장되고 데이터베이스를 껏다가 다시 키더라도 남아 있다.

② ROLLBACK
사용:

rollback;

  • 트랜잭션의 실행을 취소하였음을 알리는 연산자로 트랜잭션이 수행한 결과를 원래의 상태로 원상 복귀시키는 연산
  • 작업을 취소한다.
  • 모든 DML 문장을 수행한 후 작업을 취소할 때 반드시 필요하다.
오라클은 작업이 발생하면 작업 이전 정보를 undo tablespace 라는 임시 저장 공간을 이용하여 저장하고 있는다. undo tablespace는 자동으로 관리되며 사용자는 이들 정보를 직접 볼 수 없으며 단지 트랙잭션에만 사용된다. 그래서 ROLLBACK을 실행하면 DML 작업에 의해서 변경된 정보를 이전 정보로 환원할 수 있다.

③ SAVEPOINT

사용:

savepoint savepoint_name;

  • 현재 트랜잭션 내에 저장점을 만든다.


ROLLBACK을 실행하면 transaction 시작 전인 원래의 상태로 되돌아 간다. 그러나 트랜잭션 내에 SAVEPOINT를 이용하여 저장점을 만들어 놓으면 ROLLBACK TO savepoint_name 명령으로 SAVEPOINT를 지정해 놓은 지점으로 되돌아 갈 수 있다. 여기서 주의할 것은 ROLLBACK으로 돌아간다고 해서 저장이 된다고 착각하지 말자. 당연히 COMMIT 명령을 실행해야만 실제디스크에 저장된다.


TCL 실습은 DML 문장을 다룰 때 같이 해보도록 하겠다.




샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.




No. 01

 

 

집합 연산자의 종류


SELECT 결과에 대해서 집합 연산을 수행한다.



연산자 

의 미 

결   과 

 UNION

합집합

 중복을 제거한 결과의 합을 검색

 UNION ALL

 중복을 포함한 결과의 합을 검색

 INTERSECT

교집합

 양쪽 모두에서 포함된 행을 검색

 MINUS

차집합

 첫 번째 검색 결과에서 두 번째 검색 결과를 제외한 나머지를 검색




No. 02

 

 

집합 연산


기본 구조

SELECT ……

[UNION | UNION ALL | INTERSECT | MINUS]


SELECT ……

[ORDER BY 컬럼 [ASC/DESC]];

  • 두 SELECT 문의 컬럼 개수와 데이터 타입은 일치해야 한다.
  • 검색 결과의 헤더는 앞쪽 SELECT문에 의해 결정된다.
  • ORDER BY 절을 사용할 때는 문장의 제일 마지막에 사용한다.


◈ 예제

▶ UNION을 이용한 합집합

select employee_id, job_id

from employees

union

select employee_id, job_id

from job_history;


위의 예제를 실행해보면 115개의 행이 검색된다. EMPLOYEES 테이블에는 107개의 행이 있고, JOB_HISTORY 테이블에는 10개의 행이 있다.

UNION ALL을 이용하여 합집합을 했다면 117개의 행이 검색됐겠지만 UNION 연산은 중복된 결과는 하나만 남기고 제거하기 때문에 115개의 행만 검색된 것이다. 오라클 성능의 관점으로 보면, UNION ALL은 중복을 제거하는 작업을 하지 않기 때문에 UNION ALL 연산이 성능이 더 좋다.

UNION 연산은 중복된 결과를 제거한 결과의 합이 검색된다.

가능하면 UNION 보다는 UNION ALL을 사용하는 것이 좋다.



▶ INTERSECT를 이용한 교집합

select employee_id, job_id

from employees

intersect

select employee_id, job_id

from job_history;


두 테이블에서 검색된 결과에서 중복된 값의 결과가 검색되었다.

INTERSECT 연산은 양쪽에서 공통된 결과를 검색한다.



▶ MINUS를 이용한 차집합

select employee_id, job_id

from employees

minus

select employee_id, job_id

from job_history;


첫 번째 SELECT 문의 결과와 두 번째 SELECT 문의 결과에서 공통된 행을 빼고 검색된다.

즉, 첫 번째 결과 107개의 행에서 공통된 2개 행을 뺀 105개의 행이 검색된다.

MINUS 연산은 첫 번째 결과에서 두 번째 결과를 제외한 나머지를 검색한다.



▶ SELECT 문의 매칭

select department_id, to_number(null) location, hire_date

from employees

union

select department_id, location_id, to_date(null)

from departments;


UNION 연산을 이용하여 모든 사원의 department_id, location, hire_date를 검색한 결과이다.

집합 연산을 할 때는 SELECT 문의 컬럼 개수와 타입이 일치해야 한다.

TO_DATE(null)을 사용한 이유는 DEPARTMENTS 테이블에는 hire_date 라는 컬럼이 없다. 그래서 컬럼의 개수와 컬럼의 타입을 맞춰주기 위해서 변환 함수까지 사용한 것이다. 물론 NULL 이라고만 써도 오라클 자체에서 자동변환되어 인식하겠지만, 이전에도 말했듯이 명시적으로 타입을 바꿔주면 성능면에서 더 좋다. NULL이 아닌 다른 값을 없는 컬럼의 값 대신 집어 넣을때는 변환 함수로 타입을 맞춰주지 않으면 에러가 발생하는 경우가 있다. 검색 결과의 헤더는 앞쪽 SELECT문에 의해 결정된다.

TO_NUMBER(null)도 마찬가지의 이유로 기술한 것이다. 여기서 주목해야 할 것은 TO_NUMBER(null)에 LOCATION 이라는 별명을 붙여주었다. SQL문의 실행 결과의 헤더에도 LOCATION으로 표시된다.

즉, 검색 결과의 헤더는 앞쪽 SELECT 문에 의해 결정된다.




 TIP 


보통 MINUS 집합 연산자를 이용한 방법보다 두 개의 조건을 이용한 SELECT 문이 훨씬 단순하고 가독성이 좋으며 성능 또한 우수하다. 그러나 경우에 따라 MINUS 연산자를 이용하면 성능이 훨씬 좋을 때가 있다. 다음과 같은 경우처럼 조건에 부정연산이 들어 갔을 때 같은 경우이다.


select employee_id, last_name, job_id

from employees

where last_name like 'K%'

and job_id <> 'SA_REP';

select employee_id, last_name, job_id

from employees

where last_name like 'K%'

minus

select employee_id, last_name, job_id

from employees

where job_id = 'SA_REP';


 인덱스란 나중에 배우면 알겠지만 책의 목차와 같은 역할을 한다. '<>'와 같은 부정연산은 인덱스를 사용할 수 없다. 만약 job_id 컬럼에 인덱스가 있더하더라도 job_id 컬럼에 대한 연산이 부정 연산이면 인덱스를 사용할 수 없다. 이런 경우 job_id에 대한 연산을 '<>'이 아니라 '='으로 바꾸면 인덱스를 이용할 수 있다. 위의 경우처럼 MINUS 연산자를 이용하여 일치하는 데이터를 검색한 다음 빼면 원하는 결과를 얻을 수 있다. 물론 이때 조건에 사용된 last_name 컬럼에도 사용가능한 인덱스가 있어야 한다.





샘플 스키마 : HR

설명하는 과정에서 예제로 주어진 문장은 눈으로 보지만 말고 직접 실행시켜 보길 권한다.




No. 01

 

 

다중 행 서브 쿼리


서브 쿼리에서 여러 행이 검색되는 쿼리문이다. 연산자를 제외하면 단일 행 서브 쿼리와 같다.


기본 구조

SELECT [DISTINCT] 컬럼, 컬럼 …

FROM 테이블


WHERE 컬럼 <다중 행 연산자> (SELECT 컬럼

  FROM 테이블);

  • 다중 행 서브 쿼리는 다중 행 연산자를 이용한다.
  • 다중 행 연산자의 종류

연산자

기   능

 

IN

 검색된 값 중에 하나만 일치하면 참이다.

 

ANY 

 검색된 값 중에 조건에 맞는 것이 하나 이상 있으면 참이다.

 

ALL

 모든 검색된 값과 조건에 맞아야 한다.

 



◈ 예제

▶ IN 연산자를 이용한 다중 행 서브 쿼리

select employee_id, first_name, job_id, salary

from employees

where manager_id in (select manager_id

from employees

where department_id = 20)

and department_id <> 20;


20번 부서원들과 같은 상사로부터 관리 받는 사원을 검색한 것이다.

서브 쿼리문을 따로 실행해보면 두 개의 행이 검색된다. 단일행 연산자를 사용할 경우에는 위의 SQL문은 에러가 발생하겠지만, 다중 행 연산자인 'IN'을 사용하면 에러가 발생하지 않는다.

'IN'의 기능은 예전에 배웠던 그대로이다. 즉, 서브 쿼리에서 검색되는 값들 중 일치하는 값이 하나라도 있으면 검색한다.



▶ ANY를 이용한 다중 행 서브 쿼리

select employee_id, last_name, job_id, salary

from employees

where salary < any (select salary

    from employees

    where job_id = 'ST_MAN')

and job_id <> 'ST_MAN';


직책이 'ST_MAN'인 사원들 중 가장 많은 급여를 받는 사람보다 급여가 작은 사원을 검색했다.


서브 쿼리를 따로 검색해보면,

select salary

from employees

where job_id = 'ST_MAN';


ANY는 검색된 값 중에 조건에 맞는 것이 하나 이상 있으면 참인데, 위 예제의 경우는 검색된 급여 중 어떤 값보다도 작으면 된다. 즉, 가장 큰 값인 8000 보다 낮은 급여를 받으면 참이다. 그래서 서브 쿼리의 검색된 급여 중 5800이 있음에도 불구하고 메인 쿼리에서 5800보다 많은 급여를 받는 사원들이 검색된 것이다.



▶ ALL을 이용한 다중 행 서브 쿼리

select employee_id, last_name, job_id, salary

from employees

where salary < all (select salary

 from employees

 where job_id = 'ST_MAN')

and job_id <> 'ST_MAN';


직책이 'ST_MAN'인 사원들 보다 급여가 낮은 사원을 검색했다.

ALL은 모든 검색 값과 조건이 맞아야 한다. 위 예제의 경우는 ALL을 이용 검색된 모든 값보다 낮은 값을 검색하도록 한 것이다.

즉, 서브 쿼리문에 검색된 모든 급여보다 낮아야 하므로 결국 5800보다 낮은 급여를 받는 사원이 검색된다.



ALL과 ANY 정리

사 용 

의 미 

같은 표현 

 

컬럼 > ANY

 가장 작은 값보다 크다. 

컬럼 > MIN

 

컬럼 < ANY

 가장 큰 값보다 작다. 

컬럼 < MAX

 

컬럼 > ALL

 가장 큰 값보다 크다. 

컬럼 > MAX

 

컬럼 < ALL

 가장 작은 값보다 작다. 

컬럼 < MIN

 


부등호를 이용하는 다중 행 서브 쿼리문은 MIN이나 MAX함수를 이용한 문으로 수정이 가능하다.

ALL을 이용한 다중 행 서브 쿼리의 예제문을 바꿔보면 다음과 같다.


select employee_id, last_name, job_id, salary

from employees

where salary  < (select min(salary)

     from employees

     where job_id = 'ST_MAN')

and job_id <> 'ST_MAN';

실행해보면 알겠지만, 결과값은 같다.




No. 02

 

 

다중 열 서브 쿼리


여러 개의 컬럼을 검색하는 서브 쿼리이다.

다중 열 서브 쿼리는 주로 'IN'을 사용하지만 '='도 사용 가능하다. 'IN'의 사용을 권한다.


기본 구조

SELECT [DISTINCT] 컬럼, 컬럼 …

FROM 테이블


WHERE (컬럼1, 컬럼2, …) IN (SELECT 컬럼1, 컬럼2, …

FROM 테이블);

  • 서브 쿼리의 SELECT 문에 여러 개의 컬럼을 검색한다.
  • 반드시 비교 대상 컬럼과 1:1 대응돼야 한다.


◈ 예제

select employee_id, first_name, job_id, salary

from employees

where (manager_id, job_id) in (select manager_id, job_id

from employees

where first_name = 'Bruce')

and first_name <> 'Bruce';


Bruce와 동일한 상사이면서 같은 직업인 사원을 검색한 것이다.

서브 쿼리만 실행해 보면,

select manager_id, job_id

from employees

where first_name = 'Bruce';


즉, manager_id가 103이면서 job_id가 'IT_PROG'인 사원들이 검색되는 것이다.

검색 조건에 기술하는 컬럼들이 1:1대응하여 모두 같아야 검색이 된다.


검색 조건이 동시에 검색된 두 개의 컬럼과 비교 될 때 다중 열 서브 쿼리를 이용한다.


위 예제 같은 경우는 다음과 같은 형태의 서브 쿼리로 바뀔 수 있다.

select employee_id, first_name, job_id, salary

from employees

where manager_id in (select manager_id from employees where first_name = 'Bruce')

and job_id in (select job_id from employees where first_name = 'Bruce')

and first_name <> 'Bruce';


다중 열 서브쿼리는 조건을 따로 검색한 것과 같은 기능을 한다.



▶ 각 부서별로 최소 급여를 받는 사원의 정보를 검색한다.

select department_id, employee_id, first_name, salary

from employees

where (department_id, salary) in (select department_id, min(salary)

   from employees

   group by department_id)

order by department_id;


조건은 각 부서의 번호와 최소 급여가 둘 다 일치하는 사원을 찾는 문제인데, 이때 부서 번호와 급여를 각각 따로 조건 검색 할 방법이 없다.

조건을 따로 검색하는 것이 불가능한 경우 그룹함수를 사용하는 전형적인 예이다.




+ Recent posts