사전 관계형 모델
개념

관계형 모델

gabury1고친 사람 github-actions[bot]

관계형 모델은 데이터를 전부 표에 담아 다루게 하는 이론입니다. 꺼내는 쪽은 원하는 결과만 말합니다. 데이터가 디스크의 어디에 놓였는지는 몰라도 됩니다. 오늘날 쓰는 관계형 데이터베이스가 이 이론 위에 서 있습니다.

쉽고 빠른 이해

관계형 모델은 데이터를 전부 표로 적게 하는 이론입니다. 회원은 회원 표에, 주문은 주문 표에 적습니다. 두 표는 회원 번호라는 같은 값을 적어 두는 것으로 이어집니다.

이게 없으면 프로그램이 데이터가 저장된 모양을 알아야 합니다. 저장 구조를 바꾸면 그 데이터를 읽는 코드도 함께 고쳐야 합니다.

  1. 데이터는 이름 붙은 칸을 가진 표에 한 줄씩 담습니다
  2. 표끼리는 같은 값을 적어 두는 것으로 잇습니다
  3. 꺼낼 때는 원하는 결과만 적습니다. 찾아가는 길은 데이터베이스가 고릅니다

회원·주문처럼 서로 얽힌 업무 데이터에 맞습니다. 대가는 다시 맞추는 비용입니다. 여러 표에 나눠 적은 정보를 꺼낼 때마다 짝을 맞춰야 합니다. 코드의 객체와 모양이 달라서 둘 사이를 옮기는 일도 생깁니다.

상세

관계형 모델은 데이터를 다루는 방식을 세 가지로 정한 이론입니다. 데이터를 담는 모양과 꺼내는 연산, 그리고 데이터가 늘 지켜야 할 규칙입니다. 셋 모두 표 하나를 단위로 삼습니다.

쇼핑몰의 회원과 주문을 이 모델로 적어 보겠습니다. 회원은 member 표에 한 줄에 한 명씩 적습니다.

id name city
1 민지 서울
2 태호 부산

주문은 orders 표에 따로 적습니다. member_id 칸에는 그 주문을 한 회원의 id 를 적습니다.

id member_id amount
10 1 12000
11 2 8000
12 1 3000

민지의 줄이 주문 10 과 12 를 직접 가리키지는 않습니다. orders 표에 1 이라는 값이 두 번 적혀 있을 뿐입니다. 무엇과 무엇이 이어지는지를 값으로만 나타내는 것이 이 모델의 중심입니다.

세 가지가 무엇을 정하는지는 아래 표와 같습니다.

정하는 것 내용
모양 데이터를 이름 붙은 칸을 가진 표에 담는다. 표에는 값만 들어간다. 다른 줄의 저장 위치는 넣지 않는다
연산 표를 받아 새 표를 내놓는 연산으로 데이터를 꺼낸다
규칙 줄을 가리키는 값(뒤에 나올 기본 키)은 비지 않는다 · 다른 표를 가리키는 값(외래 키)은 그 표에 있어야 한다

셋을 풀기 전에 이 모델보다 앞선 방식부터 봅니다. 값으로만 잇는다는 것이 무엇을 바꿨는지는 옛 방식과 견주면 드러납니다. 그 뒤로 모양·연산·규칙을 차례로 풉니다.

경로를 따라 읽던 이전 데이터베이스

관계형 모델이 나오기 전의 데이터베이스는 데이터를 레코드 단위로 저장했습니다. 레코드는 회원 한 명이나 주문 한 건처럼 값 몇 개를 묶은 한 건의 데이터입니다.

레코드끼리는 포인터로 이었습니다. 포인터는 다른 레코드가 저장된 위치를 적어 둔 값입니다. 회원 레코드가 첫 주문의 위치를 품습니다. 그 주문은 다음 주문의 위치를 품습니다.

이어진 모양에 따라 이름이 갈립니다. 레코드마다 부모가 하나뿐인 나무 모양이면 계층형 데이터베이스입니다. 한 레코드를 여러 레코드가 함께 가리킬 수 있으면 네트워크 모델입니다.

아래 그림 위쪽이 이 방식입니다. 아래쪽은 같은 데이터를 관계형 모델로 적은 것입니다.

flowchart TD
    subgraph P["포인터로 잇는 방식"]
        M1["회원 민지"] -->|위치| O1["주문 10"]
        O1 -->|위치| O2["주문 12"]
    end
    subgraph R["관계형 모델"]
        M2["member 줄 · id 1 · 민지"]
        O3["orders 줄 · id 10 · member_id 1"]
        O4["orders 줄 · id 12 · member_id 1"]
    end
    P ~~~ R

