Chapter 4. Intermediate SQL

핵심 요약: 중급 SQL에서는 조인 표현식(natural join, inner/outer join, on/using 조건), 뷰(정의, 사용, 갱신, 뷰 확장), 무결성 제약조건(not null, unique, check, 참조 무결성과 cascade 동작), SQL 데이터 타입과 스키마(날짜/시간, 사용자 정의 타입, 도메인), 인덱스 정의를 다룬다. 그리고 권한 부여(authorization)를 통한 데이터 접근 제어를 다룬다. 이들은 실질적인 데이터베이스 설계와 운용에 핵심적인 중급 기능들이다.


1. 조인 표현식 (Join Expressions)

1.1 조인 연산의 개요

  • **조인 연산(join operation)**은 두 릴레이션을 입력으로 받아, 특정 조건 하에 튜플들을 매칭하여 새로운 릴레이션을 결과로 반환한다.
  • 본질적으로 조인은 **카티션 곱(Cartesian product)**에 매칭 조건을 적용하고, 결과에 포함할 속성을 지정한 것이다.
  • 조인 연산은 주로 from 절에서 서브쿼리 표현식으로 사용된다.

조인을 이해하기 위해 두 가지 축을 구분해야 한다:

종류설명
조인 타입 (Join Type)inner join매칭되는 튜플만 반환 (기본값)
left outer join왼쪽 릴레이션의 모든 튜플 보존
right outer join오른쪽 릴레이션의 모든 튜플 보존
full outer join양쪽 릴레이션의 모든 튜플 보존
조인 조건 (Join Condition)natural동일 이름 속성 전부로 매칭, 중복 열 제거
on <predicate>임의 조건으로 매칭, 중복 열 유지
using (A₁, A₂, ..., Aₙ)지정 속성으로만 매칭, 중복 열 제거

1.2 Natural Join

Natural join은 두 릴레이션에서 동일한 이름을 가진 모든 공통 속성에 대해 같은 값을 가지는 튜플들을 매칭하고, 공통 속성은 하나만 결과에 포함시킨다.

-- student와 takes에서 학생 이름과 수강 과목 ID 조회
-- 방법 1: 명시적 조인 조건
select name, course_id
from student, takes
where student.ID = takes.ID;
 
-- 방법 2: natural join 사용
select name, course_id
from student natural join takes;
  • 관계대수 표현: student ⋈_(student.ID = takes.ID) takes
  • 여러 릴레이션을 연쇄적으로 natural join 할 수 있다:
select A₁, A₂, ..., Aₙ
from r₁ natural join r₂ natural join ... natural join rₙ
where P;

Natural Join의 위험성

동일 이름이지만 의미가 다른 속성이 있을 경우 의도치 않게 매칭되어 잘못된 결과를 낳을 수 있다.

예제: 학생 이름과 수강 과목 제목을 조회

-- 잘못된 버전
select name, title
from student natural join takes natural join course;
  • studentcourse 모두 dept_name 속성을 가진다.
  • student natural join takes natural join coursedept_name까지 매칭하므로, 학생이 자기 학과가 아닌 다른 학과의 과목을 수강한 경우가 결과에서 누락된다.
-- 올바른 버전 1: natural join + where
select name, title
from student natural join takes, course
where takes.course_id = course.course_id;
 
-- 올바른 버전 2: join ... using 사용
select name, title
from (student natural join takes) join course using (course_id);

1.3 Outer Join

Outer join은 정보 손실을 방지하기 위한 조인의 확장이다. 일반 조인(inner join)에서 매칭되지 않아 탈락하는 튜플을, 나머지 속성을 null로 채워서 결과에 포함시킨다.

예시 데이터

course 릴레이션:

course_idtitledept_namecredits
BIO-301GeneticsBiology4
CS-190Game DesignComp. Sci.4
CS-315RoboticsComp. Sci.3

prereq 릴레이션:

course_idprereq_id
BIO-301BIO-101
CS-190CS-101
CS-347CS-101
  • course에는 CS-347이 없고, prereq에는 CS-315가 없다.

Left Outer Join

왼쪽 릴레이션(course)의 모든 튜플을 보존한다. 오른쪽에 매칭이 없으면 null로 채운다.

course natural left outer join prereq
course_idtitledept_namecreditsprereq_id
BIO-301GeneticsBiology4BIO-101
CS-190Game DesignComp. Sci.4CS-101
CS-315RoboticsComp. Sci.3null
  • CS-315는 prereq에 없지만, left outer join이므로 prereq_id를 null로 채워 결과에 포함.

