INSERT와 다중 행 등록
INSERT의 열·값 대응, 기본값·NULL, 다중 행 원자성을 이해하고 재현 가능한 회원 게시판 표본 데이터를 만듭니다.
INSERT는 값을 테이블에 넣는 가장 단순한 DML처럼 보이지만 스키마 변경에 가장 쉽게 깨지는 경계입니다.
열 목록을 생략하면 물리 열 순서와 값 목록이 결합되고 선택·기본값 열을 구분하기 어렵습니다.
입력 규칙은 값 개수보다 각 값이 어느 열과 어떤 의미로 대응하는지에 있습니다.
회원 두 명과 여러 상태의 게시글을 추가해 뒤의 SELECT·집계 실습 표본을 만듭니다.
PK AUTO_INCREMENT는 생략하고 UTC 의미의 시각을 명시합니다.
PUBLISHED 상태는 published_at을 함께 넣어 앞 장 검사를 만족합니다.
운영 중 새 열을 추가하는 방법은 이 장 마지막의 심화 문서에서 다룹니다.
열 순서에 의존하는 INSERT
테이블에 선택 열이 늘어난 뒤 열 목록 없는 INSERT를 재사용합니다.
값 개수가 맞지 않으면 즉시 오류가 나고, 우연히 개수가 맞아도 순서가 바뀌면 문자열이 다른 열로 들어갈 수 있습니다.
스키마 변경이 애플리케이션 SQL을 보이지 않게 깨뜨립니다.
INSERT INTO posts VALUES (
NULL,
1,
'GROUP BY 복습',
50,
'2026-07-04 09:00:00'
);posts에는 더 많은 열이 있으므로 MySQL은 값 개수를 테이블 정의와 맞출 수 없습니다.
ERROR 1136 (21S01): Column count doesn't match value count at row 1오류가 즉시 난 것은 오히려 안전합니다.
더 위험한 경우는 열 수와 타입이 우연히 맞아 잘못된 위치에 값이 저장되는 것입니다.
INSERT마다 필요한 열을 이름으로 선언하고 기본값·NULL을 생략 이유와 함께 사용합니다.
AUTO_INCREMENT에 NULL을 직접 넣는 관습보다 PK 열 자체를 목록에서 빼는 편이 명확합니다.
INSERT 열 목록의 역할
INSERT INTO t (a,b,c) VALUES (...)에서 모든 VALUES 행은 열 목록과 같은 개수·순서를 가져야 합니다.
생략한 열은 기본값이 있으면 기본값, NULL 허용이면 NULL을 받고 둘 다 아니면 오류입니다.
명시적 NULL은 기본값을 요청하는 것과 다릅니다.
다중 VALUES 한 구문은 구문 단위 오류를 함께 관찰하기 쉽고 왕복을 줄이지만 너무 큰 배치는 패킷·잠금·롤백 비용을 늘립니다.
INSERT와 기본값은 표준 SQL 개념입니다.
AUTO_INCREMENT, LAST_INSERT_ID(), 다중-행 ID 범위 세부는 MySQL 기능입니다.
생성된 여러 ID가 연속이라고 영구 가정하지 않고 필요한 키는 RETURNING 지원 여부나 후속 유일 키 조회를 기준으로 얻습니다.
이 교재에서는 이메일 같은 후보 키로 회원 id를 조회해 하드코딩된 ID 의존을 줄입니다.
다중 행 등록
먼저 회원을 한 행씩 넣고 방금 생성된 PK를 변수에 보관합니다.
LAST_INSERT_ID()는 현재 연결에서 직전 AUTO_INCREMENT 값을 돌려주는 MySQL 함수입니다.
이렇게 부모 키를 눈에 보이는 두 단계로 얻은 뒤 게시글을 넣으면 INSERT의 열·값 대응에 집중할 수 있습니다.
각 게시글 행은 같은 열 모양을 가지며 DRAFT에는 published_at NULL, PUBLISHED에는 실제 시각을 넣습니다.
INSERT INTO members (email, password_hash, name, status, created_at)
VALUES ('sora@example.com', '{demo}hash-sora', '김소라', 'ACTIVE', '2026-07-02 01:00:00');
SET @sora_id = LAST_INSERT_ID();
INSERT INTO members (email, password_hash, name, status, created_at)
VALUES ('doyun@example.com', '{demo}hash-doyun', '이도윤', 'ACTIVE', '2026-07-03 02:00:00');
SET @doyun_id = LAST_INSERT_ID();
INSERT INTO posts (
author_id, title, content, status, view_count,
created_at, updated_at, published_at, request_key
) VALUES
(
@sora_id, 'SELECT 실습', '필요한 열만 조회하는 방법을 정리합니다.',
'PUBLISHED', 45,
'2026-07-04 01:00:00', '2026-07-04 01:45:00', '2026-07-04 01:45:00', 'post-sora-001'
),
(
@sora_id, 'JOIN 예습', '회원과 게시글의 관계를 게시 전에 정리합니다.',
'DRAFT', 0,
'2026-07-05 01:00:00', '2026-07-05 01:00:00', NULL, 'post-sora-002'
),
(
@doyun_id, 'WHERE 조건', '조건식과 경계값을 예제로 설명합니다.',
'PUBLISHED', 35,
'2026-07-05 02:00:00', '2026-07-05 02:35:00', '2026-07-05 02:35:00', 'post-doyun-001'
);회원·게시글의 필수 값과 선택값이 열 이름에 맞춰 저장되고 다음 장의 NULL·상태·시간 필터를 재현할 데이터가 생깁니다.
개선 실행 결과email | posts | published | drafts
min@example.com | existing | existing | existing
sora@example.com | 2 | 1 | 1
doyun@example.com | 1 | 1 | 0심화 읽기: (SELECT id FROM members WHERE email = 'sora@example.com')처럼 한 값만 돌려주는 SELECT를 VALUES 안에 둘 수도 있습니다.
이를 스칼라 서브쿼리라고 하며 6장에서 결과 모양과 오류를 배웁니다.
지금은 부모 INSERT→생성 PK 확인→자식 INSERT라는 기본 흐름만 이해하면 됩니다.
표본 시각은 UTC 규칙이며 화면에서는 사용자 환경의 시간대로 변환합니다.
삽입 결과와 분포 확인
마지막 구문의 ROW_COUNT만 믿지 않고 회원별 게시글 수와 검사 위반 가능성을 조회합니다.
아래 쿼리의 LEFT JOIN과 GROUP BY는 4~5장에서 배우는 사후 검증용 심화 구문입니다.
지금은 결과에서 sora 2건·doyun 1건과 잘못된 상태 조합 0건만 확인하면 됩니다.
SELECT
m.email,
COUNT(p.id) AS post_count,
SUM(p.status = 'PUBLISHED') AS published_count,
SUM(p.status = 'DRAFT') AS draft_count,
SUM(p.published_at IS NULL) AS null_published_at_count
FROM members AS m
LEFT JOIN posts AS p ON p.author_id = m.id
GROUP BY m.id, m.email
ORDER BY m.email;
SELECT
COUNT(*) AS broken_rows
FROM posts
WHERE (status = 'PUBLISHED' AND published_at IS NULL)
OR (status = 'DRAFT' AND published_at IS NOT NULL);sora는 2건, doyun은 1건이고 broken_rows가 0이어야 합니다.
LEFT JOIN의 건수(*)가 아니라 자식 PK를 세어 게시글 0건 회원도 0으로 봅니다.
다중 VALUES 중 한 행이 제약 조건을 위반하면 구문 전체가 실패하는지 스토리지 엔진의 동작과 오류를 확인합니다.
InnoDB 구문 원자성이 표본 일부만 남는 것을 막지만 애플리케이션 재시도는 같은 요청을 다시 넣어 중복을 만들 수 있습니다.
외부 요청 키에 UNIQUE를 두거나 멱등성 정책을 설계합니다.
대량 가져오기는 한 거대한 INSERT보다 검증용 임시 적재·배치·거부 기록이 필요합니다.
한 다중-행 구문의 두 번째 행만 빈 본문으로 바꾸어 구문 전체가 실패하는지 확인한 뒤 정상값으로 복원합니다.
view_count와 version을 열 목록에서 생략한 경우 기본값 0이 적용되는지 확인합니다.
이메일 서브쿼리를 없는 주소로 바꾸어 최종 오류가 NOT NULL인지 FK인지 관찰하고 오류가 실제 루트 원인을 얼마나 설명하는지 적습니다.
개인정보와 비밀값을 SQL 예제·로그에 넣지 않습니다.
가져오기 출처와 외부 참조를 나중에 저장하면 중복·오류를 추적할 수 있습니다.
삽입 처리량만 높이기 위해 제약을 끄지 않고 배치 크기와 트랜잭션 길이를 측정합니다.
보조 인덱스와 복제본 비용은 운영 심화에서 다룹니다.
기본값 CURRENT_TIMESTAMP는 서버가 구문을 평가한 시각이며 애플리케이션이 전달한 사건 시각과 다릅니다.
과거 게시글을 가져오기할 때 애플리케이션 수신 시각과 게시글의 created_at을 구분합니다.
빈 문자열 이메일은 NOT NULL을 만족할 수 있으므로 형식 검증과 공백 제거 정책이 필요합니다.
다중-행 안에서 같은 UNIQUE 이메일 두 개를 넣으면 구문 전체에서 오류가 발생합니다.
INSERT IGNORE는 오류를 경고로 낮출 수 있어 손실을 명시적으로 허용할 때만 사용합니다.
여기서 만든 분포는 ch3의 WHERE·ORDER BY, ch4의 GROUP BY, ch5의 JOIN 결과 예시가 공유합니다.
실습을 반복할 때 무작정 TRUNCATE하지 않고 이 문서의 INSERT와 예상 행 건수를 기준선으로 사용합니다.
앞 문서에서 추가한 request_key는 API 재시도에도 같은 값을 사용해 중복 게시글을 막습니다.
INSERT 검토 기준
| 판단 축 | 확인할 질문 |
|---|---|
| 열 목록 | 물리 순서가 아닌 이름으로 값을 대응했는가? |
| 생략 | 기본값·NULL·자동 생성 중 어느 의미인지 아는가? |
| 부모 | FK 대상이 유일하게 존재하는가? |
| 배치 | 한 행 오류와 재시도의 원자성·중복을 정의했는가? |
| 일치 검사 | 입력 수·관계·상태 조합을 후속 쿼리로 검증했는가? |
INSERT 문만 보고 승인하지 않고 입력 원본의 식별 키, 중복 정책, 제약 오류 처리, 성공 후 일치 검사 쿼리까지 하나의 쓰기 규칙으로 봅니다.
연습 문제
새 회원 soo@example.com을 넣고 초안 게시글과 공개 게시글을 한 다중-행 INSERT로 추가하세요.
PK 숫자를 가정하지 말고 이메일 서브쿼리를 사용하며 게시 상태·시각 조합을 지키세요.
해설과 예시 답안
먼저 후보 키가 유일한 회원을 만들고 두 게시글이 같은 스칼라 서브쿼리로 부모를 찾습니다.
각 행의 상태에 맞춰 published_at을 둡니다.
INSERT INTO members (email, password_hash, name, created_at)
VALUES ('soo@example.com', '{demo}hash-soo', '김수', '2026-07-06 00:00:00');
INSERT INTO posts (
author_id, title, content, status,
created_at, updated_at, published_at, request_key
) VALUES
((SELECT id FROM members WHERE email = 'soo@example.com'),
'서브쿼리 초안', '스칼라 서브쿼리로 작성자를 찾습니다.', 'DRAFT',
'2026-07-06 01:00:00', '2026-07-06 01:00:00', NULL, 'post-soo-001'),
((SELECT id FROM members WHERE email = 'soo@example.com'),
'SELECT 공개', '공개 글은 게시 시각을 함께 저장합니다.', 'PUBLISHED',
'2026-07-06 02:00:00', '2026-07-06 02:55:00', '2026-07-06 02:55:00', 'post-soo-002');soo의 post_count 2, 공개 1, 초안 1이고 잘못된 상태 조합이 0인지 확인합니다.
핵심 정리
- INSERT에는 항상 필요한 열 목록을 명시해 스키마 순서 결합을 끊습니다.
- 기본값·NULL·
AUTO_INCREMENT의 생략 의미를 구분합니다. - 다중-행 구문의 오류 원자성과 재시도 중복을 함께 설계합니다.
- 삽입 후 후보 키·FK·상태 분포가 일치하는지 확인합니다.
다음 문서에서는 같은 조건식을 SELECT로 미리 보고 트랜잭션 안에서 UPDATE·DELETE 범위를 통제합니다.