위쪽에서 프로그램은 화살표를 하나씩 따라가며 읽습니다. 그래서 프로그램이 저장 구조를 알아야 합니다. 구조를 바꾸면 그 길을 걷던 코드도 고쳐야 합니다. 미리 이어 두지 않은 방향으로 묻기도 어렵습니다.

아래쪽에는 화살표가 없습니다. 에드거 F. 코드가 제안한 관계형 모델은 저장 위치를 가리키는 값을 표에서 뺐습니다. 이어지는 관계는 같은 값을 적어 두는 것으로 나타냅니다. 찾아가는 길은 꺼낼 때마다 새로 정합니다.

이름에 든 관계의 뜻

「관계형」은 표끼리 이어져 있어서 붙은 이름처럼 들립니다. 이 이름의 관계는 수학 낱말입니다. 표 하나를 가리킵니다.

수학에서 관계는 어떤 조건을 만족하는 값 묶음들의 모음입니다. member 표라면 조건은 「이 번호의 회원은 이런 이름이고 이 도시에 산다」입니다. (1, 민지, 서울) 과 (2, 태호, 부산) 은 이 조건을 만족하는 묶음입니다. 이 묶음을 한 줄에 하나씩 적은 것이 member 표입니다.

릴레이션을 이루는 이름들

관계형 모델에서는 이렇게 표로 적은 관계를 릴레이션이라고 부릅니다. 앞의 member 표가 릴레이션 하나입니다.

릴레이션을 이루는 부분에도 이름이 따로 있습니다. 실무에서 쓰는 이름과 짝을 지으면 아래와 같습니다.

관계형 모델의 이름 흔히 부르는 이름 member 에서
릴레이션 표 · 테이블 member 전체
튜플 행 (1, 민지, 서울)
속성 열 id · name · city

튜플 하나는 회원 한 명처럼 한 건의 정보를 적습니다. 속성은 그 정보의 항목마다 붙은 이름입니다.

속성이 받는 값의 범위

속성마다 넣을 수 있는 값의 범위가 정해져 있습니다. 이 범위를 도메인이라고 부릅니다. city 의 도메인이 도시 이름이면 그 칸에 날짜는 못 들어갑니다.

도메인이 있어야 값끼리 견주는 일이 뜻을 가집니다. orders 의 member_id 와 member 의 id 는 같은 도메인이라 서로 견줄 수 있습니다. 금액과 회원 번호는 둘 다 숫자여도 견줄 까닭이 없습니다.

칸 하나에는 더 쪼개지지 않는 값 하나만 넣습니다. 전화번호가 둘인 회원은 한 칸에 둘을 몰아 적지 않습니다. 줄을 나누거나 표를 따로 둡니다. 이 조건을 제1정규형이라고 부릅니다.

집합에서 오는 두 성질

릴레이션은 튜플의 집합입니다. 집합에는 순서가 없습니다. 같은 원소가 두 번 들어가지도 않습니다. 이 두 성질이 릴레이션에도 옵니다.

튜플에는 순서가 없습니다. 「세 번째 줄」은 릴레이션에서 뜻이 없습니다. 순서가 필요하면 꺼낼 때 무엇으로 줄 세울지를 따로 적습니다.

똑같은 튜플이 두 번 있을 수도 없습니다. 그래서 어느 튜플이든 값만 보고 다른 튜플과 가를 수 있습니다. 순서 대신 값으로 튜플을 가리키는 방법이 여기서 나옵니다.

튜플을 가리키는 키

튜플 하나를 집어낼 수 있는 속성의 묶음을 후보 키라고 합니다. 그 값을 대면 튜플이 하나만 나옵니다. 회원마다 다른 이메일을 적는 속성이 있다면 그것도 후보 키입니다.

후보 키가 여럿이면 그중 하나를 골라 대표로 씁니다. 대표로 고른 후보 키가 기본 키입니다. member 의 기본 키는 id 입니다. 수정도 삭제도 이 값으로 대상을 가리킵니다.

다른 릴레이션의 기본 키 값을 적어 두는 속성이 외래 키입니다. orders 의 member_id 가 외래 키입니다. 이 값이 주문을 member 의 어느 튜플과 잇는지 알려 줍니다.

이렇게 이으면 정보 하나를 한 곳에만 적을 수 있습니다. 민지가 서울에 산다는 정보는 member 에 한 번만 있습니다. 주문마다 도시를 다시 적지 않습니다. 겹치는 정보를 없애도록 표를 나누는 설계를 정규화라고 합니다.

표를 받아 표를 내는 연산

관계형 모델은 데이터를 꺼내는 연산도 정합니다. 연산은 릴레이션을 받아 새 릴레이션을 내놓습니다. 이 연산들의 모음을 관계 대수라고 부릅니다.

자주 쓰는 연산은 아래 다섯입니다. 오른쪽 칸은 앞의 두 표에 걸었을 때의 예입니다.