Right Outer Join

오른쪽 릴레이션(prereq)의 모든 튜플을 보존한다. 왼쪽에 매칭이 없으면 null로 채운다.

course natural right outer join prereq
course_idtitledept_namecreditsprereq_id
BIO-301GeneticsBiology4BIO-101
CS-190Game DesignComp. Sci.4CS-101
CS-347nullnullnullCS-101
  • CS-347은 course에 없지만, right outer join이므로 title, dept_name, credits를 null로 채워 결과에 포함.

Full Outer Join

양쪽 릴레이션의 모든 튜플을 보존한다.

course natural full outer join prereq
course_idtitledept_namecreditsprereq_id
BIO-301GeneticsBiology4BIO-101
CS-190Game DesignComp. Sci.4CS-101
CS-315RoboticsComp. Sci.3null
CS-347nullnullnullCS-101

1.4 on 조건 vs. natural 조건

on 조건은 임의의 조인 predicate를 명시할 수 있으며, 공통 속성 열이 중복으로 남는다 (natural/using과의 차이).

-- inner join + on: 매칭 열(course_id)이 양쪽 모두 결과에 나타남
course inner join prereq on course.course_id = prereq.course_id
course_idtitledept_namecreditsprereq_idcourse_id
BIO-301GeneticsBiology4BIO-101BIO-301
CS-190Game DesignComp. Sci.4CS-101CS-190
-- left outer join + on
course left outer join prereq on course.course_id = prereq.course_id
course_idtitledept_namecreditsprereq_idcourse_id
BIO-301GeneticsBiology4BIO-101BIO-301
CS-190Game DesignComp. Sci.4CS-101CS-190
CS-315RoboticsComp. Sci.3nullnull
  • on 조건은 course_id 열이 양쪽 테이블에서 각각 나타나므로, natural join과 달리 course_id가 두 번 결과에 포함됨에 주의.

1.5 using 조건

using은 매칭에 사용할 속성을 명시적으로 지정한다. natural처럼 중복 열은 하나만 결과에 포함된다.

course full outer join prereq using (course_id)
course_idtitledept_namecreditsprereq_id
BIO-301GeneticsBiology4BIO-101
CS-190Game DesignComp. Sci.4CS-101
CS-315RoboticsComp. Sci.3null
CS-347nullnullnullCS-101

2. 뷰 (Views)

2.1 뷰의 개념

  • 모든 사용자가 전체 논리적 모델(데이터베이스에 저장된 모든 실제 릴레이션)을 볼 수 있는 것은 바람직하지 않을 수 있다.
  • 예를 들어, 교수의 이름과 학과는 알아야 하지만 급여는 숨기고 싶을 때:
select ID, name, dept_name
from instructor
  • **뷰(view)**는 특정 데이터를 특정 사용자에게 숨기는 메커니즘을 제공한다.
  • 개념적 모델에 속하지 않지만 사용자에게 “가상 릴레이션(virtual relation)“으로 보이는 릴레이션을 라 한다.

2.2 뷰 정의

create view v as <query expression>
  • <query expression>은 어떤 합법적인 SQL 표현식이든 가능하다.
  • 뷰 이름 v는 이후 해당 가상 릴레이션을 참조하는 데 사용된다.
  • 뷰 정의 ≠ 새로운 릴레이션 생성: 뷰 정의는 쿼리 표현식을 저장하는 것이며, 뷰를 사용하는 쿼리에서 해당 표현식이 **대입(substitution)**된다.

2.3 뷰의 정의와 사용 예시

-- 급여 없이 교수 정보를 보여주는 뷰
create view faculty as
    select ID, name, dept_name
    from instructor;
 
-- 뷰를 일반 테이블처럼 사용
select name
from faculty
where dept_name = 'Biology';
 
-- 뷰에 명시적 속성 이름 부여 (집계 함수 사용 시 유용)
create view departments_total_salary(dept_name, total_salary) as
    select dept_name, sum(salary)
    from instructor
    group by dept_name;

2.4 다른 뷰를 사용하여 뷰 정의

하나의 뷰 정의에 다른 뷰를 사용할 수 있다.

-- 뷰 1: 2017 가을학기 Physics 과목의 강의실 정보
create view physics_fall_2017 as
    select course.course_id, sec_id, building, room_number
    from course, section
    where course.course_id = section.course_id
        and course.dept_name = 'Physics'
        and section.semester = 'Fall'
        and section.year = 2017;
 
