테이블과 컬럼 이름 짓기

단수냐 복수냐, id 냐 user_id 냐, is_ 냐 has_ 냐 — 지킬 만한 규칙과, 일관되기 때문에만 의미 있는 규칙.

대부분은 일관되기 때문에만 의미가 있다

명명 논쟁의 거의 전부에 방어 가능한 답이 둘씩 있습니다. 테이블 이름의 단수와 복수, 기본키의 id 와 table_id, created_at 과 created_date — 각각 근거가 있고, 어느 쪽도 질의를 느리게 하거나 데이터를 잃지 않습니다.

진짜 시간을 잡아먹는 것은 절반은 이쪽, 절반은 저쪽으로 간 스키마입니다. 질의를 쓰는 사람이 매번 어느 테이블이 어느 규칙인지 찾아봐야 합니다. 그래서 지킬 규칙은 이것입니다 — 하나를 고르고, 적어 두고, 전부를 바꾸는 마이그레이션으로만 바꾼다.

아래는 맞출 기존 스키마가 없을 때 어느 쪽을 고를지입니다. 기존 스키마가 있다면 그것에 맞추세요 — 동의하지 않는 부분이라도요.

테이블

규칙예이유
복수users, orders, order_items테이블은 여러 행을 담고, 한 행이 단수다
snake_caseorder_itemsPostgres·Oracle 에서 따옴표 없는 식별자는 어차피 소문자로 접힌다
접두사 없이tbl_users 가 아니라 users접두사는 맥락이 이미 말한 것을 반복한다
연결 테이블은 둘을 붙여post_tags, listing_amenities관계를 대개 타고 가는 순서로 읽힌다
자라면 이름을 준다student_courses 가 아니라 enrollments자기 컬럼을 가진 연결 테이블은 개체다

복수가 다수 관행이고 대부분의 ORM 이 전제하는 쪽입니다. 단수 쪽의 논거가 더 낫습니다 — 테이블은 집합이고 이름은 그 원소를 가리킨다 — 지만 소수 입장이라 옮겨 가는 비용이 얻는 것보다 큽니다.

키

컬럼규칙비고
기본키id짧고, 자기 테이블 안에서는 모호하지 않다
외래키<단수>_iduser_id, listing_id — 대상 테이블을 이름에 담는다
같은 테이블로 가는 외래키 둘<역할>_iduser_id_1·user_id_2 가 아니라 reviewer_id·reviewee_id
연결 테이블 복합키(a_id, b_id)쌍이 곧 키. 대리 id 는 더하는 것이 없다
자연키값 자체country_code, currency_code — 값이 안정적이고 의미 있을 때

기본키를 id 대신 user_id 로 하자는 논거는 조인이 별칭 없이 읽힌다는 것입니다. 반대 논거는 모든 테이블이 가장 많이 쓰는 컬럼에서 자기 이름을 반복하게 된다는 것입니다. id 가 더 흔하고, 외래키 규칙 — user_id 는 users.id 를 가리킨다 — 이 자명해집니다.

패턴이 있는 컬럼

종류규칙예
불리언is_ 또는 has_is_active, has_paid
사건의 시각<동사>_atcreated_at, published_at, deleted_at
시각 없는 날짜<동사>_date 또는 <명사>_datedue_date, birth_date
개수<명사>_countfollower_count, retry_count
금액<명사>_amount 또는 <명사>_totaldiscount_amount, line_total
열거 상태status 또는 <명사>_statusstatus, payment_status
순서position 또는 sort_order부모 안에서는 position, 전역은 sort_order

_at 접미사는 제 몫을 합니다. 그 컬럼이 플래그나 기간이 아니라 순간이라는 것을 말해 주는데, 바로 그것이 헷갈리는 지점입니다. deleted_at 은 주석 없이도 소프트 삭제로 읽히고, is_deleted 와 deleted_at 을 같이 두면 한 가지를 두 컬럼이 말하게 됩니다.

피할 단어

  • 예약어 — order, user, group, table, key. 따옴표를 붙이면 되지만 영원히 붙이는 것이 세금입니다. orders, users, groups 를 쓰세요.
  • 보편적이지 않은 축약 — usr, dt, amt. 아낀 타자는 누군가 처음 잘못 짐작하는 순간 되갚습니다.
  • 이름에 든 타입 — name_varchar, is_flag. 타입은 이미 스키마에 있습니다.
  • 뜻 없는 접미사 — user_data, order_info. 모든 테이블이 데이터를 담습니다.
  • 한 단어를 두 뜻으로 — 같은 스키마에서 status 가 계정 상태이기도 하고 결제 상태이기도 한 경우. 하나는 수식하세요.

사람들이 걸리는 것은 user 입니다. PostgreSQL 에서는 예약어이고 MySQL 에서는 그냥 됩니다. 한쪽에서는 되고 다른 쪽에서는 따옴표가 필요한 스키마는 언젠가 그것을 모르는 사람이 옮기게 됩니다. users 면 이 질문 자체가 없습니다.

물리명과 논리명

물리명은 데이터베이스가 보는 이름 — order_items 입니다. 논리명은 사람이 부르는 이름 — 주문 품목 입니다. 둘 다 두면 SQL 을 쓰지 않는 사람도 도면을 읽을 수 있고, 컬럼 이름을 바꾸지 않고도 용어집을 가질 수 있습니다.

이것이 작동하게 하는 규율은 개념 하나에 용어 하나입니다. 같은 것을 한 곳에서는 '회원', 다른 곳에서는 '사용자' 라고 부르는 도면이라면, 논리명은 용어집이기를 그만두고 두 번째 혼란이 된 것입니다.

자주 묻는 것

테이블 이름은 단수인가요 복수인가요?
이미 단수인 스키마가 있는 게 아니라면 복수입니다. 다수 관행이고 대부분의 ORM 이 전제합니다. 단수 쪽 논거도 일관되지만 소수 입장이고, 일관성이 그것을 이깁니다.
기본키를 id 로 할까요 user_id 로 할까요?
id 입니다. 가장 많이 읽히는 자리에서 컬럼이 짧게 유지되고, 외래키 규칙 — user_id 는 users.id 를 가리킨다 — 이 자명해집니다. 반대쪽은 조인에서 약간 더 잘 읽히는 대신 모든 곳에서 테이블 이름을 반복합니다.
연결 테이블 이름은 어떻게 짓나요?
두 테이블을 붙입니다 — post_tags, listing_amenities. 자기 컬럼이 생기면 개체이므로 제 이름을 줄 자격이 있습니다. student_courses 보다 enrollments 입니다.
테이블 이름을 user 로 하면 왜 문제인가요?
PostgreSQL 에서 예약어라 따옴표가 필요하고 MySQL 에서는 그냥 됩니다. 한쪽에서 되고 다른 쪽에서 따옴표가 필요한 스키마는 결국 그 사실을 모르는 사람이 옮기게 됩니다. users 면 비껴갑니다.
is_deleted 인가요 deleted_at 인가요?
deleted_at 하나입니다. 여부와 시점에 모두 답하고, 플래그를 더하면 서로 어긋날 수 있는 컬럼이 하나 늘어납니다. deleted_at is null 로 거르세요.
데이터베이스 명명 규칙 — 테이블·컬럼·키