연산 하는 일 예
선택 조건에 맞는 튜플만 남긴다 city 가 서울인 회원
투영 원하는 속성만 남긴다 name 만
조인 두 릴레이션의 튜플을 조건에 맞는 것끼리 짝짓는다 id 와 member_id 가 같은 짝
합집합 · 차집합 모양이 같은 두 릴레이션을 합치거나 뺀다 두 지점의 회원 명단
카티션 곱 두 릴레이션의 튜플을 빠짐없이 짝짓는다 회원 2명 × 주문 3건 = 6짝

다섯 모두 결과가 다시 릴레이션입니다. 그래서 한 연산의 결과에 다음 연산을 이어 붙일 수 있습니다. 서울에 사는 회원의 주문 금액은 세 연산을 이어서 얻습니다.

flowchart TD
    M["member"] --> S["선택 · city 가 서울인 튜플"]
    S --> J["조인 · id 와 member_id 가 같은 짝"]
    O["orders"] --> J
    J --> P["투영 · name 과 amount"]
    P --> RES["결과 · 민지 12000 · 민지 3000"]

그림의 화살표마다 오가는 것은 모두 릴레이션입니다. 마지막 결과도 릴레이션이라 거기에 다시 연산을 걸 수 있습니다.

원하는 결과만 적는 질의

관계 대수를 바탕으로 만든 질의 언어가 SQL(Structured Query Language, 구조화 질의 언어)입니다. 앞의 그림을 SQL 로 적으면 아래와 같습니다.

SQL
SELECT m.name, o.amount
FROM member m
JOIN orders o ON o.member_id = m.id
WHERE m.city = '서울';  -- 민지 12000
                        -- 민지 3000

SELECT 줄이 투영을, JOIN 줄이 조인을, WHERE 줄이 선택을 맡습니다. 어느 연산을 먼저 할지는 적지 않았습니다. 어떤 저장 구조를 거쳐 찾을지도 적지 않았습니다.

이렇게 원하는 결과만 적는 방식을 선언형이라고 합니다. 무엇을 원하는지는 쓰는 쪽이 적습니다. 어떻게 찾을지는 데이터베이스가 정합니다.

선언형 질의는 도서관 사서에게 「이 작가가 쓴 책을 전부 주세요」라고만 부탁하는 것과 같습니다. 어느 서가에 꽂혔는지는 말하지 않아도 사서가 찾아옵니다. 서가를 새로 짜도 부탁하는 말은 바뀌지 않습니다. 사서는 데이터베이스, 부탁은 질의, 서가는 저장 구조에 해당합니다.

찾는 길을 고르는 부품이 쿼리 옵티마이저입니다. 같은 결과를 내는 순서가 여럿이면 그중 읽고 계산할 양이 가장 적을 것 같은 순서를 고릅니다. 사람이 연산 순서를 적지 않아도 되는 까닭이 이 부품입니다.

인덱스는 값으로 튜플을 빨리 찾게 해 주는 보조 구조입니다. city 에 인덱스를 두면 서울 회원을 찾을 때 표를 처음부터 끝까지 훑지 않아도 됩니다.

인덱스를 새로 만들어도 앞의 SQL 은 한 줄도 안 바뀝니다. 인덱스를 쓸지는 옵티마이저가 정합니다. 이렇게 저장 구조를 바꿔도 질의를 고칠 필요가 없는 성질을 데이터 독립성이라고 합니다.

데이터베이스가 지키는 두 규칙

관계형 모델은 모든 릴레이션이 지켜야 할 규칙 둘을 둡니다. 데이터베이스가 쓰기마다 이 규칙을 검사합니다. 규칙을 어기는 쓰기는 거절됩니다.

첫째는 개체 무결성입니다. 기본 키 값은 비어 있으면 안 된다는 규칙입니다. 비어 있으면 그 튜플을 집어낼 방법이 없기 때문입니다.

값이 비어 있음을 나타내는 표시를 널이라고 합니다. 널은 0 이나 빈 문자열과 다릅니다. 값을 모르거나 해당 값이 없다는 표시입니다. 기본 키에는 널이 들어갈 수 없습니다.

널과 견준 결과는 참도 거짓도 아닙니다. SQL 은 이것을 「알 수 없음」으로 칩니다. 그래서 WHERE city = NULL 은 어떤 행도 고르지 않습니다. 널인지 물으려면 WHERE city IS NULL 을 씁니다.

둘째는 참조 무결성입니다. 외래 키 값이 가리키는 튜플이 있어야 한다는 규칙입니다. orders 에 member_id 가 3 인 주문을 넣으려 하면 거절됩니다. member 에 id 가 3 인 튜플이 없기 때문입니다.

