CREATE TABLE [IF NOT EXISTS] schema_name.table_name (
column_definitions,
table_constraints
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='테이블 설명';
IF NOT EXISTS는 같은 이름의 테이블이 이미 있으면 오류를 피합니다.
ENGINE(스토리지 엔진)은 일반적으로 InnoDB 권장(트랜잭션, 외래키, MVCC 지원).
DEFAULT CHARSET은 utf8mb4 권장(이모지·다국어 안전).
컬럼 선언의 기본 형식:
col_name DATA_TYPE [NULL | NOT NULL] [DEFAULT expr] [AUTO_INCREMENT] [UNIQUE] [COMMENT '...']
주요 포인트:
데이터 타입 선택: 정수(INT/BIGINT), 문자열(VARCHAR/TEXT), 날짜(DATE/DATETIME/TIMESTAMP), 소수(DECIMAL) 등.
금액 등 정밀한 소수는 DECIMAL(p,s) 권장(부동소수점 FLOAT/DOUBLE는 부정확).
NULL vs NOT NULL: 가능하면 NOT NULL + 기본값을 사용해 불필요한 NULL을 줄이는 것이 성능·명확성에 유리.
DEFAULT: 상수 또는 함수(CURRENT_TIMESTAMP) 가능(버전/컬럼 타입 제약 존재).
AUTO_INCREMENT: 기본키에 자주 사용. BIGINT UNSIGNED AUTO_INCREMENT를 권장(확장성).
열 코멘트: COMMENT '설명'으로 설명을 남겨두면 SHOW CREATE TABLE에 보입니다.
PRIMARY KEY
PRIMARY KEY (id)
테이블의 유일 식별자. InnoDB에서는 클러스터드 인덱스로 동작.
UNIQUE KEY
UNIQUE KEY ux_users_email (email)
인덱스
INDEX idx_name (col1)
KEY idx_multi (col1, col2)
복합 인덱스(Left-most rule): (a,b,c)는 a, a,b, a,b,c 같은 쿼리에서만 완전 활용.
인덱스는 읽기 속도↑ 쓰기 속도↓/디스크 사용↑ — 필요한 인덱스만 설계.
전문 인덱스: FULLTEXT(텍스트 검색), SPATIAL(지리공간). MySQL 버전·엔진 제약 확인.
InnoDB에서만 정상 동작.
CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
ON UPDATE RESTRICT
ON DELETE/ON UPDATE 옵션: CASCADE, SET NULL, RESTRICT, NO ACTION 등.
외래키를 사용하면 데이터 무결성이 향상되지만 복잡한 배치 작업 시 제약으로 작업이 실패할 수 있으므로 설계 시 고려.
식으로 계산되는 컬럼을 선언할 수 있습니다.
col2 INT GENERATED ALWAYS AS (col1 * 2) [VIRTUAL | STORED]
VIRTUAL: 저장하지 않고 읽을 때 계산(디스크 절약).
STORED: 값을 물리적으로 저장(읽기 빠름, 인덱스에 안정적으로 사용).
인덱스: 생성 컬럼에 인덱스를 만들 수 있음(버전별 동작 차이가 있으므로, 인덱싱 목적이면 STORED 사용 권장).
파티셔닝: 대용량 테이블 관리를 위한 분할(예: RANGE, HASH). 설계가 잘못되면 성능 오히려 떨어질 수 있으므로 사용 전 테스트 필수.
임시 테이블:
CREATE TEMPORARY TABLE temp_x (...);
커넥션 단위로 존재, 연결 종료 시 자동 삭제. 세션별 격리.
기본 구조만 복사
CREATE TABLE new_table LIKE existing_table;
구조 + 데이터 복사
CREATE TABLE new_table AS SELECT col1, col2 FROM old_table WHERE ...;
주의: CREATE ... AS SELECT는 일반적으로 인덱스·제약(FK 등)을 복사하지 않음(구조만 가져오거나 데이터만 채움).
CHECK 제약은 MySQL 8.0부터 실제로 적용(이전 버전은 무시되던 시점이 있었음).
age INT CHECK (age >= 0)
TIMESTAMP: 서버/세션 타임존을 고려해 내부적으로 변환 저장(시간대 변환 기능). 일부범위 제한 존재.
DATETIME: 순수한 날짜/시간 값을 그대로 저장(타임존 변환 없음).
둘 다 CURRENT_TIMESTAMP 기본값·ON UPDATE CURRENT_TIMESTAMP 사용 가능(버전별 차이 있으니 환경 확인).
CREATE TABLE users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL,
password_hash CHAR(60) NOT NULL,
profile JSON DEFAULT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY ux_users_email (email),
KEY idx_users_username (username)
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='회원 테이블';
CREATE TABLE orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
total DECIMAL(12,2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_orders_user_created (user_id, created_at),
CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
profile JSON 같은 경우, JSON 필드에서 자주 검색할 값은 생성 컬럼을 만들어 인덱스하는 패턴 사용.
구조 확인
DESCRIBE table_name;
SHOW CREATE TABLE table_name;
SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='db' AND TABLE_NAME='table_name';
인덱스 확인
SHOW INDEX FROM table_name;
테이블 생성에는 해당 스키마에 대한 CREATE 권한 필요.
GRANT CREATE ON dbname.* TO 'user'@'host';
테이블 생성 자체는 빠르지만 대량 데이터 삽입/색인 생성 등으로 시간이 걸릴 수 있음.
ALTER TABLE은 많은 경우 암묵적 커밋을 발생시키므로 운영환경에서 신중. MySQL의 ALGORITHM=INPLACE, LOCK=NONE 같이 온라인 변경을 지원하는 옵션 사용 가능(버전·옵션 지원여부 확인).
대규모 변경은 pt-online-schema-change, gh-ost 같은 도구 사용 권장.
네이밍: 소문자·언더스코어(snake_case) 권장: user_profiles, order_items. 인덱스/제약 이름 규칙 일관성 유지: pk_users, ux_users_email, fk_orders_user.
기본키: 대체로 BIGINT UNSIGNED AUTO_INCREMENT 사용 권장(분산 환경에서는 UUID 고려).
문자셋: DB·테이블·컬럼에 utf8mb4 사용.
금액: DECIMAL(p,s) 사용(절대 FLOAT 사용 금지).
NULL 최소화: 가능한 NOT NULL로 명시.
인덱스: 쿼리 패턴 기반으로 최소한만 생성. EXPLAIN으로 검증.
주석: 컬럼·테이블 COMMENT로 목적·비고 남기기.
CREATE TABLE ... AS SELECT로 생성하면 인덱스·제약을 복사하지 않음.
외래키는 MyISAM 등 비지원 엔진에서는 동작하지 않음(항상 InnoDB).
대량 데이터 삽입 시 인덱스가 많으면 삽입 성능 급감 — 대량 로드 시에는 인덱스 생성 순서 고려.
ALTER/DDL은 트랜잭션 내에서 사용하면 의도치 않은 커밋을 유발할 수 있음.
목적에 맞는 데이터 타입 선택(정확도·범위 확인).
기본키·유니크키·필요 인덱스 설계.
문자셋(utf8mb4)·콜레이션 지정.
외래키가 필요하면 InnoDB로 결정.
생성 컬럼/JSON 인덱싱 필요 시 미리 설계.
운영 환경이라면 온라인 DDL/마이그레이션 도구 고려.
SHOW CREATE TABLE로 생성 후 구조 검증.