-- 뷰 2: 위 뷰에서 Watson 건물만 필터링
create view physics_fall_2017_watson as
    select course_id, room_number
    from physics_fall_2017
    where building = 'Watson';

뷰 의존 관계

  • 뷰 v₁이 뷰 v₂를 정의 표현식에서 사용하면, v₁은 v₂에 **직접 의존(depend directly)**한다.
  • v₁이 v₂에 직접 의존하거나, v₁에서 v₂까지의 의존 경로가 존재하면, v₁은 v₂에 **의존(depend on)**한다.

2.5 뷰 확장 (View Expansion)

다른 뷰를 참조하는 뷰의 의미를 정의하는 방법이다.

뷰 v₁이 표현식 e₁으로 정의되고, e₁이 다른 뷰 릴레이션을 포함할 때:

repeat
    e₁ 안의 뷰 릴레이션 vᵢ를 찾는다
    vᵢ를 vᵢ의 정의 표현식으로 대체한다
until e₁에 더 이상 뷰 릴레이션이 없을 때까지
  • 뷰 정의가 **재귀적(recursive)**이지 않은 한, 이 루프는 반드시 종료한다.

확장 예시:

-- 확장 전
create view physics_fall_2017_watson as
    select course_id, room_number
    from physics_fall_2017
    where building = 'Watson';
 
-- 확장 후 (physics_fall_2017을 정의로 대체)
create view physics_fall_2017_watson as
    select course_id, room_number
    from (select course.course_id, building, room_number
          from course, section
          where course.course_id = section.course_id
              and course.dept_name = 'Physics'
              and section.semester = 'Fall'
              and section.year = '2017')
    where building = 'Watson';

2.6 뷰 갱신 (Update of a View)

뷰에 대한 삽입/삭제/갱신은 기반 릴레이션에 반영되어야 한다. 그러나 이 변환이 항상 가능하거나 명확한 것은 아니다.

간단한 경우

-- faculty 뷰 (salary 없이 instructor의 ID, name, dept_name)
create view faculty as
    select ID, name, dept_name
    from instructor;
 
-- 뷰에 삽입
insert into faculty values ('30765', 'Green', 'Music');
  • 이 삽입은 instructor 릴레이션에 반영되어야 하는데, salary 값이 없다.
  • 두 가지 접근: (1) 삽입 거부, 또는 (2) salary를 null로 설정하여 ('30765', 'Green', 'Music', null) 삽입

올바르게 변환할 수 없는 경우

-- instructor와 department를 조인한 뷰
create view instructor_info as
    select ID, name, building
    from instructor, department
    where instructor.dept_name = department.dept_name;
 
insert into instructor_info values ('69987', 'White', 'Taylor');
  • Taylor 건물에 해당하는 학과가 없을 수도 있다.
  • 삽입 후에도 뷰에서 ('69987', 'White', 'Taylor')가 보이지 않을 수 있다 (매칭 학과가 없으므로).

뷰 조건을 만족하지 않는 삽입

create view history_instructors as
    select *
    from instructor
    where dept_name = 'History';
 
-- Biology 학과 교수를 history_instructors 뷰에 삽입
insert into history_instructors
    values ('25566', 'Brown', 'Biology', 100000);
  • instructor 릴레이션에는 삽입되지만, dept_name = 'History' 조건을 만족하지 않으므로 뷰에는 나타나지 않는다.

2.7 뷰 갱신이 허용되는 조건

대부분의 SQL 구현체는 **단순 뷰(simple view)**에 대해서만 갱신을 허용한다:

  1. from 절에 데이터베이스 릴레이션이 하나만 있을 것
  2. select 절에 릴레이션의 속성 이름만 포함 (수식, 집계 함수, distinct 없음)
  3. select 절에 나열되지 않은 속성은 null로 설정 가능할 것
  4. 쿼리에 group byhaving 절이 없을 것

3. 무결성 제약조건 (Integrity Constraints)

무결성 제약조건은 데이터베이스에 대한 우발적 손상을 방지하여, 허용된 변경이 데이터 일관성의 손실을 초래하지 않도록 보장한다.

예시:

  • 당좌 예금 계좌의 잔액은 $10,000 이상이어야 한다
  • 은행 직원의 시급은 최소 $4.00이어야 한다
  • 고객은 반드시 (null이 아닌) 전화번호를 가져야 한다

3.1 단일 릴레이션에 대한 제약조건

