SW 전공

SW 전공 이론 학습

이론 목록으로 돌아가기

SQL 핵심 문법과 결과 판독

SELECT의 논리적 처리 순서와 NULL, 조인, 서브쿼리, 집계·윈도우 함수, 데이터 변경 문장을 종합한다.

예상 읽기 8

1. 예제 스키마

이후 예시는 다음 두 테이블을 사용한다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
CREATE TABLE department (
    dept_id   INTEGER PRIMARY KEY,
    dept_name VARCHAR(50) NOT NULL
);

CREATE TABLE employee (
    emp_id     INTEGER PRIMARY KEY,
    emp_name   VARCHAR(50) NOT NULL,
    dept_id    INTEGER,
    salary     DECIMAL(12, 2),
    hire_date  DATE,
    manager_id INTEGER,
    FOREIGN KEY (dept_id) REFERENCES department(dept_id),
    FOREIGN KEY (manager_id) REFERENCES employee(emp_id)
);

2. SQL 명령의 분류

분류목적대표 명령
DDL객체와 구조 정의CREATE, ALTER, DROP, TRUNCATE
DML데이터 조회·변경SELECT, INSERT, UPDATE, DELETE, MERGE
DCL권한 제어GRANT, REVOKE
TCL트랜잭션 제어COMMIT, ROLLBACK, SAVEPOINT

COMMIT·ROLLBACK은 트랜잭션 상태를 제어하는 TCL로 구분한다.

3. SELECT의 논리적 처리 순서

SQL 문장을 작성하는 순서와 DBMS가 논리적으로 처리하는 순서는 다르다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
작성 순서
SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY

논리 처리 순서
FROM/JOIN
    ↓
WHERE
    ↓
GROUP BY
    ↓
HAVING
    ↓
SELECT
    ↓
DISTINCT
    ↓
ORDER BY
    ↓
행 제한(FETCH/LIMIT 등)

따라서 일반적으로 SELECT에서 만든 별칭을 같은 단계보다 먼저 처리되는 WHERE에서 사용할 수 없다. ORDER BY에서는 별칭 사용을 허용하는 DBMS가 많다.

4. NULL과 3값 논리

NULL은 0이나 빈 문자열이 아니라 알 수 없음 또는 값 없음을 나타낸다. NULL과의 비교 결과는 TRUE나 FALSE가 아니라 UNKNOWN이 된다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
-- 잘못된 조건
WHERE manager_id = NULL

-- 올바른 조건
WHERE manager_id IS NULL
  • COUNT(*)는 결과 행 수를 센다.
  • COUNT(column)은 해당 열이 NULL이 아닌 행만 센다.
  • 대부분의 집계함수는 NULL을 제외한다.
  • NOT IN의 목록이나 서브쿼리 결과에 NULL이 있으면 UNKNOWN 때문에 예상과 다른 결과가 나올 수 있다. 이때 NOT EXISTS가 더 안전한 경우가 많다.

5. 조인

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
employee.dept_id ─────────► department.dept_id
       FK                         PK
SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT e.emp_name, d.dept_name
FROM employee e
JOIN department d
  ON d.dept_id = e.dept_id;
조인결과
INNER JOIN양쪽 조인 조건을 만족하는 행만 반환
LEFT OUTER JOIN왼쪽 행을 모두 보존하고, 일치하지 않는 오른쪽 열은 NULL
RIGHT OUTER JOIN오른쪽 행을 모두 보존
FULL OUTER JOIN양쪽의 불일치 행까지 모두 보존
CROSS JOIN모든 행 조합을 생성
SELF JOIN같은 테이블을 서로 다른 별칭으로 조인

외부조인에서 오른쪽 테이블 조건을 WHERE에 잘못 두면 NULL 확장 행이 제거되어 내부조인처럼 동작할 수 있다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
-- 영업 부서가 아니어도 사원을 모두 유지하려면 조건을 ON에 둔다.
SELECT e.emp_name, d.dept_name
FROM employee e
LEFT JOIN department d
  ON d.dept_id = e.dept_id
 AND d.dept_name = '영업';

6. 서브쿼리와 집합 연산

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
-- 전체 평균보다 급여가 높은 사원
SELECT emp_name, salary
FROM employee
WHERE salary > (SELECT AVG(salary) FROM employee);
  • 단일행 서브쿼리는 =, >, < 등을 사용할 수 있다.
  • 다중행 서브쿼리는 IN, ANY, ALL, EXISTS를 사용한다.
  • 상관 서브쿼리는 바깥 행마다 내부 쿼리가 논리적으로 연관된다.
  • EXISTS는 서브쿼리 결과의 값보다 행 존재 여부를 판단한다.

