원문: Busting SQL Migration Myths: How New SQL Features Make Lift-and-Shift to Lakehouse Easier 저자: Abhishek Dey, Laurent Léturgez 게시일: 2026년 8월 20일
요약
- 레거시 저장 프로시저는 Python/Spark로 전면 재작성하는 대신, **한 줄씩 Databricks SQL Scripting으로 “번역”**하여 비즈니스 로직을 그대로 보존하며 이관할 수 있습니다.
- 임시 테이블, 커서, 제어 흐름, 트랜잭션 등 기존에 재작성이 필요하다고 여겨지던 요소들이 Databricks SQL에서 네이티브로 지원됩니다.
- 기계적 번역으로 복잡한 프로시저의 마이그레이션 기간을 50~75%까지 단축할 수 있고, 이관된 프로시저는 Unity Catalog 거버넌스까지 얻습니다.
여러분의 웨어하우스 어딘가에서는, 수백 개의 저장 프로시저가 매일 밤 깨어나 조용히 비즈니스를 돌아가게 합니다. 그것들은 오래전, 지금은 회사를 떠난 지 오래인 여러 SQL 개발자들이 작성한 것입니다. 중첩된 커서가 있습니다. 즉석에서 임시 테이블을 만듭니다. 여러 테이블에 걸친 업데이트를 하나의 트랜잭션으로 묶습니다. 그리고 47번째 줄 어딘가에는 그저 이렇게 적힌 주석이 있습니다: “이 부분은 절대 바꾸지 마시오.” 이제 이 프로시저들을 온전히 이해하는 사람은 아무도 없습니다. 그런데도 모두가 여기에 의존합니다. 매출 대시보드, 재무 마감, 운영 리포트, 이 모든 것이 이런저런 방식으로 결국 이 절차적(procedural) SQL 비즈니스 로직 계층으로 거슬러 올라갑니다. 데이터를 Lakehouse로 옮기는 일은 이미 잘 이해되어 있습니다. 마찰은 어느 데이터 웨어하우스 마이그레이션에서든 절차적 핵심부에 있었습니다. 저장 프로시저, 트랜잭션 처리, 임시 테이블, 제어 흐름, 그리고 여전히 많은 기업이 SQL 역량 위에서 돌아간다는 사실입니다. 마이그레이션 이야기가 나올 때마다 이 프로시저들이 가장 먼저 지목되는 대상이었습니다: “저것을 최소한의 변경으로 돌릴 수 있기 전에는 옮길 수 없습니다. 우리 기업은 여전히 SQL 중심으로 무겁게 돌아갑니다.” 그래서 우리는 여러분이 지금 떠올리고 있을 법한 사용 사례 — 여러 마이그레이션에서 보아 온 복합적인 프로시저 — 를 하나 골라, Lakehouse 위에서 하나하나 시연해 보기로 했습니다. 이 예시는 Oracle 마이그레이션 사용 사례를 바탕으로 하지만, 어떤 데이터 웨어하우스(레거시든 클라우드 기반이든)에도 적용할 수 있습니다.
원래의 비즈니스 로직을 가져오기
이 예시 프로시저는 일일 주문을 처리합니다. 처리되지 않은 주문을 임시 테이블에 스테이징하고, 고객 마스터(customer master)에 대해 검증하며, 실패 건을 하나씩 순회하며 각 반려(rejection)를 개별적으로 로깅한 뒤, 지역별 매출 요약을 업데이트하고 모든 주문을 처리 완료로 표시합니다. 이 모든 것이 실패 시 롤백되는 하나의 트랜잭션 안에서 이루어집니다. 깨져서는 안 되는 하나의 야간 잡(job)입니다. 예전에는 이를 이관하려면 Python과 Spark로 통째로 재작성해야 했습니다. 몇 주의 작업, 새로 찾아내야 할 버그들, 그리고 더 이상 자신의 비즈니스 로직을 유지보수할 수 없게 된 SQL 팀 말입니다. 우리는 그것을 재작성하지 않았습니다. 번역했습니다.이제 Databricks 위에 토대를 놓기
모든 프로시저는 시그니처와 안전망에서 시작합니다. 레거시는 본문을 BEGIN … EXCEPTION … END로 감쌌습니다. Databricks는 대신 DECLARE EXIT HANDLER FOR SQLEXCEPTION을 씁니다. 같은 발상이지만 문법이 약간 다릅니다. 적절한 카탈로그와 스키마가 세션에 설정되어 있다고 가정합시다. 큰 차이는 코드에 있지 않습니다. 배포 이후에 벌어지는 일에 있습니다. Databricks에서는 프로시저가 Unity Catalog에 등록됩니다. 접근 제어, 컬럼 수준 리니지(column-level lineage), 그리고 모든 워크스페이스에 걸친 발견 가능성(discoverability)을 얻습니다. 기존 시스템에서 그것은 단 세 사람만 비밀번호를 아는 스키마 안에 살고 있었습니다.
참조: docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-ddl-create-procedure
그다음 임시 테이블을 다루기: 데이터 웨어하우스 마이그레이션에서 손쉬운 승리
원래 프로시저는 스테이징과 검증 실패를 위해 두 개의 임시 테이블을 만듭니다. 이는 나머지 로직이 의존하는 스크래치 공간(scratch space)입니다. Databricks에서 이것은 마이그레이션에서 가장 단순한 부분 중 하나가 됩니다. EXECUTE IMMEDIATE도, ON COMMIT PRESERVE ROWS도 필요 없습니다. 세션 스코프의 CREATE TEMP TABLE이 직접적인 대체물입니다. 다만 한 가지 작은 유의점이 있습니다. CREATE OR REPLACE TEMP TABLE은 아직 지원되지 않으므로, 같은 세션에서 재실행 가능하게 만들려면 먼저 DROP 하세요. 참조: docs.databricks.com/aws/en/tables/temporary-tables커서가 가장 어려운 부분이었다 — 그렇게 생각했을 뿐
이 부분은 모두가 재작성이 필요하리라 짐작한 대목입니다. 원래 프로시저는 검증 실패를 하나하나 순회하며 각 잘못된 주문을 반려하고 그 사유를 로깅합니다. 전형적인 커서 패턴입니다. 수십 년에 걸친 레거시(예를 들어 Oracle)의 손에 밴 습관이지요. Databricks의 SQL scripting은 Runtime 18.1부터 OPEN, FETCH, CLOSE로 커서를 네이티브로 지원합니다. %NOTFOUND 속성은 CONTINUE HANDLER FOR NOT FOUND가 됩니다. 루프 레이블과 LEAVE가 EXIT WHEN을 대체합니다.스크립팅 로직은 별일 아니었다
처리할 행이 없으면 건너뛰고 로깅하는 조건 검사는 거의 바뀌지 않았습니다. SELECT … INTO는 SET var = (SELECT …)가 됩니다. 나머지는 모두 동일합니다. 우리의 SQL scripting은 절차적 도구 전체를 지원합니다: IF/ELSE, WHILE, FOR, LOOP, REPEAT, LEAVE, ITERATE, SIGNAL/RESIGNAL. 코드베이스에 .GOTO와 .LABEL 지시문이 있는 Teradata BTEQ 스크립트가 있다면, 이는 LEAVE와 ITERATE를 사용하는 레이블 지정 루프로 매핑됩니다. 참조: docs.databricks.com/aws/en/sql/language-manual/sql-ref-scripting트랜잭션에서 비로소 현실이 되었다
이것이 마지막 조각이자, 마이그레이션을 실제로 성립시킨 부분이었습니다. 원래 프로시저는 regional_revenue를 업데이트하고, 주문을 처리 완료로 표시하며, 배치를 로깅합니다. 어느 한 부분이라도 실패하면 전부 롤백됩니다. 레거시 시스템에서 이것은 명시적 COMMIT을 동반한 암묵적 트랜잭션입니다. Databricks에서는 BEGIN ATOMIC … END가 동일한 의미론 — 성공 시 자동 커밋, 실패 시 자동 롤백 — 을 제공하며, 한 가지 중요한 이점이 더해집니다. 바로 행 수준 충돌 감지(row-level conflict detection)입니다. 같은 테이블에 쓰는 동시(concurrent) 배치들은 같은 행을 건드릴 때만 충돌합니다. 예를 들어 Oracle과 Snowflake는 둘 다 테이블 수준 잠금(table-level locking)을 사용해 순차 실행을 강제합니다. MERGE 문은 있는 그대로 Databricks로 이관할 수 있습니다. 명시적 COMMIT은 BEGIN ATOMIC이 처리하므로 사라졌습니다. 그리고 팀은 동시 배치 잡들이 서로를 밟고 넘어질까 걱정하기를 멈추었습니다. 이 패턴을 도입할 때 실무적으로 유의할 두 가지입니다.- 원자적(atomic) 블록 안에서 정의되는 모든 테이블은 catalogManaged 테이블 기능이 활성화되어 있어야 합니다. 기존 Delta 테이블에서는 다음과 같이 제자리에서(in place) 활성화할 수 있습니다:
ALTER TABLE <name> SET TBLPROPERTIES('delta.feature.catalogManaged'= 'supported'); - BEGIN ATOMIC은 최상위 수준(top level) — SQL 스크립트, 노트북 셀, 또는 SQL 잡 태스크 — 에 있어야 합니다.