189. 동적 SQL (Dynamic SQL)

핵심 인사이트: SQL은 보통 딱딱하게 굳어있는 문장이다. 하지만 "사용자가 검색창에 이름을 입력하면 이름으로 조회하고, 안 쓰면 전체 조회하라" 같은 상황에선 쿼리가 고무줄처럼 변해야 한다. 문자열 조각들을 런타임에 이리저리 레고처럼 조립해서 그 자리에서 실행시키는 마법이 동적 SQL이다.

Ⅰ. 정적 SQL과 동적 SQL의 비교

프로그래밍 언어(Java, Python 등)나 절차적 SQL(PL/SQL) 내에서 SQL을 실행할 때, 쿼리의 형태가 고정되어 있는지 변하는지에 따라 나뉩니다.

1. 정적 SQL (Static SQL)

  • 소스 코드에 SELECT * FROM EMP WHERE EMP_ID = 10처럼 쿼리 문장이 완벽하게 완성된 형태(Hard-coded) 로 고정되어 있는 방식입니다.
  • 컴파일 시점에 구문 오류(Syntax)를 100% 잡아낼 수 있고, DB가 미리 실행 계획을 짜둘 수 있어 성능이 빠릅니다.

2. 동적 SQL (Dynamic SQL)

  • 실행 시점(Runtime)에 사용자의 입력이나 조건에 따라 문자열(String) 조각들을 이어 붙여서(Concatenation) SQL 문장을 동적으로 생성해 내고 실행하는 방식입니다.

Ⅱ. 동적 SQL이 반드시 필요한 상황

  • 다이내믹 검색 조건: 쇼핑몰 검색 필터처럼, 고객이 '카테고리'만 고를 수도 있고 '가격 범위'를 추가할 수도 있어서 WHERE 절이 수십 가지로 변할 때.
  • 테이블/컬럼명 동적 변경: "올해 데이터를 조회하면 SALES_2024 테이블을 읽고, 작년이면 SALES_2023 테이블을 읽어라"처럼 FROM 절의 테이블 이름 자체가 변수일 때. (정적 SQL에서는 테이블명을 변수로 줄 수 없습니다)
  • DDL 실행: 프로시저 내부에서 테이블을 생성(CREATE)하거나 파티션을 자를(DROP) 때는 정적 SQL이 작동하지 않으므로 무조건 동적 SQL로 문자열을 조립해 실행해야 합니다.

Ⅲ. PL/SQL에서의 동적 SQL 실행 예시 (EXECUTE IMMEDIATE)

오라클에서는 조립된 문자열 쿼리를 EXECUTE IMMEDIATE 명령어에 던져주어 즉시 실행합니다.

DECLARE
    v_sql VARCHAR2(1000);  -- 쿼리를 담을 빈 문자열 변수
    v_table_name VARCHAR2(20) := 'EMPLOYEES'; -- 동적으로 변하는 테이블명
    v_count NUMBER;
BEGIN
    -- 문자열 조립: 'SELECT COUNT(*) FROM EMPLOYEES' 가 됨
    v_sql := 'SELECT COUNT(*) FROM ' || v_table_name;
    
    -- 조립된 쿼리를 그 자리에서 펑! 하고 실행하여 결과를 변수에 담음
    EXECUTE IMMEDIATE v_sql INTO v_count;
END;

Ⅳ. 동적 SQL의 치명적 단점과 방어책

단점설명
SQL 인젝션(Injection) 취약점사용자가 입력한 문자열을 쿼리에 그대로 이어 붙이기 때문에, 해커가 입력창에 ' OR 1=1 -- 같은 파괴적인 코드를 넣으면 데이터가 통째로 털립니다.
하드 파싱(Hard Parsing) 오버헤드동적으로 만들어지는 쿼리가 띄어쓰기 1개라도 다르면, DB 옵티마이저는 이를 "태어나서 처음 보는 쿼리"로 인식하여 매번 값비싼 실행 계획을 새로 짜야(하드 파싱) 합니다. 이는 DB CPU를 100%로 치솟게 합니다.
  • 해결책: 동적 SQL을 만들 때 문자열을 직접 더하지 말고, 반드시 바인드 변수(Bind Variable, ? 또는 :val) 를 사용하여 조립해야 인젝션을 막고 파싱 결과를 재사용할 수 있습니다.

📢 섹션 요약 비유: 정적 SQL이 공장에서 이미 단단하게 굳어져 나온 '기성품 블록'이라면, 동적 SQL은 현장에서 고객이 원하는 모양대로 잘라서 본드로 붙여 만드는 '맞춤형 진흙 블록'입니다. 유연하고 어떤 모양이든 짤 수 있지만, 매번 새로 빚어야 하니 속도가 느리고(하드 파싱), 고객이 이상한 걸 섞어달라면 그대로 폭발(인젝션)할 위험이 큽니다.