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
스크립트는 브라우저에 남습니다. 해석도 생성도 사용자의 기기에서 이뤄지고 서버로는 아무것도 가지 않습니다.
무엇을 찾았는가
관계 양쪽의 타입이 다름
orders.user_id열과 참조 대상의 선언이 서로 다릅니다. 어떤 데이터베이스는 키 생성을 거부하고, 나머지는 조인할 때마다 조용히 형변환합니다.
기본 키가 없는 테이블
orders행을 안정적으로 지목할 수 없습니다. 똑같은 두 행 중 하나만 고치지도, 밖에서 참조하지도 못합니다. 복제와 ORM도 키가 있다고 전제합니다.
인덱스 없는 외래 키
orders조인과 부모 행 삭제 검사가 이 열을 지나갑니다. 인덱스가 없으면 그때마다 테이블 전체를 훑습니다.
외래 키가 NULL을 허용
orders.user_id관계가 선택적이라 일부러 그런 경우도 있습니다. 하지만 대개는 NOT NULL을 빠뜨려 부모 없는 행이 생깁니다.
이름이 예약어
user질의마다 따옴표로 감싸야 합니다. 예외를 기억하는 것보다 이름을 바꾸는 편이 쌉니다.
금액을 근사 타입에 저장
user.balanceorders.totalFLOAT와 REAL은 수를 근사로 저장합니다. 첫 나눗셈만 지나도 합계가 맞지 않습니다. 금액에는 NUMERIC이나 DECIMAL을 씁니다.
시간대 없는 TIMESTAMP
user.created_atorders.placedAtPostgreSQL에서 이 열은 시간대를 담지 않고 조용히 현지 시각으로 읽힙니다. 서버를 옮기거나 서머타임이 바뀌면 값이 어긋납니다. 보통 필요한 것은 timestamptz입니다.
이름 규칙이 섞임
orders.placedAtsnake_case와 camelCase가 함께 쓰이고 있습니다. 여기 나열된 것은 소수 쪽 이름입니다. 예외를 기억하기보다 맞추는 편이 낫습니다.
ON DELETE가 지정되지 않음
orders부모를 지울 때 자식을 어떻게 할지는 데이터베이스가 정하며, 기본값은 삭제 거부입니다. 의도가 그것이 아니라면 명시하는 편이 좋습니다.