SQL 기초 쿼리문 작성법, 초보 실수 10가지부터
처음 SQL을 배울 때 가장 먼저 깨지는 건 문법이 아닙니다. WHERE 한 줄을 빼먹고 UPDATE를 돌린 순간입니다. 문법 정리 글은 인터넷에 넘치는데, 정작 “내가 왜 이 에러를 보고 있는지”는 아무도 안 알려줍니다. 이 글은 그 빈틈을 메우려고 썼습니다.
직접 초보자 질문 수백 개를 훑어보니, 막히는 지점은 신기할 만큼 똑같았습니다. 그래서 단순 문법 나열 대신, 실제로 손이 멈추는 10가지 순간을 기준으로 정리했습니다. 기준 DBMS는 입문자가 가장 많이 쓰는 MySQL로 잡되, 표준 SQL이나 PostgreSQL·Oracle과 달라지는 지점은 그때그때 짚겠습니다.
> 본 글은 2026년 기준 MySQL 및 표준 SQL(ISO/IEC 9075 계열) 기초 문법을 바탕으로 작성했습니다. 사용 중인 DBMS와 버전에 따라 일부 문법·기본 설정이 다를 수 있습니다.
Table of Contents
SQL 쿼리문 기초, 4가지 문장부터 잡습니다
SQL에서 데이터를 다루는 핵심 문장은 딱 네 개입니다. 이 네 개만 손에 익으면 기초는 끝났다고 봐도 됩니다.
| 문장 | 역할 | 기본 형태 |
|---|---|---|
| SELECT | 조회 | SELECT 컬럼 FROM 테이블 WHERE 조건 |
| INSERT | 추가 | INSERT INTO 테이블 (컬럼) VALUES (값) |
| UPDATE | 수정 | UPDATE 테이블 SET 컬럼=값 WHERE 조건 |
| DELETE | 삭제 | DELETE FROM 테이블 WHERE 조건 |
이 네 문장을 데이터 조작어, 즉 DML이라고 부릅니다. 테이블 자체를 만들고 바꾸는 CREATE·ALTER는 DDL, 권한을 다루는 GRANT는 DCL로 분류합니다. 기초 단계에서는 DML 네 개에 집중하면 됩니다.
여기서 미리 못 박을 게 있습니다. UPDATE와 DELETE의 WHERE는 선택이 아니라 안전장치입니다. 이 이야기는 아래에서 다시 합니다.
SELECT 절 순서, 외우지 말고 흐름으로 이해합니다
초보자가 가장 먼저 헷갈리는 건 절의 순서입니다. ORDER BY를 WHERE 앞에 쓰거나, LIMIT을 정렬보다 먼저 적었다가 문법 오류를 만납니다.

작성 순서는 항상 이렇게 고정입니다.
“`
SELECT 컬럼
FROM 테이블
WHERE 행_조건
GROUP BY 그룹_기준
HAVING 그룹_조건
ORDER BY 정렬
LIMIT 개수
“`
문제는 실제 실행되는 순서가 작성 순서와 다르다는 점입니다. 이걸 모르면 GROUP BY나 별칭에서 계속 막힙니다.
| 단계 | 실행 순서 | 하는 일 |
|---|---|---|
| 1 | FROM | 어떤 테이블을 볼지 정함 |
| 2 | WHERE | 행을 먼저 거름 |
| 3 | GROUP BY | 거른 행을 묶음 |
| 4 | HAVING | 묶은 그룹을 거름 |
| 5 | SELECT | 보여줄 컬럼 결정 |
| 6 | ORDER BY | 정렬 |
| 7 | LIMIT | 개수 자름 |
SELECT가 거의 마지막에 실행된다는 점만 기억해도, “왜 SELECT에서 만든 별칭을 WHERE에서 못 쓰지?” 같은 의문이 한 번에 풀립니다. WHERE가 SELECT보다 먼저 돌기 때문입니다.
WHERE 없는 UPDATE·DELETE, 가장 비싼 실수입니다
가장 흔하면서 가장 아픈 사고입니다. WHERE를 빼고 실행하면 테이블 전체 행이 바뀌거나 사라집니다.
“`sql
— 위험: id 조건이 없습니다
UPDATE members SET grade = ‘VIP’;
— members 테이블의 모든 회원이 VIP가 됩니다
DELETE FROM members;
— 전 회원이 삭제됩니다
“`
MySQL 공식 문서도 UPDATE에서 WHERE를 생략하면 모든 행이 영향을 받고, DELETE에서 조건을 빼면 전 행이 삭제된다고 명시합니다. 즉 이건 버그가 아니라 SQL이 시킨 대로 한 결과입니다.
해결책은 습관 하나로 끝납니다. 고치기 전에 같은 조건으로 SELECT를 먼저 돌립니다.
“`sql
— 1단계: 영향 범위 먼저 확인
SELECT * FROM members WHERE id = 42;
— 2단계: 결과가 의도한 행이 맞으면 동사만 바꿈
UPDATE members SET grade = ‘VIP’ WHERE id = 42;
“`
UPDATE·DELETE 작성 전 체크리스트
- WHERE 절이 있는가
- 같은 WHERE로 SELECT를 먼저 돌려 봤는가
- 결과 행 수가 예상과 맞는가
- 실서비스 DB라면 트랜잭션(BEGIN) 안에서 실행하는가
이 4줄짜리 체크리스트를 모니터에 붙여두는 것만으로 대형 사고의 90%는 막힙니다.
SELECT *, 편하지만 언제부터는 독입니다
초보자는 일단 다 보고 싶어서 SELECT *를 씁니다. 연습 단계에서는 괜찮습니다. 문제는 실무로 넘어갈 때 터집니다.
- 컬럼이 추가되면 조회 결과 구조가 말없이 바뀝니다
- 필요 없는 컬럼까지 읽어 성능이 떨어집니다
- 조인할 때 같은 컬럼명이 겹쳐
Column 'id' in field list is ambiguous에러가 납니다
기준은 단순합니다. **혼자 빠르게 확인할 때는 *, 코드나 리포트에 넣을 쿼리는 컬럼 명시.** 조인이 들어가는 순간부터는 무조건 테이블별칭.컬럼 형태로 적는다고 생각하면 편합니다.

