속성 A의 값을 알면 속성 B의 값이 유일하게 정해지는 의존 관계를 'B는 A에 종속한다'라고 한다. 이 관계는 AB로 나타내며, A는 B의 결정자라고 하며, B는 종속 속성이라 한다.
'학생번호주소'와 같이 왼쪽 속성의 각 값에 대하여 오른쪽 속성의 값이 유일하게 결정될 때 '함수적으로 종속한다(functionally dependent)라고 한다.
릴레이션의 속성 간에 함수적으로 종속하는 성질을 함수 종속성이라 한다.
함수 종속성 규칙
X, Y, Z가 릴레이션 R에 포함된 속성의 집합이라고 할 때, 함수 종속성에 관한 다음과 같은 규칙이 성립한다.
릴레이션 R(K, A1, A2, A3...An)에서 K가 기본키이면 KR이 성립한다. 즉 기본키는 릴레이션의 모든 속성에 대한 결정자이다.
이상현상은 기본키가 아니면서 결정자인 속성이 있을 때 발생한다.
이상현상이 발생하는 릴레이션을 분해하여 이상현상을 없애는 과정을 정규화라고 한다.
A와 B가 릴레이션 R의 속성이고 AB가 성립할 때, B가 A의 속성 전체에 함수 종속하고 부분집합 속성에 함수 종속하지 않을 경우 완전 함수 종속이라고 한다. 반면, AB 종속성에서 A의 속성 일부를 제거했는데도 종속성이 여전히 성립하는 경우 부분 함수 종속이라고 한다. 예를 들어, (A1, A2)B 종속성에서 A2를 제거했는데도 A1B가 성립한다면 부분 함수 종속이다.
정규형
릴레이션을 분해할 때는 각 릴레이션에 공통 속성을 하나 이상 두어야 한다.
릴레이션 분해 과정에서 원래 릴레이션 R의 함수 종속성 집합 F는 분해된 각 릴레이션에 맞게 재분배된다.
클로저 계산
1. 클로저를 속성 집합 로 초기화.
2. 주어진 함수 종속성 중에서 의 현재 클로저에 있는 속성을 결정자로 가진 함수 종속성을 확인
- 에서 일 경우, 를 에 추가. 즉 결정자가 클로저에 포함되어 있다면, 종속자를 클로저에 추가.
- 새로운 속성이 추가되지 않을 때까지 2번 반복
- 최종적으로 계산된 는 로부터 도달 가능한 모든 속성.
결정자를 기반으로, 결정자가 작은 것부터, 종속자가 많은 것부터.
1) A, B, D
A의 클로저 계산:
시작: {A}
A → BC 적용: {A, B, C}
A → D 적용: {A, B, C, D}
D → A는 이미 포함.
결과: A의 클로저 = {A, B, C, D}
B의 클로저 계산:
시작: {B}
B → A 적용: {B, A}
A → BC 적용: {B, A, C}
A → D 적용: {B, A, C, D}
결과: B의 클로저 = {A, B, C, D}
D의 클로저 계산:
시작: {D}
D → A 적용: {D, A}
A → BC 적용: {D, A, B, C}
결과: D의 클로저 = {A, B, C, D}
2) AC
A의 클로저 계산:
시작: {A}
A → B 적용: {A, B}
B → C 적용: {A, B, C}
결과: A의 클로저 = {A, B, C}
AC의 클로저 계산:
시작: {A, C}
A → B 적용: {A, B, C}
AC → D 적용: {A, B, C, D}
결과: AC의 클로저 = {A, B, C, D}
3) ABD
AB의 클로저 계산:
시작: {A, B}
AB → C 적용: {A, B, C}
CD → E는 적용 불가.
결과: AB의 클로저 = {A, B, C}
ABD의 클로저 계산:
시작: {A, B, D}
AB → C 적용: {A, B, C, D}
CD → E 적용: {A, B, C, D, E}
결과: ABD의 클로저 = {A, B, C, D, E}
4) AC
AC의 클로저 계산:
시작: {A, C}
AC → E 적용: {A, C, E}
C → D 적용: {A, C, D, E}
D → A는 이미 포함.
결과: AC의 클로저 = {A, B, C, D, E}
5) BC
BC의 클로저 계산:
시작: {B, C}
BC → ADE 적용: {A, B, C, D, E}
D → B는 이미 포함.
결과: BC의 클로저 = {A, B, C, D, E}
6) DBC
DBC의 클로저 계산:
시작: {D, B, C}
DC → AE 적용: {A, B, C, D, E}
E → F 적용: {A, B, C, D, E, F}
결과: DBC의 클로저 = {A, B, C, D, E, F}
7) ABD
ABD의 클로저 계산:
시작: {A, B, D}
AD → E 적용: {A, B, D, E}
BE → F 적용: {A, B, D, E, F}
B → C 적용: {A, B, C, D, E, F}
AF → G 적용: {A, B, C, D, E, F, G}
결과: ABD의 클로저 = {A, B, C, D, E, F, G}
X가 Y를 함수적으로 결정한다면, X의 값이 같다면 Y의 값도 항상 같다. X의 값에 따른 Y의 값은 유일하다.
| A | B | C |
|---|---|---|
| b | c | h |
| e | i | f |
| g | i | f |
| e | b | a |
1) 3, 5, 6
1. 동일한 A값(e)에 대해 C값이 다르므로 성립X
2. 동일한 A값(e)에 대해 B값이 다르므로 성립X
3. 동일한 B값에 대해 C값이 항상 같으므로 성립
4. 동일한 (B, C)값에 대해 A값이 다르므로 성립X
5. 동일한 (A, B)값이 없으므로 함수 종속성이 진공성으로 성립
6. 동일한 (A, C)값이 없으므로 함수 종속성이 진공성으로 성립
2) 2
CD→E (주어진 함수 종속성)
E→A (주어진 함수 종속성): 추이성 규칙에 따라 CD→A를 얻음.
A→C (주어진 함수 종속성): 다시 추이성 규칙에 따라 CD→C를 얻음.
따라서 CD→A와 CD→C를 합쳐서 CD→AC가 성립.
B → D (주어진 종속성)으로 B에서 D를 결정할 수 있음.
하지만, C를 결정하기 위해 B 또는 D로부터 C를 결정할 수 있는 경로가 없음.
C를 결정하려면 A가 필요하지만, BD만으로는 C를 결정할 수 없음.
B→D (주어진 함수 종속성): 확장 규칙에 따라 BC→DC를 얻음.
DC는 CD와 동일하므로, BC→CD가 성립.
A→B (주어진 함수 종속성):확장 규칙에 따라 AC→BC를 얻음.
1) (1, 1, 2), (1, 2, 1)
| A | B | C |
|---|---|---|
| 1 | 1 | 1 |
| 1 | 1 | 2 |
| 1 | 2 | 1 |
| 1 | 2 | 2 |
| 2 | 3 | 3 |
공통 속성 A가 분해된 릴레이션 중 하나에서 키가 되어야 하지만, 그렇지 못하다.
2) 손실 분해
분해된 릴레이션의 공통 속성인 AC가 AC→R1 또는 AC→R2를 만족하는지 확인한다.
3) 손실 분해
분해된 릴레이션의 공통 속성인 AD가 AD->R1 또는 AD->R2를 만족하는지 확인한다.
1)
2)
3)
4)
1) 제 2정규형
2) 제 1정규형
3) 제 1정규형
4) 제 1정규형
1) {shipname, date}, {voyageID, date}
(1) 후보 키 후보 식별:
후보 키 정의: 모든 속성을 유일하게 식별할 수 있는 최소 속성 집합.
(2) 각 후보 키 후보의 폐포 계산:
{shipname, date}의 폐포:{shipname, date}shipname → shiptype ⇒ {shipname, date, shiptype}shipname, date → voyageID, port ⇒ {shipname, date, shiptype, voyageID, port}voyageID → shipname, cargo ⇒ {shipname, date, shiptype, voyageID, port, cargo}{shipname, date}는 후보 키.{voyageID, date}의 폐포:{voyageID, date}voyageID → shipname, cargo ⇒ {voyageID, date, shipname, cargo}shipname → shiptype ⇒ {voyageID, date, shipname, cargo, shiptype}shipname, date → voyageID, port ⇒ {voyageID, date, shipname, cargo, shiptype, port}{voyageID, date}도 후보 키.2)
(1) 프라임 속성과 비프라임 속성:
shipname, voyageID, dateshiptype, cargo, port(2) 부분 함수 종속성 확인:
shipname → shiptype: 부분 함수 종속성.voyageID → shipname, cargo: 부분 함수 종속성.shipname, date → voyageID, port: 완전 함수 종속성.(3) 분해:
릴레이션 1: Ship(shipname, shiptype)
shipname → shiptypeshipname릴레이션 2: Voyage(voyageID, shipname, cargo)
voyageID → shipname, cargovoyageID릴레이션 3: Shipping(shipname, date, voyageID, port)
shipname, date → voyageID, portshipname, date3)
(1) 릴레이션별 이행적 함수 종속성:
릴레이션 1: Ship(shipname, shiptype)
shipname → shiptype릴레이션 2: Voyage(voyageID, shipname, cargo)
voyageID → shipname, cargo릴레이션 3: Shipping(shipname, date, voyageID, port)
shipname, date → voyageID, portvoyageID → shipname (원래의 FDs에서 가져옴)voyageID → shipname은 프라임 속성으로의 이행적 종속 → 3NF 만족.프라임 속성으로의 이행적 종속
결정자(왼쪽 값)가 프라임 속성일 때, 종속 대상(오른쪽 값)이 프라임 속성이라면 이 종속성은 3NF 위반이 아님
4)
(1) 릴레이션별 BCNF 만족 여부:
릴레이션 1: Ship(shipname, shiptype)
shipname → shiptypeshipname은 후보 키 → BCNF 만족.릴레이션 2: Voyage(voyageID, shipname, cargo)
voyageID → shipname, cargovoyageID는 후보 키 → BCNF 만족.릴레이션 3: Shipping(shipname, date, voyageID, port)
shipname, date → voyageID, port (후보 키)voyageID → shipname (BCNF 위반: voyageID는 후보 키가 아님)(2) 위반된 함수 종속성에 따른 분해:
릴레이션 3-1: VoyageShip(voyageID, shipname)
voyageID → shipnamevoyageID릴레이션 3-2: ShippingDetails(voyageID, date, port)
voyageID, date → portvoyageID, date(3) 최종 릴레이션
Ship(shipname, shiptype)
shipname → shiptypeVoyage(voyageID, shipname, cargo)
voyageID → shipname, cargoVoyageShip(voyageID, shipname)
voyageID → shipnameShippingDetails(voyageID, date, port)
voyageID, date → port1) 제 1정규형
(1) 폐포 계산
{booktitle, authorname}의 폐포:{booktitle, authorname}booktitle, authorname → publisher ⇒ {booktitle, authorname, publisher}booktitle → booktype ⇒ {booktitle, authorname, publisher, booktype}booktype → listprice ⇒ {booktitle, authorname, publisher, booktype, listprice}authorname → authorgroup ⇒ {booktitle, authorname, publisher, booktype, listprice, authorgroup}{booktitle, authorname}는 후보 키.(2) 릴레이션의 정규형 판단
제1정규형: 릴레이션은 기본적으로 1NF를 만족한다고 가정.
제2정규형:
booktitle, authorname (후보 키에 포함)booktype, listprice, authorgroup, publisher부분 함수 종속성 확인
booktitle → booktype:booktitle는 후보 키의 일부이므로 부분 함수 종속성 존재.authorname → authorgroup:authorname은 후보 키의 일부이므로 부분 함수 종속성 존재.booktype → listprice:booktype은 비프라임 속성이며, 이 경우는 이행적 함수 종속성에 해당.2)
(1) 정규화 수행
릴레이션 1: BookPublisher(booktitle, authorname, publisher)
booktitle, authorname → publisher{booktitle, authorname}릴레이션 2: BookType(booktitle, booktype)
booktitle → booktypebooktitle릴레이션 3: AuthorGroup(authorname, authorgroup)
authorname → authorgroupauthornamebooktype → listprice 존재. listprice는 비프라임 속성이고 booktype은 비프라임 속성이므로, 이행적 종속성 존재.BookType(booktitle, booktype)booktitlebooktitle → booktypeBookTypePrice(booktype, listprice)booktype → listpricebooktype(2) 최종 릴레이션
BookPublisher(booktitle, authorname, publisher)
booktitle, authorname → publisher{booktitle, authorname}BookType(booktitle, booktype)
booktitle → booktypebooktitleAuthorGroup(authorname, authorgroup)
authorname → authorgroupauthornameBookTypePrice(booktype, listprice)
booktype → listpricebooktype(3) 각 릴레이션의 정규형 확인
{booktitle, authorname}booktitle, authorname → publisherpublisher는 후보 키 전체에 완전 종속.booktitlebooktitle → booktypebooktype은 후보 키에 완전 종속.authornameauthorname → authorgroupauthorgroup은 후보 키에 완전 종속.booktypebooktype → listpricelistprice는 후보 키에 완전 종속.오손 읽기: T1이 READ UNCOMMITTED이고 T2가 READ COMMITTED일 때, T1이 읽기 작업을 한 후, T2가 쓰기 작업을 하고 COMMIT은 하지 않음. 그 후 T1이 읽기 작업을 하고 T2가 ROLLBACK을 하면 결국 T2가 쓰기 작업을 한 데이터는 COMMIT되지 않았음에도 T1은 두 번의 읽기에서 다른 결과를 냄. READ COMMITTED 이상의 격리 수준에서는 방지됨.
반복불가능 읽기: T1, T2 모두 READ COMMITTED일 때, T1이 읽기 작업을 하고, T2가 갱신을 한 후 커밋함. T1이 다시 읽기를 하면 T1은 같은 트랜잭션의 읽기 작업임에도 다른 결과를 냄. REPEATABLE READ 격리 수준 이상에 방지됨.
유령데이터 읽기: T1이 REPEATABLE READ이고 T1이 READ COMMITTED일 때, T1이 읽기 작업을 하고, T2가 삽입을 한 후 커밋함. T1이 다시 읽기를 하면 T1은 같은 트랜잭션의 읽기 작업임에도 다른 결과를 냄. SERIALIZABLE 격리 수준에서만 방지됨.
MySQL에서는 REPEATABLE READ으로도 유령데이터 읽기를 방지 가능. 트랜젝션이 처음 데이터를 읽어 올 때 SNAPSHOT을 구축하여 자료를 가져오고, 그에 따라 다른 세션의 자료가 변경되더라도 동일한 결과를 보여주게 됨.
(1) X = 990, Y = 1010
(2) X = 1100, Y = 1100
(3) T1과 T2가 동시에 X를 읽고 갱신하는 과정에서 T2의 변경 사항이 T1에 의해 덮어씌워짐. 갱신 손실 발생.
(4)
| T1 | T2 |
|---|---|
| LS(X) | |
| A1 = read_item(X); | |
| LX(X) | |
| A1 = A1 + 100; | |
| write_item(A1→X); | |
| UNLOCK(X) | |
| LS(X) | |
| A2 = read_item(X); | |
| temp = A2 * 0.1; | |
| LX(X) | |
| A2 = A2 - temp; | |
| write_item(A2→X); | |
| UNLOCK(X) | |
| LS(Y) | |
| B2 = read_item(Y); | |
| LS(Y) | |
| B1 = read_item(Y); | |
| LX(Y) | |
| B1 = B1 - 100; | |
| write_item(B1→Y); | |
| UNLOCK(Y) | |
| B2 = B2 + temp; | |
| LX(Y) | |
| write_item(B2→Y); | |
| UNLOCK(Y) |
(1) T1이 데이터를 읽은 후, T2가 데이터를 갱신하고 COMMIT으로 확정한 후, T1이 다시 데이터를 읽었으므로 같은 트랜잭션 내에서 다른 읽기 결과가 나온다. 반복 불가능 읽기이다.
(2) REPEATABLE READ 격리 수준을 지정한다.
(3)
| T1 | T2 |
|---|---|
| SET TRANSACTION ISOLATION LEVEL REPEATABLE READ | |
| START TRANSACTION | |
| SELECT age | |
| FROM Users | |
| WHERE id = 1; | |
| UPDATE Users | |
| SET age = 21 | |
| WHERE id = 1; | |
| COMMIT; | |
| SELECT age | |
| FROM Users | |
| WHERE id = 1; |
(1) 10100
(2) 고립 수준을 REPEATABLE READ로 변경
(3) 10000
(4) T2의 COMMIT이 T1의 2번 SELECT 전에 이루어져야 한다.
-- T2 먼저 실행
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
UPDATE Book
SET price = price + 100
WHERE bookid = 1;
COMMIT;
-- 이후 T1 실행
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT SUM(price)
FROM Book; -- 10100
SELECT SUM(price)
FROM Book; -- 10100
COMMIT;
+-----+ +-----+ +-----+ +-----+ +-----+
| T1 | ----> | a | ----> | T4 | ----> | d | ----> | T2 |
+-----+ +-----+ +-----+ +-----+ +-----+
| ^ |
v | v
+-----+ +-----+ +-----+ +-----+ +-----+
| b | ----> | T5 | ----> | c | ----> | T2 | ----> | e |
+-----+ +-----+ +-----+ +-----+ +-----+
^ |
| v
+-----+ +-----+ +-----+
| g | ----> | T3 | ----> | f |
+-----+ +-----+ +-----+
grant 명령은 db 객체에 대한 권한을 사용자나 role에 부여한다. revoke 명령은 부여된 권한을 회수하거나 제한한다.
1)
GRANT INSERT ON madangdb.order TO mdguest;
2)
GRANT SELECT, DELETE ON madangdb.order TO mdguest2 WITH GRANT OPTION;
3)
GRANT UPDATE ON madangdb.book TO mdguest;
전체 백업은 db 내 모든 데이터를 백업 파일로 복사한다. 복구 시간이 빠르고 모든 데이터를 한 번에 복구 가능하지만, 백업 크기가 크다. 차등 백업은 마지막 전체 백업 이후 변경된 데이터만 백업한다.