집합 연산은 열 수와 대응 열의 자료형이 호환되어야 한다.

  • UNION: 합집합, 중복 제거
  • UNION ALL: 합집합, 중복 보존
  • INTERSECT: 교집합
  • EXCEPT 또는 MINUS: 차집합

7. 그룹과 윈도우 함수

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT dept_id,
       COUNT(*) AS employee_count,
       AVG(salary) AS avg_salary
FROM employee
WHERE salary IS NOT NULL
GROUP BY dept_id
HAVING COUNT(*) >= 3;

WHERE는 그룹화 전 개별 행을, HAVING은 그룹화 후 그룹을 필터링한다.

윈도우 함수는 행을 하나로 축약하지 않고 각 행에 분석 결과를 붙인다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT emp_name,
       dept_id,
       salary,
       RANK() OVER (
           PARTITION BY dept_id
           ORDER BY salary DESC
       ) AS salary_rank,
       AVG(salary) OVER (
           PARTITION BY dept_id
       ) AS dept_avg
FROM employee;
CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
GROUP BY: 여러 행 → 그룹당 한 행
윈도우 함수: 여러 행 → 행 수 유지 + 분석 열 추가

8. 데이터 변경과 트랜잭션

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
START TRANSACTION;

UPDATE employee
SET salary = salary * 1.05
WHERE dept_id = 10;

SAVEPOINT after_raise;

DELETE FROM employee
WHERE emp_id = 9999;

ROLLBACK TO SAVEPOINT after_raise;
COMMIT;

실제 트랜잭션 시작·세이브포인트 문법과 DDL의 자동 커밋 여부는 DBMS마다 다를 수 있다.

9. 결과 집합을 직접 추적하는 SQL

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
EMP(id,dept,sal,manager)
(1,A,50,NULL)
(2,A,80,1)
(3,B,70,NULL)
SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT dept, COUNT(*), COUNT(manager), AVG(sal)
FROM emp
GROUP BY dept;

A는 (2,1,65), B는 (1,0,70)이다. COUNT(*)는 행, COUNT(manager)는 NULL이 아닌 값만 센다.

10. NULL과 조인의 경계조건

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
-- 서브쿼리에 NULL이 있으면 기대와 달리 결과가 없을 수 있다.
SELECT * FROM A
WHERE x NOT IN (SELECT y FROM B);

-- 반례가 없는지 검사하는 방식이 안전하다.
SELECT * FROM A a
WHERE NOT EXISTS (
  SELECT 1 FROM B b WHERE b.y=a.x
);

다만 바깥쪽 a.x가 NULL인 행까지 제외하려는 업무 규칙이라면 a.x IS NOT NULL을 별도로 명시해야 한다. NOT EXISTSNOT IN은 NULL 입력까지 언제나 동일한 의미가 아니다.

외부조인에서 보존되지 않는 쪽 조건을 WHERE에 두면 NULL 확장 행이 제거된다. 보존하려는 조건이라면 ON 절 배치를 검토한다.

11. 순위·누적과 재귀 CTE

점수RANKDENSE_RANKROW_NUMBER
100111
90222
90223
80434

동률 행의 ROW_NUMBER를 재현 가능하게 하려면 고유한 추가 정렬 키를 넣는다. 행 단위 누적은 ROWS UNBOUNDED PRECEDING을 명시하면 RANGE의 peer 처리와 구분할 수 있다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
WITH RECURSIVE n(x) AS (
  SELECT 1
  UNION ALL
  SELECT x+1 FROM n WHERE x<4
)
SELECT SUM(x) FROM n; -- 10

12. 논리 처리 순서

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY

SELECT 별칭을 WHERE에서 일반적으로 바로 사용할 수 없는 이유와, HAVING이 집계 뒤에 적용되는 이유를 이 순서로 판단한다.

확인 문제

  1. COUNT(*)와 COUNT(col)의 차이는?
  2. NOT IN 서브쿼리에 NULL이 있으면 왜 주의해야 하는가?
  3. RANK와 DENSE_RANK의 동률 뒤 차이는?
  4. LEFT JOIN 오른쪽 조건을 WHERE에 두면?
  5. 일반 SQL 논리 처리 순서는?