erd.sketch

SQL 스키마 점검: 무엇이 잘못되었는가

아래는 지어낸 목록이 아니라 왼쪽 스키마에 대한 실제 점검 결과입니다. 편집기의 ‘검사’ 탭에서도 같은 항목이 보이고, 상당수는 버튼 하나로 고쳐집니다.

입력schema.sql
CREATE TABLE user (
    id          BIGSERIAL PRIMARY KEY,
    email       VARCHAR(255) NOT NULL,
    balance     FLOAT,
    created_at  TIMESTAMP NOT NULL
);

CREATE TABLE orders (
    order_id    INTEGER NOT NULL,
    user_id     INTEGER REFERENCES user (id),
    total       FLOAT NOT NULL,
    placedAt    TIMESTAMP
);
판단 결과
PostgreSQL
오류 1경고 8제안 2
편집기 열기

스크립트는 브라우저에 남습니다. 해석도 생성도 사용자의 기기에서 이뤄지고 서버로는 아무것도 가지 않습니다.

무엇을 찾았는가

  • 관계 양쪽의 타입이 다름

    orders.user_id

    열과 참조 대상의 선언이 서로 다릅니다. 어떤 데이터베이스는 키 생성을 거부하고, 나머지는 조인할 때마다 조용히 형변환합니다.

  • 기본 키가 없는 테이블

    orders

    행을 안정적으로 지목할 수 없습니다. 똑같은 두 행 중 하나만 고치지도, 밖에서 참조하지도 못합니다. 복제와 ORM도 키가 있다고 전제합니다.

  • 인덱스 없는 외래 키

    orders

    조인과 부모 행 삭제 검사가 이 열을 지나갑니다. 인덱스가 없으면 그때마다 테이블 전체를 훑습니다.

  • 외래 키가 NULL을 허용

    orders.user_id

    관계가 선택적이라 일부러 그런 경우도 있습니다. 하지만 대개는 NOT NULL을 빠뜨려 부모 없는 행이 생깁니다.

  • 이름이 예약어

    user

    질의마다 따옴표로 감싸야 합니다. 예외를 기억하는 것보다 이름을 바꾸는 편이 쌉니다.

  • 금액을 근사 타입에 저장

    user.balanceorders.total

    FLOAT와 REAL은 수를 근사로 저장합니다. 첫 나눗셈만 지나도 합계가 맞지 않습니다. 금액에는 NUMERIC이나 DECIMAL을 씁니다.

  • 시간대 없는 TIMESTAMP

    user.created_atorders.placedAt

    PostgreSQL에서 이 열은 시간대를 담지 않고 조용히 현지 시각으로 읽힙니다. 서버를 옮기거나 서머타임이 바뀌면 값이 어긋납니다. 보통 필요한 것은 timestamptz입니다.

  • 이름 규칙이 섞임

    orders.placedAt

    snake_case와 camelCase가 함께 쓰이고 있습니다. 여기 나열된 것은 소수 쪽 이름입니다. 예외를 기억하기보다 맞추는 편이 낫습니다.

  • ON DELETE가 지정되지 않음

    orders

    부모를 지울 때 자식을 어떻게 할지는 데이터베이스가 정하며, 기본값은 삭제 거부입니다. 의도가 그것이 아니라면 명시하는 편이 좋습니다.