열
고친 사람 github-actions[bot]
열은 테이블에 어떤 값이 들어올 수 있는지를 미리 정해 둡니다. 회원 명단을 담은 테이블이라면 나이가 들어가는 세로 한 줄이 열입니다. 표 계산 프로그램이나 행렬의 세로 한 줄도 열이라고 부릅니다. 이 항목은 데이터베이스 테이블의 열을 다룹니다.
쉽고 빠른 이해
열은 테이블에서 같은 뜻을 가진 값들이 들어가는 세로 줄입니다. 회원 명단을 담은 테이블이라면 나이가 들어가는 줄 하나가 열입니다.
열을 미리 정해 두지 않으면 어떤 회원은 나이가 숫자로, 어떤 회원은 글자로 들어옵니다. 읽는 쪽은 값을 볼 때마다 이게 무슨 종류인지 짐작해야 합니다. 열은 그 짐작을 없앱니다.
어떻게 도나:
- 테이블을 만들 때 열 이름과 그 열에 들어갈 값의 종류를 정합니다
- 자료를 한 건 넣을 때마다 데이터베이스가 약속에 맞는 값인지 검사합니다
- 읽을 때는 열 이름을 대서 원하는 열만 꺼냅니다
대가는 한 번 정한 약속을 나중에 바꾸기 어렵다는 것입니다. 열을 지우거나 값의 종류를 바꾸면 이미 쌓인 자료와 그 열을 읽던 코드를 함께 손봐야 합니다.
담을 항목이 건마다 달라야 한다면 열을 미리 정하는 방식이 맞지 않습니다. 그럴 때는 열을 미리 정해 두지 않는 저장 방식을 고릅니다.
상세
학급 출석부를 떠올려 봅니다. 맨 윗줄에 「이름」과 「생년월일」과 「연락처」가 인쇄되어 있습니다. 학생이 몇 명 늘어도 그 제목은 바뀌지 않습니다.
열은 테이블 안에서 같은 뜻을 가진 값들이 모이는 세로 줄입니다. 테이블 하나는 열 여럿을 가로로 늘어놓은 것입니다. 자료는 그 아래로 한 줄씩 쌓입니다. 그 가로 한 줄이 행입니다.
둘은 늘어나는 방식이 다릅니다. 행은 자료가 들어올 때마다 늘고 지워지기도 합니다. 열은 테이블을 설계할 때 정해 두고 오래 바꾸지 않습니다. 그래서 이 테이블이 무엇을 담는지는 열 이름만 훑어도 대강 읽힙니다.
행과 열이 나누어 맡는 몫
아래는 회원 세 건을 담은 members 테이블입니다. 가로 한 줄이 회원 한 명이고, 세로 한 줄이
열입니다.
| id | name | age |
|---|---|---|
| 1 | 유미 | 3 |
| 2 | 태호 | 5 |
| 3 | 지연 | 4 |
행은 한 건을 통째로 가리킵니다. 「2번 회원」이라고 하면 이름과 나이가 함께 따라옵니다. 열은 한 종류를 가리킵니다. 「age 열」이라고 하면 회원이 몇 명이든 나이만 모입니다.
행과 열이 만나는 한 칸이 셀입니다. 위 표에서 2번 행과 age 열이 만나는 셀의 값은 5
입니다. 이 문서는 세로 줄을 열, 그 줄과 행이 만나는 한 칸을 셀이라고 적습니다.
이 셀을 필드라고 부르는 문서도 많습니다. 둘을 가려 쓰는 문서에서 열은 설계상의 세로 줄입니다. 필드는 한 행 안에서 그 열에 든 값 하나입니다. 다른 글에서 필드를 만나면 셀을 가리키는지 열을 가리키는지만 보고 읽으면 됩니다.
관계형 모델을 다루는 문서는 열을 속성이라고 적습니다. 낱말이 갈릴 뿐 가리키는 것은 같습니다. 어느 말을 쓰는지 먼저 맞춰 두면 됩니다.
열 하나가 정해 두는 약속
열을 만드는 일은 세로 줄을 하나 긋는 일이 아니라 그 줄에 관한 약속을 적는 일입니다. 약속은 네 가지로 나뉩니다.
| 무엇을 정하나 | 정해 두면 | 안 정하면 |
|---|---|---|
| 이름 | 이 열을 부를 말이 생깁니다 | 몇 번째 열인지로만 부르게 됩니다 |
| 데이터 타입 | 숫자 열에 글자가 들어오는 일을 막습니다 | 읽는 쪽이 매번 종류를 확인합니다 |
| 비어 있어도 되는지 | 빠뜨린 값을 넣는 시점에 잡습니다 | 값이 없는 행을 나중에 발견합니다 |
| 값의 범위 | 나이가 음수인 행이 안 생깁니다 | 어긋난 값이 섞인 채 쌓입니다 |
값이 없는 상태를 널이라고 합니다. 숫자 0 이나 빈 글자와는 다릅니다. 「아직 모른다」와
「값이 0 이다」를 가르려고 따로 둔 표시입니다. 열마다 널을 받을지 말지를 정해 둡니다.
값을 안 주고 행을 넣을 때 대신 들어갈 값을 미리 정해 둘 수도 있습니다. 그 값이 기본값입니다. 기본값을 정해 두면 그 열을 빠뜨린 행에도 널 대신 그 값이 들어갑니다.
값의 범위를 제한하는 규칙은 제약입니다. 나이는 음수일 수 없다거나, 같은 이메일이 두 행에 있을 수 없다는 것이 제약입니다. 제약을 열에 붙여 두면 어긋난 값을 넣으려는 시도가 그 자체로 실패합니다.
정해 둔 약속은 값을 넣을 때마다 검사됩니다. 한 군데라도 어긋나면 그 행은 저장되지 않습니다.
flowchart TD
V["들어온 값"] --> T{"열이 정한 타입인가"}
T -->|아니다| X["거절하고 행을 안 넣는다"]
T -->|맞다| C{"열에 걸린 제약을 지키나"}
C -->|아니다| X
C -->|맞다| OK["그 행의 그 열에 저장"]
검사를 통과하지 못한 값은 조용히 버려지지 않고 오류로 돌아옵니다. 잘못된 자료가 들어오는 것을 읽는 쪽이 아니라 쓰는 쪽에서 막는 셈입니다.
어떤 열이 있고 각 열이 무엇을 약속하는지를 적어 둔 설계도가 스키마입니다. 스키마는 자료를 넣는 쪽과 읽는 쪽이 함께 보는 문서 노릇을 합니다.
열 이름으로 값을 꺼내는 질의
관계형 데이터베이스에서 자료를 꺼낼 때는 몇 번째 열인지가 아니라 열 이름을 댑니다. 아래는 SQL(Structured Query Language, 구조화 질의 언어)로 적은 질의 둘입니다.
SELECT name FROM members WHERE id = 2; -- 태호
SELECT age FROM members WHERE id = 3; -- 4
SELECT 뒤에 적은 것이 꺼낼 열입니다. WHERE 뒤에 적은 것이 고를 행의 조건입니다. 가로로 행을
고르고 세로로 열을 고르는 두 축이 질의 한 줄에 함께 들어 있습니다.
flowchart TD
Q1["members 세 행"] --> Q2["WHERE id = 2 · 행 하나만 남는다"]
Q2 --> Q3["SELECT name · 그 행에서 name 열만 남는다"]
Q3 --> Q4["값 하나 · 태호"]
필요한 열만 적으면 쓰지도 않을 값을 읽어 오는 일이 줄어듭니다. 특히 긴 글이 담긴 열이 섞여
있을 때 차이가 납니다. 모든 열을 부르는 * 는 편하지만, 뒤에 열이 하나 더 생기면 그 질의가
꺼내 오는 것도 말없이 달라집니다.
고르는 조건에 자주 쓰이는 열은 그 열로 찾는 일이 빨라지게 해 둡니다. 특정 열의 값으로 행을 빨리 찾도록 곁에 만들어 두는 구조가 인덱스입니다. 어느 열에 인덱스를 둘지는 그 열로 얼마나 자주 찾느냐가 정합니다.
열을 나중에 바꾸는 비용
열을 더하는 일과 지우는 일은 치르는 비용이 다릅니다. 이미 쌓인 행이 몇 건인지, 도는 코드가 그 열을 어떻게 읽고 있었는지가 비용을 정합니다.
| 바꾸는 일 | 이미 쌓인 행 | 그 열을 읽던 코드 |
|---|---|---|
| 열 더하기 | 기본값을 정해 두었으면 그 값이, 아니면 널이 들어갑니다 | 모르는 열이라 안 건드립니다 |
| 열 이름 바꾸기 | 그대로 남습니다 | 옛 이름으로 부르던 질의가 깨집니다 |
| 타입 바꾸기 | 모든 행의 값을 새 종류로 옮겨야 합니다 | 받는 값의 종류가 달라집니다 |
| 열 지우기 | 그 열에 있던 값이 사라집니다 | 그 열을 부르던 질의가 깨집니다 |
그래서 열은 더하는 쪽으로 먼저 움직입니다. 지우는 일은 아무도 그 열을 안 읽는 것을 확인한 뒤로 미룹니다. 새 열을 만들어 값을 옮기고, 읽는 쪽을 새 열로 바꾸고, 마지막에 옛 열을 지우는 식입니다. 이렇게 스키마를 단계로 나누어 바꾸는 일이 스키마 마이그레이션입니다.
flowchart TD
M1["새 열 추가"] --> M2["값 옮기기"]
M2 --> M3["읽는 쪽을 새 열로"]
M3 --> M4["아무도 안 읽는 것 확인"]
M4 --> M5["옛 열 삭제"]
행이 많이 쌓인 테이블에서는 바꾸는 시간도 따집니다. 모든 행을 손대야 하는 변경은 자료가 많을수록 오래 걸립니다. 그동안 그 테이블을 쓰는 다른 작업이 기다리게 될 수 있습니다.
행 단위 저장과 열 단위 저장
화면에서는 표 한 장으로 보입니다. 디스크에 적는 방법은 둘로 갈립니다. 한 행을 이루는 값들을 붙여 적을 수도 있고, 한 열을 이루는 값들을 붙여 적을 수도 있습니다.
flowchart TD
subgraph R["행 단위로 적은 경우"]
R1["행 1: 1 · 유미 · 3"]
R2["행 2: 2 · 태호 · 5"]
R3["행 3: 3 · 지연 · 4"]
end
subgraph C["열 단위로 적은 경우"]
C1["id: 1 · 2 · 3"]
C2["name: 유미 · 태호 · 지연"]
C3["age: 3 · 5 · 4"]
end
행 단위로 적으면 한 회원의 모든 값이 한자리에 붙어 있습니다. 열 단위로 적으면 나이만 한자리에 모입니다. 앞쪽이 행 지향 저장이고 뒤쪽이 열 지향 저장입니다. 둘은 잘하는 일이 갈립니다.
| 행 지향 저장 | 열 지향 저장 | |
|---|---|---|
| 한 건 읽고 쓰기 | 한 번의 접근으로 끝납니다 | 흩어진 열마다 값을 나누어 적습니다 |
| 여러 행 집계 | 안 쓸 열까지 함께 읽고 지나갑니다 | 필요한 열만 훑습니다 |
| 압축 | 한 줄에 여러 종류가 섞여 덜 먹습니다 | 같은 종류가 이어 붙어 잘 먹습니다 |
| 어디에 쓰나 | 건별로 읽고 쓰는 업무 시스템 | 몇 개 열만 훑어 집계하는 분석 시스템 |
어느 쪽이 맞는지는 한 건을 자주 만지나, 한 열을 자주 훑나로 갈립니다.
열을 늘리지 않고 행으로 미는 설계
담을 항목이 자주 늘어나면 열을 계속 더하기가 부담스러워집니다. 그래서 「항목 이름」 열과 「값」 열 둘만 두고, 항목 하나를 행 하나로 적는 설계가 나옵니다. 이 모양을 EAV(Entity-Attribute-Value, 엔티티-속성-값)라고 합니다.
같은 members 를 두 모양으로 적으면 이렇게 갈립니다.
flowchart TD
subgraph A["열로 둔 members"]
A1["id"]
A2["name"]
A3["age"]
end
subgraph B["EAV 로 눕힌 members"]
B1["1 · name · 유미"]
B2["1 · age · 3"]
B3["2 · name · 태호"]
end
왼쪽은 회원 한 명이 행 하나입니다. 오른쪽은 회원 한 명의 항목 하나가 행 하나라, 회원이 그대로여도 행 수가 항목 수만큼 불어납니다.
열을 안 건드리고 항목을 늘릴 수 있는 것이 이 설계의 이점입니다. 대신 열이 지고 있던 약속을 함께 잃습니다. 잃는 것은 셋입니다.
| 잃는 것 | 무엇이 곤란해지나 |
|---|---|
| 타입 | 값 열 하나에 숫자와 날짜와 글자가 섞여 들어옵니다 |
| 제약 | 항목마다 다른 규칙을 걸어 둘 열이 없습니다 |
| 조회 | 항목 셋을 함께 보려면 한 테이블을 세 번 맞춰 읽어야 합니다 |
세 번 맞춰 읽는다는 것은 같은 회원의 행 셋을 골라 한 줄로 합친다는 뜻입니다. 이렇게 흩어진 행을 키로 맞춰 한 줄로 합치는 일이 조인입니다. 열로 두었으면 한 행을 한 번 읽고 끝날 일입니다.
가르는 기준은 항목 목록이 설계할 때 미리 정해지느냐, 쓰는 사람이 실행 중에 늘리느냐입니다. 설계할 때 정해지는 항목이면 열로 둡니다. 그러면 검사와 질의를 데이터베이스에 맡길 수 있습니다.
쓰는 사람이 마음대로 항목을 만들어야 한다면 열로 감당하기 어렵습니다. 그때는 열을 미리 정해 두지 않는 스키마리스 저장 방식까지 함께 견줍니다.
관련 항목
열이 모여 이루는 데이터 단위
테이블 · 행 · 셀 · 레코드 · 튜플 · 릴레이션 · 결과 집합 · 차수
열 하나를 정의하는 요소
데이터 타입 · 널 · 기본값 · 제약 · 도메인 · 식별자 · 길이 제한
열에 걸어 두는 제약
기본 키 · 외래 키 · 유일 제약 · 검사 제약 · 널 제약 · 참조 무결성
열을 만들고 바꾸는 명령
DDL · CREATE TABLE · ALTER TABLE · SELECT · 스키마 · 스키마 마이그레이션 · 온라인 DDL
흩어진 행과 열을 한 결과로 합치는 질의 연산
열을 가리키는 다른 이름
필드 · 속성 · 프로퍼티 · 멤버 변수 · 인스턴스 변수
열로 행을 빨리 찾게 하는 구조
인덱스 · 복합 인덱스 · 커버링 인덱스 · 통계 정보 · 선택도 · 히스토그램
열을 디스크에 적는 방식
행 지향 저장 · 열 지향 저장 · 압축 · Parquet · 데이터 웨어하우스 · SQL · 관계형 데이터베이스
열을 미리 정하지 않는 저장 방식
스키마리스 · 문서 데이터베이스 · NoSQL · 키-값 저장소
열 설계에서 자주 터지는 문제
다른 이름: column · 컬럼 · columns