NULL과 = NULL 오해, 결과가 텅 비는 이유
“분명 NULL인 행이 있는데 왜 안 잡히지?” 이 질문이 정말 많습니다. 범인은 이 한 줄입니다.
“`sql
— 항상 결과가 비어 있습니다
SELECT * FROM users WHERE phone = NULL;
“`
NULL은 ‘값이 없음/알 수 없음’을 뜻합니다. 그래서 =로는 비교 자체가 성립하지 않습니다. 비교 결과가 참도 거짓도 아닌 ‘미정’이 되어 아무것도 안 나옵니다. 올바른 방법은 전용 연산자입니다.
“`sql
SELECT * FROM users WHERE phone IS NULL; — 비어 있는 행
SELECT * FROM users WHERE phone IS NOT NULL; — 채워진 행
“`
NULL을 다른 값으로 바꿔서 보고 싶다면 COALESCE를 씁니다.
“`sql
SELECT name, COALESCE(phone, ‘미등록’) AS phone
FROM users;
“`
참고로 COUNT(phone)은 NULL을 세지 않습니다. 전체 행 수를 원하면 COUNT(*)를 써야 한다는 점도 같이 기억해 두세요. 데이터 개수가 이상하다 싶을 때 십중팔구 여기서 어긋납니다.
JOIN, 조건 하나 빠지면 행이 폭발합니다
조인은 두 테이블을 연결하는 기능인데, 연결 조건을 빼먹으면 행이 곱으로 불어납니다. 100행 × 100행이면 1만 행이 나오는 카테시안 곱입니다.
“`sql
— 위험: 연결 조건 없음
SELECT * FROM orders, users;
— 올바름: ON으로 연결 조건을 명확히
SELECT o.id, u.name
FROM orders AS o
INNER JOIN users AS u ON o.user_id = u.id;
“`
기초 단계에서 INNER JOIN과 LEFT JOIN 둘만 구분하면 충분합니다.
| 종류 | 결과 |
|---|---|
| INNER JOIN | 양쪽에 다 있는 행만 |
| LEFT JOIN | 왼쪽 테이블은 전부 + 짝이 없으면 NULL |
주문이 없는 회원까지 보고 싶다면 LEFT JOIN입니다. 조인을 쓸 때 지킬 원칙은 두 가지뿐입니다. 항상 테이블 별칭을 붙인다, 연결 조건은 WHERE가 아니라 ON에 적는다. 이 둘만 지켜도 ambiguous 에러와 행 폭발이 사라집니다.
GROUP BY, 표준과 MySQL이 다르게 동작합니다
집계는 데이터를 묶어 개수나 합계를 내는 기능입니다.
“`sql
SELECT department, COUNT(*) AS 인원
FROM employees
GROUP BY department;
“`
여기서 초보자가 자주 만나는 에러가 있습니다.
“`sql
— 표준 SQL에서는 에러
SELECT department, name, COUNT(*)
FROM employees
GROUP BY department;
“`
name은 부서별로 하나로 정해지지 않으므로, 집계 함수가 아니면서 GROUP BY에도 없는 컬럼은 표준 규칙상 쓸 수 없습니다. 표준 SQL은 집계 함수가 아닌 선택 컬럼을 모두 GROUP BY에 넣도록 규정합니다.
그런데 여기서 환경 차이가 생깁니다. MySQL은 ONLY_FULL_GROUP_BY 설정이 꺼져 있으면 위 쿼리가 그냥 돌아갑니다. 그래서 “내 노트북에서는 되는데 회사 서버에서는 에러”라는 상황이 벌어집니다. 이건 코드가 틀린 게 아니라 DBMS 설정이 다른 것입니다. 이식성을 생각하면 처음부터 표준 방식으로 적는 습관이 안전합니다.
WHERE와 HAVING의 차이도 여기서 정리하고 갑니다.
- WHERE: 묶기 전에 개별 행을 거름
- HAVING: 묶은 다음 그룹을 거름
“`sql
SELECT department, COUNT(*) AS 인원
FROM employees
WHERE status = ‘재직’ — 행 필터
GROUP BY department
HAVING COUNT(*) >= 5; — 그룹 필터
“`
LIKE·정렬·페이징에서 자주 새는 곳
ORDER BY 없는 LIMIT
페이징
LIKE와 와일드카드
“`sql
— 잘못: OR 뒤에 컬럼이 빠짐
WHERE name LIKE ‘%김%’ OR ‘%이%’;
— 올바름: 조건마다 컬럼을 다시 명시
WHERE name LIKE ‘%김%’ OR name LIKE ‘%이%’;
“`
%는 글자 수 제한 없는 임의 문자열, _는 정확히 한 글자를 뜻합니다. 둘을 헷갈리면 검색 결과가 엉뚱하게 나옵니다.
“`sql
— 매번 순서가 바뀔 수 있습니다
SELECT * FROM posts LIMIT 10;
— 정렬 기준을 정해야 결과가 안정적입니다
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10;
“`
정렬 없이 LIMIT만 쓰면 “상위 10개”의 기준이 없어 실행할 때마다 결과가 달라질 수 있습니다.
“`sql
— 3페이지(한 페이지 10개): 21번째부터 10개
SELECT * FROM posts
ORDER BY created_at DESC
LIMIT 10 OFFSET 20;
“`
OFFSET은 (페이지 번호 − 1) × 페이지당 개수로 계산합니다. 참고로 Oracle은 LIMIT 대신 FETCH FIRST n ROWS ONLY나 ROWNUM을 씁니다. DB를 옮길 일이 있다면 이 부분부터 손봐야 합니다.
에러 메시지로 원인 빠르게 찾기
상위 정리 글들이 대부분 다루지 않는 부분입니다. 에러 문구만 알면 절반은 해결됩니다.
| 에러 메시지 | 흔한 원인 | 해결 |
|---|---|---|
You have an error in your SQL syntax | 절 순서·오타·따옴표 누락 | 절 순서와 콤마·따옴표 확인 |
Column 'x' in field list is ambiguous | 조인 시 같은 컬럼명 | 테이블별칭.컬럼으로 명시 |
Unknown column 'x' in 'where clause' | 컬럼명 오타·별칭 오용 | 컬럼명·별칭 위치 확인 |
isn't in GROUP BY | 집계 외 컬럼 누락 | 컬럼을 GROUP BY에 추가 |
에러는 적이 아니라 가장 정확한 안내문입니다. 문구를 그대로 읽는 습관만 들여도 검색 시간이 확 줄어듭니다.
연습 루틴과 코딩 스타일
문법만 외우고 멈추면 실무 쿼리에서 또 막힙니다. 작은 실전 과제로 손을 풀어 두는 게 좋습니다.
- 연습 환경: SQLBolt, LeetCode, HackerRank 같은 온라인 플레이그라운드에서 설치 없이 시작 (※ 서비스 구성은 바뀔 수 있으니 현재 제공 여부는 직접 확인하세요)
- 추천 과제: “지난 7일 신규 가입자 수”, “일별 주문 수”, “매출 상위 5개 상품” 같은 미니 리포트 쿼리. SELECT·JOIN·GROUP BY·ORDER BY를 한 번에 씁니다
- 난이도 곡선: 단일 테이블 조회 → 조건·정렬 → 집계 → 조인 → 서브쿼리 순서
가독성을 위한 최소 규칙도 정해두면 나중이 편합니다. 키워드는 대문자, 절마다 줄바꿈, 테이블에는 짧은 별칭. 이 정도만 지켜도 한 달 뒤의 내가 자기 쿼리를 읽을 수 있습니다.
지금 SQL 플레이그라운드를 하나 열고, 위 미니 리포트 과제 세 개를 직접 쳐 보세요. 읽는 것과 치는 것의 차이가 거기서 갈립니다.
자주 묻는 질문
SQL 기초는 어떤 DBMS로 시작하는 게 좋나요?
자료와 예제가 가장 많은 MySQL을 권합니다. 기본 SELECT·INSERT·UPDATE·DELETE 문법은 표준 SQL을 따르므로, MySQL로 익혀 두면 PostgreSQL이나 Oracle로 넘어갈 때 LIMIT·날짜 함수 정도만 다시 익히면 됩니다.
WHERE col = NULL인데 결과가 안 나옵니다. 왜인가요?
NULL은 ‘값이 없음’이라 =로 비교할 수 없습니다. IS NULL 또는 IS NOT NULL을 써야 합니다. NULL을 다른 값으로 바꿔 보고 싶다면 COALESCE(컬럼, '대체값')을 사용하세요.
UPDATE를 실수로 WHERE 없이 돌렸습니다. 어떻게 예방하나요?
고치기 전에 같은 조건으로 SELECT를 먼저 돌려 영향 범위를 확인하는 습관이 가장 확실합니다. 실서비스 DB라면 트랜잭션(BEGIN) 안에서 실행해 문제가 생기면 ROLLBACK으로 되돌릴 수 있게 해두세요.