제약조건설명
not nullnull 값 금지
primary key기본키 (자동으로 not null + unique)
unique후보키 (null 허용)
check (P)조건 P를 모든 튜플이 만족해야 함

3.2 Not Null 제약조건

속성에 null 값이 들어오는 것을 금지한다.

name varchar(20) not null
budget numeric(12, 2) not null

3.3 Unique 제약조건

unique (A₁, A₂, ..., Aₘ)
  • 속성 A₁, A₂, …, Aₘ이 **후보키(candidate key)**를 형성한다는 것을 명시한다.
  • 후보키는 null이 허용된다 (primary key와의 차이점).

3.4 Check 절

check (P) 절은 릴레이션의 모든 튜플이 만족해야 하는 조건 P를 지정한다.

create table section (
    course_id varchar(8),
    sec_id    varchar(8),
    semester  varchar(6),
    year      numeric(4, 0),
    building  varchar(15),
    room_number varchar(7),
    time_slot_id varchar(4),
    primary key (course_id, sec_id, semester, year),
    check (semester in ('Fall', 'Winter', 'Spring', 'Summer'))
);
  • 위 예시에서 semester 속성은 반드시 ‘Fall’, ‘Winter’, ‘Spring’, ‘Summer’ 중 하나여야 한다.

복합 Check 조건

check 절의 조건은 서브쿼리를 포함하는 임의의 predicate가 될 수 있다.

check (time_slot_id in (select time_slot_id from time_slot))
  • section 릴레이션의 time_slot_id가 time_slot 릴레이션에 실제로 존재하는 값인지 확인한다.
  • 이 조건은 section에 튜플이 삽입/수정될 때뿐만 아니라, time_slot 릴레이션이 변경될 때도 검사되어야 한다.

3.5 참조 무결성 (Referential Integrity)

한 릴레이션의 특정 속성 집합에 나타나는 값이, 다른 릴레이션의 특정 속성 집합에도 반드시 나타나야 함을 보장한다.

  • 예: instructor 릴레이션에 “Biology”라는 dept_name이 있으면, department 릴레이션에도 “Biology”가 존재해야 한다.
  • 정의: 속성 집합 A가 릴레이션 R과 S에 모두 존재하고, A가 S의 기본키일 때, R에 나타나는 A의 모든 값이 S에도 나타나면, A는 R의 **외래키(foreign key)**이다.

외래키 선언

-- 기본: 참조되는 테이블의 기본키를 자동 참조
foreign key (dept_name) references department
 
-- 명시적으로 참조할 속성 지정
foreign key (dept_name) references department (dept_name)

3.6 참조 무결성의 캐스케이딩 동작

참조 무결성 제약이 위반될 때 기본 동작은 위반을 유발한 동작을 거부하는 것이다. 대안으로 캐스케이드(cascade) 동작을 지정할 수 있다:

create table course (
    ...
    dept_name varchar(20),
    foreign key (dept_name) references department
        on delete cascade
        on update cascade,
    ...
);
동작설명
cascade참조되는 튜플이 삭제/갱신되면, 참조하는 튜플도 함께 삭제/갱신
set null참조되는 튜플이 삭제/갱신되면, 외래키 값을 null로 설정
set default참조되는 튜플이 삭제/갱신되면, 외래키 값을 기본값으로 설정

3.7 트랜잭션 중 무결성 제약 위반

자기 참조(self-referencing) 외래키가 있는 경우, 삽입 순서에 따라 제약 위반이 발생할 수 있다.

create table person (
    ID     char(10),
    name   char(40),
    mother char(10),
    father char(10),
    primary key (ID),
    foreign key (father) references person,
    foreign key (mother) references person
);

해결 방법 3가지:

  1. 부모(father, mother)를 먼저 삽입한 뒤 자식을 삽입
  2. father, mother를 null로 설정하여 삽입 후, 모든 person 삽입이 끝나면 update로 갱신 (단, not null 제약이 있으면 불가)
  3. **제약 검사를 지연(defer)**시킨다 (트랜잭션 종료 시점에 검사)

4. SQL 데이터 타입과 스키마 (SQL Data Types and Schemas)

4.1 내장 데이터 타입

타입설명예시
date날짜 (4자리 연도, 월, 일)date '2005-7-27'
time시간 (시, 분, 초)time '09:00:30', time '09:00:30.75'
timestamp날짜 + 시간timestamp '2005-7-27 09:00:30.75'
interval기간interval '1' day
  • date/time/timestamp 값의 뺄셈은 interval 값을 반환한다.
  • interval 값은 date/time/timestamp 값에 더하거나 뺄 수 있다.

