동적 SQL·SQL 테스트·실행계획·DBMS 방언
동적 SQL은 실행 시점에 SQL 텍스트를 구성·전달하여 처리하는 방식이며 문장 구조가 매번 바뀌어야 하는 것은 아니다. 값 바인딩은 문장 구조와 입력값을 분리한다. SQL 테스트는 기대 결과와 실제 값을 비교하고, 실행계획 분석은 접근 경로와 비용을 점검한다. 제품별 방언은 공통 SQL 개념과 구분해 해석한다.
정적 SQL과 동적 SQL
정적 SQL은 프로그램 작성 시 문장 구조를 미리 정해 두는 방식이다. 동적 SQL은 실행 시점에 SQL 문장을 문자열로 구성하거나 전달하여 처리·실행하는 방식이다. 실행 중 테이블·열·조건을 선택할 때 유용하지만, 구조가 매번 바뀌어야만 동적 SQL인 것은 아니다. 같은 모양의 문자열을 실행 시점에 동적 실행 기능으로 처리하는 경우도 있다.
| 구분 | 핵심 기준 | 예 |
|---|---|---|
| 정적 SQL | 문장이 미리 정의돼 프로그램의 SQL 처리 과정에 포함됨 | 고정된 직원 조회 |
| 동적 SQL | 실행 시점에 SQL 텍스트를 받아 처리함 | 실행 중 선택한 테이블을 조회 |
| 준비된 문장 | 문장을 준비한 뒤 매개변수를 넣어 실행하는 방식 | 같은 조회에 부서 번호만 교체 |
준비된 문장은 동적 SQL의 반대말이 아니다. 문장의 준비·실행 방식과 문장 텍스트가 정해지는 시점을 구분한다. 입력값만 달라진다는 이유로 매번 SQL 문자열을 새로 만들 필요는 없다.
바인드 변수
바인드 변수는 SQL 구조와 데이터 값을 분리한다. 다음 :dept_id는 실행 시 값을 전달할 자리를 나타내는 개념 표기다.
SELECT emp_id, emp_name
FROM employee
WHERE dept_id = :dept_id;
부서 번호가 10에서 20으로 바뀌어도 조회할 테이블과 열은 같다. 값을 문자열에 직접 이어 붙이면 따옴표 처리 오류나 SQL 삽입 위험이 생길 수 있다. 다만 테이블명·열명·ASC 같은 구문 요소는 일반적인 값 바인드로 대체하지 않는다. 동적으로 선택해야 한다면 허용된 이름과 구문만 사용한다.
Oracle PL/SQL의 EXECUTE IMMEDIATE는 대표적인 동적 SQL 실행 구문이다. 이 이름은 표준 SQL 전체에 공통인 키워드가 아니라 제품별 절차형 확장이라는 점만 구분한다.
SQL 텍스트와 바인드 값
WHERE id = ?라는 SQL 텍스트와 값 7을 분리해서 전달한다. 테이블명·연산자·정렬 방향은 일반 값 바인딩 대신 허용 목록을 검사한다. 동적 SQL은 실행 시 SQL 텍스트를 처리하며 매번 문자열이 달라야 하는 것은 아니다.
SQL 테스트: 실행 성공과 결과 정확성은 다르다
SQL 테스트는 입력 데이터와 기대 결과를 정해 놓고 실제 결과를 비교하는 일이다. 문법 오류가 없어도 조회 조건, 집계, 영향 행 수 또는 변경 범위가 틀릴 수 있다.
| 확인 대상 | 확인할 내용 |
|---|---|
| 조회 | 선택된 행·열, NULL, 중복, 집계값, 요구한 정렬 |
| 입력·수정·삭제 | 대상 행 수, 바뀐 값, 비대상 행의 유지 |
| 제약조건 | 중복 키, 부모 없는 외래키, NULL 및 범위 위반의 거부 |
| 권한 | 허용 계정은 성공하고 제한 계정은 거부되는지 |
| 트랜잭션 | 확정 후 값과 취소 후 값이 의도에 맞는지 |
기본 흐름은 준비 → 실행 → 기대 결과 비교 → 테스트 데이터 정리다. 정상값뿐 아니라 경계값과 오류값도 확인한다.
UPDATE inventory
SET quantity = quantity - 2
WHERE item_id = 10 AND quantity >= 2;
item_id가 기본키라고 하자. 실행 전 수량이 5면 영향 행 수 1·수량 3, 수량이 1이면 영향 행 수 0·수량 1이다. 상품 10이 없으면 영향 행 수는 0이다. 따라서 0행 변경을 업무상 정상으로 볼지 실패로 볼지까지 정해야 한다.
정렬을 검사하려면 ORDER BY가 필요하다. 현재 우연히 나온 순서나 단순한 '실행 성공' 메시지는 정답의 증거가 아니다.
실행계획과 옵티마이저
실행계획은 SQL을 수행할 접근 경로, 조인 순서, 정렬·집계 등의 연산을 나타낸다. 옵티마이저는 같은 결과를 얻는 여러 계획 중 하나를 선택한다.
| 방식 | 선택 기준 | 주의할 점 |
|---|---|---|
| 규칙 기반 최적화(RBO) | 미리 정한 우선순위·규칙 | 실제 데이터 분포가 충분히 반영되지 않을 수 있음 |
| 비용 기반 최적화(CBO) | 통계와 비용 모델에 따른 예상 비용 | 통계가 부정확하면 계획 선택도 부정확해질 수 있음 |
테이블 전체 스캔은 여러 행을 넓게 읽고, 인덱스 접근은 조건에 맞는 위치를 찾아 읽는다. 작은 테이블이나 대부분의 행을 조회하는 상황에서는 전체 스캔이 합리적일 수 있다. 인덱스 사용 여부만으로 좋은 계획을 판정하지 않는다.
| 조인 실행 방식 | 기본 원리 |
|---|---|
| 중첩 루프 조인 | 바깥 행마다 안쪽에서 대응 행을 찾음 |
| 정렬 병합 조인 | 조인 키로 정렬된 두 입력을 순서대로 병합 |
| 해시 조인 | 해시 구조를 만들고 같은 키를 찾아 결합 |
이들은 INNER·OUTER 같은 결과 구성 방식이 아니라 물리적인 수행 방법이다. 내부 자료구조 구현보다 어떤 접근이 반복되며 불필요하게 많은 행을 읽는지 파악하는 데 초점을 둔다.
추정 계획과 실제 실행 통계
추정 계획의 행 수와 비용은 예상값이며 비용 숫자를 실행 시간의 밀리초로 읽지 않는다. 실제 실행 통계가 붙은 계획은 실제 처리 행 수·시간 등을 보여 준다. 둘 사이의 큰 차이는 통계와 조건을 다시 확인할 단서다.
PostgreSQL에서 EXPLAIN은 추정 계획을, EXPLAIN ANALYZE는 SQL을 실제 실행한 결과와 계획을 보여 준다. 따라서 변경 SQL의 실제 실행 분석에는 데이터 변경이 발생할 수 있다. 제품별 명령을 서로 바꾸어 외우기보다 실제로 실행되는지를 확인한다.
튜닝은 결과 정확성을 보존한 상태에서 SQL·인덱스·통계를 점검하고, 같은 데이터와 조건에서 변경 전후의 처리 시간·I/O를 비교하는 과정이다.
DBMS 방언의 의미
방언은 같은 목적의 SQL이 제품별로 다른 문법·함수·자료형으로 표현되는 차이다. 일반 SQL, 절차형 확장 언어, 도구 전용 명령을 구분한다.
| 목적 | 대표 표기 |
|---|---|
| 행 수 제한 | FETCH FIRST, LIMIT, TOP |
| NULL 대체 | COALESCE, 제품별 NVL·ISNULL |
| 절차형 SQL | PL/SQL, PL/pgSQL, T-SQL 등 |
표준 개념을 이해한 뒤 문제에 명시된 DBMS의 문법을 적용한다. 어느 제품도 지정되지 않은 문제에서 자동 커밋, NULL 정렬 위치, 함수의 세부 동작을 한 제품 기준으로 단정하지 않는다. 제품별 버전 변화나 드라이버 API 전체를 암기할 필요는 없다.
SQL의 문법·의미와 통계의 역할
키워드와 문장 배치가 문법적으로 맞아도 존재하지 않는 테이블·열을 참조하면 올바른 질의가 아니다. SQL 처리에서는 문장 형식뿐 아니라 참조 이름, 자료형, 접근 권한 등도 검사한다. SQL 문법 오류가 없다는 사실만으로 참조 대상이나 업무 결과가 맞다는 결론을 내리지 않는다.
옵티마이저 통계에는 행 수, 서로 다른 값의 수, 값의 분포 등 계획 선택에 필요한 추정 정보가 들어간다. 통계를 갱신하는 일은 일반적인 의미에서 고객의 주소나 주문 금액을 수정하는 DML과 다르다. 데이터가 크게 바뀌어 통계가 낡았다면 실행계획의 추정과 실제 처리량이 어긋날 수 있다. 그러나 통계 수집만으로 모든 질의가 반드시 빨라진다고 단정하지 않는다.