SQL REMINDER — 개발자를 위한 SQL 작성 규칙
원리는 몰라도, 이것만은 기억하자!
강의 목표와 대상
SQL의 기본 문법(SELECT, WHERE, JOIN, GROUP BY)을 알고 업무에서 SQL을 작성해 본 백엔드·업무시스템 개발자를 위한 실무 강의다. SQL 튜닝 전문가를 만들기보다, 처음부터 성능 문제를 줄이는 SQL을 작성하고 스스로 점검하는 능력을 기른다. 실제 SQL 검증은 주로 PostgreSQL startdbpg 일반 테이블에서 진행하며, 일부 원문·보류 항목에는 BIG 계열 예제가 남아 있다. 초반 작성 규칙 상당수는 다른 DBMS에서도 응용할 수 있다.
난이도와 구성
•
01~03·05~09 · 초급 후반~중급 초반: BAD SQL → GOOD SQL → 핵심 규칙. 상세 실행계획보다는 결과 정확성, 읽는 범위, 중복·반복을 이해한다.
•
10 · 중급~중상급: LATERAL·WITH·MATERIALIZED 사용/제거를 학습한다. SECTION 11은 전체 보류이며, SECTION 12는 강의 구성에서 제외한다.
•
REMINDER 전체 목차 — Before / After
전체 43개 REMINDER · 강의 소개/통합 안내 2행. 성능·정확성은 중복 분류, 초급·중급·고급도 복수 태그를 허용한다. After가 현재 권장 학습 순서와 본문 구조의 기준이다. SECTION 11은 전체 보류, SECTION 12는 제외한다.
변경 요약
•
강의 진행 순서: 01 → 02 → 03 → 05 → 07 → 06 → 08 → 09 → 10. 04는 별도 강의가 아닌 통합 안내로 03 다음에만 표시한다.
•
09-01 → 07-01: 업무 결과 단위를 정하는 GROUP BY 규칙을 집계 섹션 첫 항목으로 이동했다. SQL과 측정값은 유지했다.
•
06-06 → SECTION 06 첫 강의: 스칼라 서브쿼리의 최대 1행·1컬럼 규칙을 먼저 학습한다. 다른 실습과 연결된 기존 06번 번호는 유지한다.
•
REMINDER 수 유지: 원래 43개 → 재배치 후 43개. 09-05는 검토 보류이며 다른 삭제/통합 항목의 번호도 재사용하지 않는다.
After — 추천 학습 순서 (현행)
아래 표를 실제 강의 순서로 사용한다. 행의 번호는 REMINDER 식별자이므로 학습 차례와 반드시 오름차순일 필요는 없다.
번호 | 핵심 규칙 | 구분 | 난이도 | 한 줄 요약 |
01 | 강의 소개 및 데이터 모델 | — | 초급 | SQL 작성 규칙의 학습 목표, 대상 및 실습 데이터 구조 |
02-01 | SELECT 절에는 필요한 컬럼만 사용하자 | 성능 | 초급 | 불필요한 컬럼 조회와 테이블 접근을 줄인다. |
02-02 | 존재 여부 확인에 전체 COUNT를 사용하지 말자 | 성능 | 초급 | 존재만 확인할 때 EXISTS 또는 LIMIT 1을 검토한다. |
02-03 | 전체 행 수는 COUNT(*)로 계산하자 | 정확성 | 초급 | 행 수와 특정 컬럼의 NULL 제외 건수를 구분한다. |
02-05 | 비교 값의 자료형을 컬럼과 맞추자 | 성능 정확성 | 초급 중급 | 불필요한 형변환을 피하고 정확한 비교를 수행한다. |
02-06 | NULL은 일반 값과 다르게 비교하자 | 정확성 | 초급 | IS NULL 등 NULL에 맞는 조건을 사용한다. |
02-07 | 테이블 별칭과 SQL ID로 명확하게 작성하자 | 정확성 | 초급 | 컬럼의 출처와 SQL의 관리 단위를 명확히 한다. |
02-08 | 페이징에는 반드시 ORDER BY를 사용하자 | 정확성 | 초급 | 일관된 순서와 동률 해소 기준을 지정한다. |
03-01 | WHERE에서 인덱스 컬럼을 함수로 가공하지 말자 | 성능 | 초급 중급 | 컬럼 가공 대신 원본 값의 검색 범위를 활용한다. |
03-02 | TIMESTAMP 일자 검색은 종료일 미만 범위를 쓰자 | 성능 정확성 | 초급 | 시작일 이상, 다음 날 미만으로 검색한다. |
03-03 | 문자형 날짜를 숫자로 변환해 비교하지 말자 | 성능 정확성 | 초급 중급 | 저장 형식에 맞춘 조건으로 변환 비용을 피한다. |
03-04 | 정확한 값 검색에 LIKE를 남용하지 말자 | 성능 | 초급 | 정확한 일치 조건에는 = 연산자를 검토한다. |
03-05 | 접두사 검색은 LEFT 대신 LIKE를 검토하자 | 성능 | 중급 | 접두사 검색의 인덱스 조건 적용 여부를 확인한다. |
03-06 | NOT IN 서브쿼리의 NULL 함정을 기억하자 | 정확성 | 중급 | NULL 때문에 결과가 달라지는 경우를 방지한다. |
03-07 | 부정 조건을 긍정 조건으로 바꿀지 검토하자 | 성능 | 중급 | 선택성 있는 긍정 조건으로 검색 범위를 줄일 수 있는지 본다. |
03-08 | 업무상 필수 조회 조건을 빠뜨리지 말자 | 성능 정확성 | 초급 | 불필요한 대량 조회를 막도록 필수 조건을 명시한다. |
04 | 독립 INDEX 강의 통합 | — | — | 기존 내용은 SECTION 08 및 보류 중인 11의 선택 심화로 이동했다. |
05-01 | 조인 조건의 테이블과 컬럼을 정확히 연결하자 | 정확성 | 초급 | 잘못된 조인 키와 자기 자신과의 비교를 방지한다. |
05-02 | M:1:M 조인 | 정확성 성능 | 중급 | 다대일 관계를 연속 조인할 때 행 증폭과 합계 왜곡에 주의한다. |
05-03 | 복합키 일부를 누락한 JOIN을 주의하자 | 정확성 | 중급 | 복합 조인 키 전체로 올바르게 매칭한다. |
05-04 | LEFT JOIN의 ON과 WHERE를 구분하자 | 정확성 | 중급 | 외부 조인에서 필터 위치에 따른 결과 차이를 확인한다. |
05-05 | 불필요한 JOIN은 제거하되 무결성을 확인하자 | 성능 정확성 | 중급 | JOIN을 없애기 전에 키의 존재 및 중복 가능성을 검증한다. |
05-06 | 존재 여부만 필요하면 EXISTS를 고려하자 | 성능 정확성 | 중급 | JOIN 후 DISTINCT 대신 세미 조인 형태를 검토한다. |
07-01 | GROUP BY에 불필요한 컬럼을 넣지 말자 | 성능 정확성 | 중급 | 업무상 필요한 그룹 키만 사용한다. |
07-02 | DISTINCT 전에 불필요한 JOIN부터 확인하자 | 성능 정확성 | 중급 | JOIN으로 늘어난 행을 사후 중복 제거하지 않도록 한다. |
07-03 | 여러 집계값은 FILTER를 활용하자 | 성능 | 중급 | 같은 데이터를 반복 조회하지 않고 여러 조건을 집계한다. |
07-04 | 중복 제거가 불필요하면 UNION ALL을 쓰자 | 성능 정확성 | 초급 중급 | 필요 없는 중복 제거 및 정렬 비용을 피한다. |
06-06 | 스칼라 서브쿼리는 최대 한 행·한 컬럼 | 정확성 | 초급 중급 | 다중 행 반환 오류와 단일 값 보장을 구분한다. |
06-01 | 반복 스칼라 서브쿼리를 줄이자 | 성능 | 중급 | 동일한 테이블을 여러 번 조회하는 부분을 JOIN 등으로 통합한다. |
06-02 | 반복 집계는 선집계 JOIN과 비교하자 | 성능 | 중급 | 상관 집계 반복과 한 번의 GROUP BY 비용을 비교한다. |
06-04 | 기간별 반복 집계는 조건부 집계로 통합하자 | 성능 | 중급 | 한 번의 입력으로 여러 기간의 합계를 계산한다. |
08-02 | ORDER BY와 인덱스 정렬 순서를 확인하자 | 성능 정확성 | 중급 | 정렬 키와 인덱스 키의 일치 여부를 검토한다. |
08-03 | WAS 대신 DB에서 페이징하자 | 성능 | 초급 | 필요한 건수만 DB에서 제한해 가져온다. |
08-05 | 페이징 후 필요한 테이블만 JOIN하자 | 성능 | 중급 | 페이지에 속한 적은 행을 먼저 고르고 JOIN한다. |
08-06 | 스칼라 서브쿼리는 페이징 후 실행하자 | 성능 | 중급 | 조회할 페이지가 확정된 뒤 추가 상세 조회를 수행한다. |
09-02 | MAX 내부의 컬럼 가공을 피하자 | 성능 | 중급 | MAX를 원본 컬럼에 적용하고 결과를 가공한다. |
09-03 | 마지막 한 행은 동률까지 고려하자 | 성능 정확성 | 중급 | 최신 일시 동률에 대비해 유일한 정렬 기준을 정한다. |
09-04 | 상관 MAX를 반복하지 말고 먼저 집계하자 | 성능 | 중급 | 반복 최댓값 계산을 한 번의 그룹 집계로 바꾼다. |
09-05 | ROW_NUMBER로 그룹별 마지막 한 건을 선택하자 | 성능 정확성 | 중급 고급 | 그룹별 최신 한 행 선택 방법을 비교한다. 추가 검토 보류. |
10-01 | 선별된 회원부터 JOIN하고 집계하자 | 성능 | 중급 | 소수 대상의 조건을 먼저 적용해 주문 집계 범위를 줄인다. |
10-03 | 그룹별 최신 1건은 LATERAL + LIMIT | 성능 정확성 | 중급 고급 | 회원별 인덱스 탐색으로 최신 행 한 건을 가져온다. |
10-04 | 회원이 많으면 LATERAL보다 선집계를 검토하자 | 성능 | 중급 고급 | 대량 외부 행의 반복 탐색 대신 주문을 한 번에 집계한다. |
10-05 | WITH를 사용한다고 무조건 구체화되지는 않는다 | 성능 | 중급 고급 | 불필요한 MATERIALIZED로 조건 전달을 차단하지 않는다. |
10-06 | 중간 결과 재사용 전 조건부 집계를 검토하자 | 성능 | 중급 | MATERIALIZED보다 단일 COUNT FILTER가 단순할 수 있다. |
10-07 | 복수 참조 CTE는 NOT MATERIALIZED도 검토하자 | 성능 | 고급 | 참조마다 다른 조건을 내부에 전달할 수 있는지 비교한다. |
Before — 변경 전 목차 (비교용 보존)
이 표는 변경 이전의 43개 REMINDER 목록이다. 09-01과 SECTION 06·07의 배치가 다르며, 현재 학습용 목차는 위의 After를 기준으로 한다.
번호 | 핵심 규칙 | 구분 | 난이도 | 한 줄 요약 |
01 | 강의 소개 및 데이터 모델 | — | 초급 | SQL 작성 규칙의 학습 목표, 대상 및 실습 데이터 구조 |
02-01 | SELECT 절에는 필요한 컬럼만 사용하자 | 성능 | 초급 | 불필요한 컬럼 조회와 테이블 접근을 줄인다. |
02-02 | 존재 여부 확인에 전체 COUNT를 사용하지 말자 | 성능 | 초급 | 존재만 확인할 때 EXISTS 또는 LIMIT 1을 검토한다. |
02-03 | 전체 행 수는 COUNT(*)로 계산하자 | 정확성 | 초급 | 행 수와 특정 컬럼의 NULL 제외 건수를 구분한다. |
02-05 | 비교 값의 자료형을 컬럼과 맞추자 | 성능 정확성 | 초급 중급 | 불필요한 형변환을 피하고 정확한 비교를 수행한다. |
02-06 | NULL은 일반 값과 다르게 비교하자 | 정확성 | 초급 | IS NULL 등 NULL에 맞는 조건을 사용한다. |
02-07 | 테이블 별칭과 SQL ID로 명확하게 작성하자 | 정확성 | 초급 | 컬럼의 출처와 SQL의 관리 단위를 명확히 한다. |
02-08 | 페이징에는 반드시 ORDER BY를 사용하자 | 정확성 | 초급 | 일관된 순서와 동률 해소 기준을 지정한다. |
03-01 | WHERE에서 인덱스 컬럼을 함수로 가공하지 말자 | 성능 | 초급 중급 | 컬럼 가공 대신 원본 값의 검색 범위를 활용한다. |
03-02 | TIMESTAMP 일자 검색은 종료일 미만 범위를 쓰자 | 성능 정확성 | 초급 | 시작일 이상, 다음 날 미만으로 검색한다. |
03-03 | 문자형 날짜를 숫자로 변환해 비교하지 말자 | 성능 정확성 | 초급 중급 | 저장 형식에 맞춘 조건으로 변환 비용을 피한다. |
03-04 | 정확한 값 검색에 LIKE를 남용하지 말자 | 성능 | 초급 | 정확한 일치 조건에는 = 연산자를 검토한다. |
03-05 | 접두사 검색은 LEFT 대신 LIKE를 검토하자 | 성능 | 중급 | 접두사 검색의 인덱스 조건 적용 여부를 확인한다. |
03-06 | NOT IN 서브쿼리의 NULL 함정을 기억하자 | 정확성 | 중급 | NULL 때문에 결과가 달라지는 경우를 방지한다. |
03-07 | 부정 조건을 긍정 조건으로 바꿀지 검토하자 | 성능 | 중급 | 선택성 있는 긍정 조건으로 검색 범위를 줄일 수 있는지 본다. |
03-08 | 업무상 필수 조회 조건을 빠뜨리지 말자 | 성능 정확성 | 초급 | 불필요한 대량 조회를 막도록 필수 조건을 명시한다. |
04 | 독립 INDEX 강의 통합 | — | — | 기존 내용은 SECTION 08 및 보류 중인 11의 선택 심화로 이동했다. |
05-01 | 조인 조건의 테이블과 컬럼을 정확히 연결하자 | 정확성 | 초급 | 잘못된 조인 키와 자기 자신과의 비교를 방지한다. |
05-02 | M:1:M 조인 | 정확성 성능 | 중급 | 다대일 관계를 연속 조인할 때 행 증폭과 합계 왜곡에 주의한다. |
05-03 | 복합키 일부를 누락한 JOIN을 주의하자 | 정확성 | 중급 | 복합 조인 키 전체로 올바르게 매칭한다. |
05-04 | LEFT JOIN의 ON과 WHERE를 구분하자 | 정확성 | 중급 | 외부 조인에서 필터 위치에 따른 결과 차이를 확인한다. |
05-05 | 불필요한 JOIN은 제거하되 무결성을 확인하자 | 성능 정확성 | 중급 | JOIN을 없애기 전에 키의 존재 및 중복 가능성을 검증한다. |
05-06 | 존재 여부만 필요하면 EXISTS를 고려하자 | 성능 정확성 | 중급 | JOIN 후 DISTINCT 대신 세미 조인 형태를 검토한다. |
06-01 | 반복 스칼라 서브쿼리를 줄이자 | 성능 | 중급 | 동일한 테이블을 여러 번 조회하는 부분을 JOIN 등으로 통합한다. |
06-02 | 반복 집계는 선집계 JOIN과 비교하자 | 성능 | 중급 | 상관 집계 반복과 한 번의 GROUP BY 비용을 비교한다. |
06-04 | 기간별 반복 집계는 조건부 집계로 통합하자 | 성능 | 중급 | 한 번의 입력으로 여러 기간의 합계를 계산한다. |
06-06 | 스칼라 서브쿼리는 최대 한 행·한 컬럼 | 정확성 | 초급 중급 | 다중 행 반환 오류와 단일 값 보장을 구분한다. |
07-02 | DISTINCT 전에 불필요한 JOIN부터 확인하자 | 성능 정확성 | 중급 | JOIN으로 늘어난 행을 사후 중복 제거하지 않도록 한다. |
07-03 | 여러 집계값은 FILTER를 활용하자 | 성능 | 중급 | 같은 데이터를 반복 조회하지 않고 여러 조건을 집계한다. |
07-04 | 중복 제거가 불필요하면 UNION ALL을 쓰자 | 성능 정확성 | 초급 중급 | 필요 없는 중복 제거 및 정렬 비용을 피한다. |
08-02 | ORDER BY와 인덱스 정렬 순서를 확인하자 | 성능 정확성 | 중급 | 정렬 키와 인덱스 키의 일치 여부를 검토한다. |
08-03 | WAS 대신 DB에서 페이징하자 | 성능 | 초급 | 필요한 건수만 DB에서 제한해 가져온다. |
08-05 | 페이징 후 필요한 테이블만 JOIN하자 | 성능 | 중급 | 페이지에 속한 적은 행을 먼저 고르고 JOIN한다. |
08-06 | 스칼라 서브쿼리는 페이징 후 실행하자 | 성능 | 중급 | 조회할 페이지가 확정된 뒤 추가 상세 조회를 수행한다. |
09-01 | GROUP BY에 불필요한 컬럼을 넣지 말자 | 성능 정확성 | 중급 | 업무상 필요한 그룹 키만 사용한다. |
09-02 | MAX 내부의 컬럼 가공을 피하자 | 성능 | 중급 | MAX를 원본 컬럼에 적용하고 결과를 가공한다. |
09-03 | 마지막 한 행은 동률까지 고려하자 | 성능 정확성 | 중급 | 최신 일시 동률에 대비해 유일한 정렬 기준을 정한다. |
09-04 | 상관 MAX를 반복하지 말고 먼저 집계하자 | 성능 | 중급 | 반복 최댓값 계산을 한 번의 그룹 집계로 바꾼다. |
09-05 | ROW_NUMBER로 그룹별 마지막 한 건을 선택하자 | 성능 정확성 | 중급 고급 | 그룹별 최신 한 행 선택 방법을 비교한다. 추가 검토 보류. |
10-01 | 선별된 회원부터 JOIN하고 집계하자 | 성능 | 중급 | 소수 대상의 조건을 먼저 적용해 주문 집계 범위를 줄인다. |
10-03 | 그룹별 최신 1건은 LATERAL + LIMIT | 성능 정확성 | 중급 고급 | 회원별 인덱스 탐색으로 최신 행 한 건을 가져온다. |
10-04 | 회원이 많으면 LATERAL보다 선집계를 검토하자 | 성능 | 중급 고급 | 대량 외부 행의 반복 탐색 대신 주문을 한 번에 집계한다. |
10-05 | WITH를 사용한다고 무조건 구체화되지는 않는다 | 성능 | 중급 고급 | 불필요한 MATERIALIZED로 조건 전달을 차단하지 않는다. |
10-06 | 중간 결과 재사용 전 조건부 집계를 검토하자 | 성능 | 중급 | MATERIALIZED보다 단일 COUNT FILTER가 단순할 수 있다. |
10-07 | 복수 참조 CTE는 NOT MATERIALIZED도 검토하자 | 성능 | 고급 | 참조마다 다른 조건을 내부에 전달할 수 있는지 비교한다. |
교육 진행 원칙
1.
앞부분은 SQL 자체를 개선한다. 대부분의 규칙은 BAD/GOOD SQL 한 쌍과 핵심 이유·예외로 충분히 설명한다.
2.
뒷부분은 실행계획으로 근거를 찾는다. 문제가 될 수 있는 노드를 보고 개선 후보를 검증한다.
3.
실행계획은 실측과 예상을 구분한다. 현재 여러 REMINDER의 SQL·실행계획은 실제 PostgreSQL에서 검증했지만, 미검증·보류 사례는 예상 계획으로 명시하고 측정 수치를 임의로 기재하지 않는다.
4.
원문을 잃지 않는다. 기존 12개 섹션의 전문은 2026-10-14 백업에 별도 보존했다.
5.
일부 전문 주제는 선택 학습으로 분리한다. ROLLUP, 고급 Partial Index, 트랜잭션·락·VACUUM 심화 등을 모든 수강자의 필수 이수 범위로 두지 않는다.
참고 자료
SQL GUIDE for ORACLE, SQL GUIDE for MySQL, StartUPTuning_PG_20250616.pdf, PostgreSQL 실습 SQL, 프로젝트 SQL 튜닝 사례를 교육의 출발점으로 사용한다. 이 자료를 토대로 예제를 BIG 계열 테이블에 맞춰 재구성했으며, 외부 자료에서 확인하지 못한 성능 수치는 임의로 추가하지 않는다.
SECTION 04 통합 안내: 독립 인덱스 강의를 제외하고 기존 04-01~04-04·04-06은 SECTION 11의 선택 심화로, 04-05는 SECTION 08의 정렬·페이징 실습으로 통합했다. 기존 SECTION 04 페이지는 이동 안내와 원문 백업 경로를 위해 유지한다. SECTION 05~11의 번호는 변경하지 않는다.
학습 페이지 바로가기
실제 진행 순서는 위 After 표를 따른다. 아래 개별 페이지는 기존 SECTION 번호순으로 배치되어 있으며, 07을 06보다 먼저 학습한다.
편집 상태: SECTION 01~10의 현행 REMINDER 43개를 After 기준으로 재배치했다. 실측된 사례와 검토 보류·미검증 사례를 구분하고, 영상 제작은 별도 진행한다.