4.2 사용자 정의 타입 (User-Defined Types)

create type 구문으로 사용자 정의 타입을 생성한다.

create type Dollars as numeric(12, 2) final
create type Pounds as numeric(12, 2) final
  • Dollars와 Pounds는 둘 다 numeric(12, 2) 기반이지만, 서로 다른 타입으로 취급된다.
  • Dollars 타입의 값을 Pounds 타입의 변수에 대입하면 컴파일 타임 오류가 발생한다.
  • 타입 안전성(type safety)을 통해 실수를 방지한다.
-- 사용자 정의 타입을 테이블 정의에 활용
create table department (
    dept_name varchar(20),
    building  varchar(15),
    budget    Dollars
);

4.3 도메인 (Domains)

create domain 구문(SQL-92)으로 사용자 정의 도메인 타입을 생성한다.

create domain person_name char(20) not null
  • 타입과 유사하지만, 도메인에는 not null 등의 제약조건을 직접 지정할 수 있다.
create domain degree_level varchar(10)
    constraint degree_level_test
        check (value in ('Bachelors', 'Masters', 'Doctorate'));
  • constraint 키워드로 제약조건에 이름을 부여할 수 있다.
  • value는 도메인에 대입되는 값을 참조하는 특수 키워드이다.

5. 인덱스 정의 (Index Definition in SQL)

5.1 인덱스의 필요성

  • 대부분의 쿼리는 테이블의 전체 레코드 중 극히 일부만 참조한다.
  • 특정 값을 가진 레코드를 찾기 위해 전체 레코드를 스캔하는 것은 비효율적이다.
  • **인덱스(index)**는 릴레이션의 특정 속성에 대한 데이터 구조로, 해당 속성 값으로 튜플을 전체 스캔 없이 효율적으로 찾을 수 있게 한다.

5.2 인덱스 생성

create index <index-name> on <relation-name> (attribute);

예시

-- student 테이블 정의
create table student (
    ID        varchar(5),
    name      varchar(20) not null,
    dept_name varchar(20),
    tot_cred  numeric(3, 0) default 0,
    primary key (ID)
);
 
-- ID에 대한 인덱스 생성
create index studentID_index on student(ID);
  • 이후 아래 쿼리는 인덱스를 사용하여 student의 전체 레코드를 스캔하지 않고 빠르게 결과를 반환할 수 있다:
select *
from student
where ID = '12345';

6. 권한 부여 (Authorization)

6.1 권한의 개념

사용자에게 데이터베이스의 특정 부분에 대해 여러 형태의 **권한(authorization)**을 부여할 수 있다. 각 권한 형태를 **특권(privilege)**이라 한다.

특권설명
Read데이터를 읽을 수 있으나 수정 불가
Insert새로운 데이터를 삽입할 수 있으나 기존 데이터 수정 불가
Update데이터를 수정할 수 있으나 삭제 불가
Delete데이터를 삭제할 수 있음
  • 사용자에게 릴레이션이나 뷰와 같은 데이터베이스의 특정 부분에 대해, 위 특권의 전부, 일부, 또는 없음을 조합하여 부여할 수 있다.

6.2 SQL에서의 권한 명세 (Authorization Specification in SQL)

grant 문

grant 문을 사용하여 권한을 부여한다:

grant <privilege list> on <relation or view> to <user list>
  • <user list>에 올 수 있는 것:
    • 사용자 ID (user-id)
    • public — 모든 유효한 사용자에게 해당 특권을 부여
    • 역할(role) — 역할에 속한 모든 사용자에게 적용

예시:

grant select on department to Amit, Satoshi;

주의사항:

  • 뷰에 대한 특권을 부여해도, 뷰의 기반 릴레이션에 대한 특권이 자동으로 부여되지는 않는다.
  • 특권을 부여하는 사람(grantor)은 해당 항목에 대한 특권을 이미 가지고 있거나, **데이터베이스 관리자(DBA)**여야 한다.

6.3 SQL에서의 특권 종류 (Privileges in SQL)

특권SQL 키워드설명
조회select릴레이션에 대한 읽기 접근 또는 뷰를 통한 쿼리 가능
삽입insert튜플을 삽입하는 능력
갱신updateSQL update 문을 사용하여 갱신하는 능력
삭제delete튜플을 삭제하는 능력
전체all privileges허용 가능한 모든 특권의 축약형