두 규칙을 데이터베이스가 맡으니 애플리케이션 코드가 쓰기마다 같은 검사를 되풀이하지 않아도 됩니다. 규칙은 표를 정의할 때 제약 조건으로 적어 둡니다.

릴레이션과 SQL 테이블의 차이

SQL 데이터베이스의 테이블은 릴레이션과 거의 같습니다. 백엔드 코드에서 자주 부딪히는 차이는 겹치는 행입니다.

SQL 테이블은 앞의 집합 성질과 달리 같은 행을 여러 번 담을 수 있습니다. 기본 키를 두지 않으면 똑같은 행이 겹쳐 들어갑니다. 질의 결과에서도 같은 행이 겹쳐 나옵니다. 겹침을 없애려면 SELECT DISTINCT 를 씁니다.

맞는 데이터와 치르는 대가

관계형 모델은 여러 종류의 데이터가 서로 얽혀 있는 곳에 맞습니다. 회원·주문·상품·결제처럼 표를 이리저리 맞춰 봐야 하는 업무 데이터가 그렇습니다. 새로운 질문이 생겨도 표는 두고 질의만 새로 씁니다.

대신 치르는 대가가 셋 있습니다. 첫째 대가는 조인 비용입니다. 정보를 여러 표에 나눠 적은 만큼 꺼낼 때 다시 짝을 맞춰야 합니다. 표가 여러 서버에 나뉘어 있으면 짝을 맞추려고 서버 사이로 데이터가 오갑니다.

둘째 대가는 틀을 먼저 정해야 한다는 점입니다. 릴레이션마다 어떤 속성과 도메인을 가질지 적은 정의를 스키마라고 합니다. 데이터를 넣기 전에 스키마부터 정해야 합니다. 건마다 항목이 다른 데이터는 이 틀에 맞추기 번거롭습니다.

셋째 대가는 코드와의 모양 차이입니다. 코드의 객체는 다른 객체를 필드로 품습니다. 릴레이션은 값만 담습니다. 그래서 한 객체의 정보가 여러 표로 나뉩니다. 둘 사이를 옮기는 일을 맡는 도구가 ORM(Object-Relational Mapping, 객체-관계 매핑)입니다.

이 대가가 이득보다 큰 데이터에는 다른 모델을 씁니다. 어떤 데이터에 무엇을 고르는지는 아래와 같습니다.

데이터가 이럴 때 고르는 모델 담는 방식
키 하나로 값 하나를 꺼내는 일이 전부다 키-값 저장소 키마다 값 한 덩이
한 건을 늘 한꺼번에 읽고 건마다 항목이 다르다 문서 데이터베이스 한 건을 표로 쪼개지 않고 문서 한 덩이로
연결을 여러 단계 따라가는 질문이 중심이다 그래프 데이터베이스 점과 그 사이를 잇는 선

관련 항목

관계형 모델을 이루는 구성 요소

릴레이션 · 튜플 · 속성 · 도메인 · 테이블 · 행 · 열 · 스키마 · 차수

관계형 모델에서 튜플을 가리키고 잇는 키

기본 키 · 후보 키 · 외래 키 · 슈퍼 키 · 대리 키 · 복합 키 · 자연 키

관계형 모델의 데이터를 다루는 연산과 언어

관계 대수 · 관계 해석 · 조인 · 카티션 곱 · SQL · 질의 · 서브쿼리 · 뷰 · 선언형 프로그래밍

관계형 모델이 지키는 규칙과 성질

개체 무결성 · 참조 무결성 · 무결성 · 제약 조건 · 데이터 독립성 · 널 · 3값 논리

관계형 모델 위에서 표를 나누는 설계 기법

정규화 · 정규형 · 제1정규형 · 함수 종속 · 비정규화 · 데이터 모델링 · 개체 관계 모델

관계형 모델의 질의를 처리하는 데이터베이스 내부 부품

쿼리 옵티마이저 · 실행 계획 · 인덱스 · 트랜잭션 · ACID

관계형 모델을 구현한 데이터베이스 제품

관계형 데이터베이스 · PostgreSQL · MySQL · SQLite · Oracle Database

관계형 모델 이전에 쓰던 데이터 모델

계층형 데이터베이스 · 네트워크 모델 · 레코드 · 포인터

관계형 모델과 같은 역할을 두고 겨루는 저장 방식

NoSQL · 문서 데이터베이스 · 키-값 저장소 · 그래프 데이터베이스 · 열 지향 저장

관계형 모델과 코드의 객체를 잇는 도구

ORM · 객체-관계 임피던스 불일치 · 액티브 레코드 · 데이터 매퍼

관계형 모델이 비롯된 수학 개념과 창안자

관계 · 집합 · 순서쌍 · 술어 논리 · Codd

다른 이름: relational model · 관계형 데이터 모델 · 관계 데이터 모델