PostgreSQL을 사용해보기 전에, 일반적인 데이터베이스 이론으로 기초를 다지도록 합시다. 어떤 예제 코드도 입력할 필요는 없습니다. 모든 코드는 오직 설명을 목적으로 합니다.
이 강의의 목표: 핵심적인 데이터베이스 개념들을 이해하기.
데이터베이스는 하나 또는 그 이상의 용도로 쓰이는, 일반적으로 디지털 형태인, 데이터의 조직화된 집합으로 이루어진다. - 위키백과
DBMS(데이터베이스 관리 시스템)는 저장소, 접속, 보안, 백업 및 기타 기능을 제공하는, 데이터베이스를 조작하는 소프트웨어로 이루어진다. - 위키백과
관계형 데이터베이스 및 플랫 파일 데이터베이스에서, 테이블이란 (명칭으로 식별되는) 수직 열과 수평 행의 모델을 사용해 조직된 데이터 요소(값)들의 집합이다. 테이블의 열은 지정된 개수이지만, 행은 무한대로 확장될 수 있다. 각 행은 후보 키라고 식별되는 특정 열 서브셋에 나타나는 값으로 식별된다. - 위키백과
id | name | age
----+-------+-----
1 | Tim | 20
2 | Horst | 88
(2 rows)
SQL 데이터베이스에서는 테이블을 관계 라고도 합니다.
열이란 테이블의 각 행의 하나씩을 차지하는, 특정 단순형 데이터 값들의 집합이다. 열은 어떤 행들로 이루어지느냐에 따르는 구조를 제공한다. 필드라는 용어는 종종 열과 바꿔 쓰이기도 하지만, 많은 이들은 필드(또는 필드 값)을 한 행과 한 열의 교차 지점에 있는 단일 값을 특별히 지칭하는 용어라고 간주한다. - 위키백과
열 :
| name |
+-------+
| Tim |
| Horst |
필드 :
| Horst |
레코드란 테이블의 행에 저장된 정보를 말합니다. 각 레코드는 테이블 안 각 열에 대한 필드를 차지하게 됩니다.
2 | Horst | 88 <-- one record
데이터형이란 열에 저장될 수 있는 정보의 유형을 제한한다. - Tim & Horst
여러 유형의 데이터형이 있습니다. 가장 흔히 쓰이는 데이터형에 대해 알아봅시다.
문자열 - 자유 형식 텍스트 데이터를 저장
정수 - 정수를 저장
실수 - 소수를 저장
날짜 - 아무도 잊지 않도록 Horst의 생일을 저장
불 - 단순한 참/거짓 값을 저장
데이터베이스가 필드에 아무것도 저장하지 않도록 할 수도 있습니다. 필드에 아무것도 없을 경우, 필드의 내용을 ‘널’ 값 이라고 합니다.
insert into person (age) values (40);
select * from person;
결과 :
id | name | age
----+-------+-----
1 | Tim | 20
2 | Horst | 88
4 | | 40 <-- null for name
(3 rows)
더 많은 데이터형을 사용할 수 있습니다. PostgreSQL 매뉴얼을 살펴보세요!
간단한 예제를 통해 데이터베이스가 어떻게 구성되는지 알아봅시다. 주소 데이터베이스를 생성하겠습니다.
주소를 표현하는 요소가 곧 열입니다. 각 열에 저장되는 데이터의 유형이 곧 데이터형입니다. 다음 단계에서 이 개념적인 주소 테이블을 분석해서 어떻게 향상시킬 수 있는지 알아보겠습니다!
데이터베이스를 생성하는 과정은 실재 세계의 모형을 생성하는 것입니다. 실재 세계의 개념을 취해서 데이터베이스에 엔티티로서 표현하는 것입니다.
데이터베이스의 주요 아이디어 가운데 하나는 데이터의 복제/중복을 피하자는 것입니다. 데이터베이스에서 중복을 제거하는 과정을 정규화라고 합니다.
정규화란 데이터베이스의 구조가 범용 쿼리 및, 데이터 무결성을 잃을 수 있는 바람직하지 않은 특정 특성들 - 인서트, 업데이트, 딜리트 이상 - 을 피하는 데 적합한지 확인하는 체계적인 방법이다. - 위키백과
정규화 ‘서식’에는 서로 다른 유형들이 있습니다.
간단한 예를 살펴보겠습니다.
Table "public.people"
Column | Type | Modifiers
----------+------------------------+-----------------------------------------
id | integer | not null default
| | nextval('people_id_seq'::regclass)
| |
name | character varying(50) |
address | character varying(200) | not null
phone_no | character varying |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)
select * from people;
id | name | address | phone_no
--+---------------+-----------------------------+-------------
1 | Tim Sutton | 3 Buirski Plein, Swellendam | 071 123 123
2 | Horst Duester | 4 Avenue du Roix, Geneva | 072 121 122
(2 rows)
동일한 도로명 또는 도시명을 가진 친구들이 많다고 상상해보십시오. 이 데이터가 복제될 때마다 용량을 소비하게 됩니다. 더구나 도시명이 변경될 경우, 데이터베이스를 업데이트하는 데 많은 작업을 해야 합니다.
복제를 줄이고 데이터 구조를 정규화하기 위해 앞의 이론적인 ‘people’ 테이블을 재설계해보십시오.
데이터베이스 정규화에 대해 이곳 에서 더 읽어볼 수 있습니다.
데이터베이스 인덱스란 데이터베이스 테이블에서 데이터 검색 작업의 속도를 향상시키는 데이터 구조이다. - 위키백과
교과서를 읽다가 어떤 개념에 대한 설명을 찾는다고 상상해보십시오 - 그런데 교과서에 색인이 없군요! 여러분은 필요한 정보를 찾을 때까지 표지부터 책 전체를 다시 훑어야 할 겁니다. 책의 끝부분에 있는 색인은 관련 정보가 있는 페이지를 빨리 찾을 수 있게 해줍니다.
create index person_name_idx on people (name);
이제 성명 검색이 빨라질 겁니다.
Table "public.people"
Column | Type | Modifiers
----------+------------------------+-----------------------------------------
id | integer | not null default
| | nextval('people_id_seq'::regclass)
| |
name | character varying(50) |
address | character varying(200) | not null
phone_no | character varying |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)
"person_name_idx" btree (name)
시퀀스란 유일 숫자 생성기입니다. 보통 테이블의 어떤 열을 위한 유일한 식별자를 생성하는 데 쓰입니다.
이 예제에서 id가 시퀀스입니다. 테이블에 레코드가 추가될 때마다 숫자가 증가합니다.
id | name | address | phone_no
---+--------------+-----------------------------+-------------
1 | Tim Sutton | 3 Buirski Plein, Swellendam | 071 123 123
2 | Horst Duster | 4 Avenue du Roix, Geneva | 072 121 122
정규화된 데이터베이스는 일반적으로 많은 관계(테이블)을 가지게 됩니다. 이 관계들 사이의 논리적 의존성을 설계하는 데 엔티티-관계 도표(ER Diagram)를 사용합니다. 이전 단계의 아직 정규화되지 않은 ‘people’ 테이블을 생각해보십시오.
select * from people;
id | name | address | phone_no
----+--------------+-----------------------------+-------------
1 | Tim Sutton | 3 Buirski Plein, Swellendam | 071 123 123
2 | Horst Duster | 4 Avenue du Roix, Geneva | 072 121 122
(2 rows)
같은 거리에 사는 개인들에 대해 도로명을 반복할 필요가 없도록 이 테이블을 손쉽게 두 테이블로 나눌 수 있습니다.
select * from streets;
id | name
----+--------------
1 | Plein Street
(1 row)
이렇게 하면,
select * from people;
id | name | house_no | street_id | phone_no
----+--------------+----------+-----------+-------------
1 | Horst Duster | 4 | 1 | 072 121 122
(1 row)
이 두 테이블을 streets.id 와 people.streets_id 라는 ‘키’를 이용해서 연결할 수 있습니다.
이 두 테이블에 대해 ER 도표를 그린다면 다음과 같이 보일 것입니다.
ER 도표를 통해 ‘일대다’ 관계를 표현할 수 있습니다. 이 예제에서 화살표는 한 도로에 많은 사람이 살 수 있다는 사실을 보여줍니다.
데이터베이스 제약 조건은 관계 안에 있는 데이터가 어떻게 데이터가 저장되어야 하는지에 대한 모델러의 시각과 일치하는지 확인하는 데 쓰입니다. 예를 들어 우편번호에 대한 제약 조건으로 1000 과 9999 사이의 숫자만 저장되도록 할 수 있습니다.
기본 키는 레코드를 유일하게 만들어주는 하나 이상의 필드 값입니다. 기본 키를 보통 id라고 하며, 시퀀스인 경우가 대부분입니다.
외래 키는 유일한 레코드를 (해당 테이블의 기본 키를 써서) 다른 테이블에 참조시키는 데 쓰입니다.
ER 도표를 그릴 때, 테이블 사이의 연결은 보통 기본 키와 연결되는 외래 키에 기반하고 있습니다.
이 ‘people’ 예제를 보면, 테이블 정의에서 street 열이 streets 테이블의 기본 키를 참조하는 외래 키라는 사실을 보여주고 있습니다.
Table "public.people"
Column | Type | Modifiers
-----------+-----------------------+--------------------------------------
id | integer | not null default
| | nextval('people_id_seq'::regclass)
name | character varying(50) |
house_no | integer | not null
street_id | integer | not null
phone_no | character varying |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"people_street_id_fkey" FOREIGN KEY (street_id) REFERENCES streets(id)
데이터베이스에서 데이터를 추가, 변경, 삭제할 때 뭔가 문제가 발생해도 데이터베이스는 언제나 양호한 상태로 남아 있어야 합니다. 대부분의 데이터베이스는 트랜잭션 지원이라는 기능을 제공합니다. 트랜잭션은 데이터베이스에 대한 사용자의 수정 작업이 계획대로 되지 않았을 경우 되돌아갈 수 있는 복원 지점을 생성할 수 있게 해줍니다.
사용자가 은행 계좌 시스템을 가지고 있다고 생각해보십시오. 어떤 계좌에서 다른 계좌로 자금을 전송해야 합니다. 이 일련의 단계를 다음과 같이 가정해볼 수 있습니다.
조에게서 R20을 출금
앤에게 R20을 입금
이 과정에서 무언가 (정전 같은) 문제가 생길 경우, 트랜잭션이 이전으로 복원됩니다.
데이터베이스를 이용하면 간단한 코드 구조를 써서 데이터를 구조화된 방법으로 관리할 수 있습니다.
이제 데이터베이스가 이론적으로 어떻게 작동하는지 알아봤으니, 지금 배운 이론을 시행할 수 있는 새 데이터베이스를 생성해봅시다.