예시:

-- U₁, U₂, U₃에게 instructor 릴레이션에 대한 select 권한 부여
grant select on instructor to U₁, U₂, U₃;

6.4 권한 회수 (Revoking Authorization in SQL)

revoke 문을 사용하여 권한을 회수한다:

revoke <privilege list> on <relation or view> from <user list>

예시:

revoke select on student from U₁, U₂, U₃;

회수 규칙:

  • <privilege-list>all을 지정하면, 해당 사용자가 가진 모든 특권을 회수한다.
  • <revokee-list>public이 포함되면, 명시적으로 부여받은 사용자를 제외한 모든 사용자가 해당 특권을 잃는다.
  • 동일한 특권이 서로 다른 부여자(grantor)에 의해 두 번 부여된 경우, 한쪽의 회수 후에도 다른 쪽의 부여로 인해 특권이 유지될 수 있다.
  • 회수되는 특권에 의존하는 모든 특권도 함께 회수된다 (cascading revocation).
         U₁ ———→ U₄
        ↗        ↘
DBA ——→ U₂ ———→ U₅
        ↘
         U₃
  • 위 그래프에서 DBA가 U₁의 특권을 회수하면, U₁이 부여한 U₄의 특권도 함께 회수된다.
  • 단, U₅가 U₁과 U₂ 양쪽에서 부여받았다면, U₁의 회수 후에도 U₂를 통한 경로가 남아 있으므로 U₅는 특권을 유지한다.

6.5 역할 (Roles)

**역할(role)**은 사용자들이 데이터베이스에서 접근/갱신할 수 있는 범위에 따라 구분하는 방법이다.

역할 생성 및 사용자 할당

-- 역할 생성
create role instructor;
 
-- 사용자에게 역할 부여
grant instructor to Amit;

역할에 특권 부여

특권은 사용자뿐 아니라 역할에도 부여할 수 있다:

grant select on takes to instructor;

역할 간 상속 (Chain of Roles)

역할은 다른 사용자뿐 아니라 다른 역할에도 부여할 수 있다. 이를 통해 역할 계층 구조를 형성한다:

-- teaching_assistant 역할 생성 후 instructor 역할에 부여
create role teaching_assistant;
grant teaching_assistant to instructor;
-- → instructor는 teaching_assistant의 모든 특권을 상속받는다
 
-- 역할 체인 예시
create role dean;
grant instructor to dean;
grant dean to Satoshi;
-- → Satoshi는 dean의 특권 + instructor의 특권 + teaching_assistant의 특권을 모두 갖는다

6.6 뷰에 대한 권한 (Authorization on Views)

뷰를 통한 데이터 접근에서는 기반 릴레이션 각각에 대한 권한이 별도로 관리된다.

예시:

-- geo_instructor 뷰 생성
create view geo_instructor as
    (select *
     from instructor
     where dept_name = 'Geology');
 
-- geo_staff 역할에게 뷰에 대한 select 권한 부여
grant select on geo_instructor to geo_staff;

핵심 규칙:

  • geo_staffinstructor 릴레이션에 대한 권한이 없더라도, geo_instructor 뷰에 대한 select 권한만으로 뷰 쿼리 결과를 볼 수 있다.
  • 따라서 시스템은 뷰를 기반 릴레이션으로 대체하기 전에 뷰 자체에 대한 권한을 먼저 검사해야 한다.
  • 뷰의 **생성자(creator)**는 뷰 정의에 사용된 기반 릴레이션(예: instructor)에 대한 select 권한을 가지고 있어야 한다.

7. 요약 비교표

조인 타입별 동작 비교

조인 타입왼쪽 비매칭 튜플오른쪽 비매칭 튜플
inner join탈락탈락
left outer joinnull 채워서 보존탈락
right outer join탈락null 채워서 보존
full outer joinnull 채워서 보존null 채워서 보존

조인 조건별 차이

조건매칭 기준중복 열 처리
natural동일 이름 속성 전부 (자동)하나만 유지
on <predicate>임의 조건 (명시적)양쪽 열 모두 유지
using (A₁, ...)지정 속성만 (명시적)하나만 유지

무결성 제약조건 비교

제약조건null 허용중복 허용범위
primary keyXX단일 릴레이션
uniqueOX단일 릴레이션
not nullXO단일 속성
check (P)--단일 릴레이션 (서브쿼리 가능)
foreign key--릴레이션 간 참조