원본 SQL
변경된 SQL

SQL Diff: 두 개의 SQL 파일을 온라인에서 비교

두 개의 SQL 쿼리나 스키마 파일을 붙여넣고, 포맷하고, 나란히 비교하세요. PostgreSQL, MySQL, SQL Server, SQLite, Oracle에서 동작합니다.

SQL diff 도구란?

두 개의 SQL 파일을 비교하기 위한, 브라우저에서 동작하는 무료 도구입니다. 왼쪽에 예전 CREATE TABLE을 붙여넣고 오른쪽에 새 것을 붙이면, 변경 사항이 문자 단위로 강조됩니다. 동일한 흐름이 분석 쿼리의 두 버전, ORM이 생성한 문장, pg_dump 출력에도 적용됩니다. 어떤 데이터도 사용자의 기기를 떠나지 않습니다.

diff는 텍스트 수준에서 동작합니다. SQL을 AST로 파싱하지 않고 의미적 동등성도 이해하지 않습니다: SELECT a, bSELECT b, a는 두 쿼리가 같은 행을 다른 순서로 반환하더라도 diff에는 다르게 보입니다. 코드 리뷰 작업의 95% (스키마 마이그레이션, 쿼리 리팩터링, ORM 출력 비교)에서는 텍스트 diff가 실제로 원하는 바입니다. 컬럼과 절의 순서가 팀이 변경을 읽는 방식의 일부이기 때문입니다.

400줄짜리 분석 쿼리에서 행 개수가 달라진 원인이 된 새로운 WHERE 절 하나를 찾아본 적이 있다면, 이 도구가 몇 초 안에 거기로 데려갑니다. SQL이 아닌 일반 텍스트라면 텍스트 diff 도구가 적절합니다. JSON 형식의 ORM 결과 페이로드라면 JSON diff가 객체 재정렬을 더 깔끔하게 처리합니다. XML 메타데이터로 작성된 레거시 DDL 익스포트라면 XML diff가 더 적합합니다.

diff가 실제로 동작하는 방식

내부적으로 diff는 문자 수준에서 동작하고, 그 다음 의미 패스가 강조 표시를 옮겨 임의의 구두점이 아닌 식별자, 키워드, 절 경계에 떨어지도록 합니다. 삽입은 오른쪽에서 녹색으로, 삭제는 왼쪽에서 빨간색으로 표시됩니다. 각 패널에는 Format 버튼이 있어 SQL을 일관된 들여쓰기와 한 줄에 한 문장으로 재배치하고, 비교 전에 대부분의 포맷 노이즈를 제거합니다.

SQL은 단일 언어가 아니라 방언의 집합입니다. ISO/IEC 9075 표준은 핵심 문법을 정의하지만, 모든 데이터베이스가 이를 확장합니다: PostgreSQL은 달러 인용 문자열과 RETURNING이 있고, MySQL은 백틱 식별자와 ON DUPLICATE KEY UPDATE가 있으며, SQL ServerTOP과 대괄호 식별자를 사용하고, SQLite는 거의 모든 것을 허용하며, Oracle에는 ROWNUMCONNECT BY가 있습니다. 방언 사이의 diff는 문제없이 동작합니다. 다만 도구는 어떤 방언에서 무효한 조각이라고 경고하지 않습니다.

또 하나 알아둘 점: 쿼리 텍스트를 비교하는 것은 쿼리 플랜을 비교하는 것과 다릅니다. 텍스트가 동일한 두 문장이 통계가 다르면 매우 다른 플랜을 만들 수 있고, 보기에 다른 두 문장이 동일한 플랜을 만들 수도 있습니다. 플랜 비교에는 psql의 EXPLAIN ANALYZE나 데이터베이스 클라이언트(DataGrip, DBeaver, SSMS)의 동등 기능이 적절합니다. 텍스트 리팩터링 리뷰에는 diff가 원하는 바입니다. 언어 자체에 대한 배경 자료는 Wikipedia에 있습니다.

세 단계로 SQL 비교하기

두 개의 텍스트 패널, 하나의 diff. 가입 없이, 업로드 없이, 서버 왕복 없이.

  1. 1

    SQL 붙여넣기 또는 업로드

    왼쪽에 예전 SQL을, 오른쪽에 새 SQL을 붙여넣으세요. 또는 양쪽 어느 쪽에서든 업로드를 눌러 .sql, .ddl, .psql 파일을 직접 불러올 수 있습니다. Sample 버튼은 도구를 먼저 보고 싶을 때 양쪽 패널을 작은 orders 스키마 예제로 채워줍니다.

  2. 2

    공정한 비교를 위해 양쪽을 포맷

    각 패널에서 Format을 클릭하면 SQL이 일관된 들여쓰기, 한 줄에 한 문장, 키워드 대문자로 재배치됩니다. 한쪽이 DataGrip으로 포맷된 쿼리이고 다른 쪽이 손으로 입력한 쿼리일 때 발생하는 노이즈를 없애므로, diff가 공백과 대소문자 차이가 아닌 진짜 스키마와 절 변경을 강조하게 됩니다.

  3. 3

    diff 읽기

    삭제는 왼쪽에 빨간 강조로, 삽입은 오른쪽에 녹색 강조로 나타납니다. 한쪽을 스크롤하면 다른 쪽이 따라옵니다. 각 헤더의 변경 카운터는 컬럼, JOIN, 술어, DDL 요소 전반에서 diff가 찾은 개별 편집의 개수를 알려줍니다.

SQL diff가 적절한 도구일 때

스키마 마이그레이션을 적용 전에 리뷰

마이그레이션 PR이 컬럼 세 개를 추가하고 인덱스 두 개를 조정합니다. 이전 CREATE TABLE과 새 것을 붙여넣으면 추가 사항이 두드러집니다: currency CHAR(3) NOT NULL 컬럼이 DEFAULT 없이 추가됐고, 이는 기존 행에서 실패합니다. 그것을 마이그레이션이 프로덕션에 돌기 전에 잡는 것, 새벽 2시에 배포 알람이 울린 후가 아닌 것, 그것이 이 워크플로의 전부입니다. VARCHAR(255)에서 VARCHAR(50)으로 컬럼 타입이 좁아지는 ALTER TABLE 문에도 같은 가치가 있습니다.

분석 쿼리 두 버전의 diff

어제 12,400행을 반환했던 보고서 쿼리가 오늘은 8,900행을 반환합니다. 어제 저장한 쿼리와 오늘 것의 diff를 떠보면 원인이 된 변경이 떠오릅니다: LEFT JOIN이 조용히 INNER JOIN이 됐거나, 대시보드를 정리하려는 누군가가 WHERE status <> 'cancelled'를 추가했거나. 텍스트 diff는 몇 초 안에 그 줄로 데려가지만, Slack에서 두 쿼리를 나란히 읽는 데에는 10분이 걸릴 것입니다.

앱 버전 간 ORM 생성 SQL 비교

Hibernate나 SQLAlchemy를 업그레이드한 뒤, 세 개의 JOIN이었던 쿼리가 추가 서브쿼리와 함께 네 개가 됐거나, 파라미터 바인딩이 ?에서 $1 플레이스홀더로 바뀌었습니다. 각 앱 버전의 쿼리 로그(psql log_statement = all, MySQL slow log, 또는 APM 도구)에서 SQL을 캡처하고 diff를 떠보세요. 별칭 이름은 노이즈가 많겠지만(t1, t2, generatedAlias0), 실제 구조 변경이 보통 diff가 진짜 편집으로 강조하는 유일한 것입니다.

DDL 리팩터링이 의도를 보존하는지 검증

user_ordersorders로 이름을 바꾸고 JSON blob 컬럼을 정규화된 테이블로 분리했습니다. 리팩터링 전후의 pg_dump --schema-only 출력을 붙여넣으면 모든 제약, 인덱스, 기본값을 보존했는지 diff가 명확히 보여줍니다. 주의해야 할 함정은 이름 변경 중 조용히 사라진 NOT NULL이나, 새 테이블에 다시 추가한 사람이 없어 떨어진 UNIQUE 제약입니다.

스테이징 스키마가 프로덕션과 일치하는지 확인

스테이징과 프로덕션에 대해 pg_dump --schema-only --no-owner를 실행하고 둘의 diff를 떠보세요. 차이는 정확히 다음 배포에 큐잉된 마이그레이션이어야 합니다. 그 외의 어떤 것이든, 추가 인덱스, 다른 컬럼 타입, 잊힌 테스트 테이블, 모두 실제 배포에서 물어뜯을 스키마 드리프트의 신호입니다. MySQL은 mysqldump --no-data, SQL Server는 SCRIPT TO, 또는 어떤 데이터베이스든 DataGrip의 스키마 익스포트로 같은 접근이 가능합니다.

방언 포팅 리뷰 (Postgres에서 MySQL)

PostgreSQL에서 MySQL로, 또는 그 반대로 쿼리를 포팅한다는 것은 어떤 함수, 타입, 절을 바꿔야 하는지를 추적한다는 뜻입니다: SERIALAUTO_INCREMENT, NOW()CURRENT_TIMESTAMP, RETURNINGLAST_INSERT_ID(). 원본과 포팅한 버전의 diff를 떠보면 모든 방언 치환이 떠오릅니다. diff는 대상 방언에서 SQL을 검증해 주지 않으니, 확인을 위해 여전히 psql이나 mysql을 통과시키되, 행별 보기는 어떤 결정을 두 번 확인해야 하는지 알려줍니다.

SQL 빠른 참조

이 diff가 가장 자주 표면화하는 방언과 파싱의 경계 사례에 대한 짧은 치트 시트. ISO SQL 표준과 주요 벤더 문서에 기반.

TopicWhat this tool does
방언 드리프트PostgreSQL, MySQL, SQL Server, SQLite, Oracle은 각자 ISO SQL 표준을 자체 구문으로 확장합니다. SERIAL, AUTO_INCREMENT, IDENTITY, GENERATED ALWAYS AS IDENTITY는 같은 아이디어를 쓰는 네 가지 방식입니다.
식별자 대소문자 폴딩PostgreSQL은 인용되지 않은 식별자를 소문자로 폴딩하므로 Usersusers는 같은 테이블입니다. MySQL은 lower_case_table_names와 기반 파일시스템에 의존합니다. SQL Server는 기본적으로 대소문자 구분이 없지만 collation을 따릅니다. 대소문자를 보존하려면 식별자를 "Users"(표준), 백틱(MySQL), 대괄호(SQL Server)로 인용하세요.
키워드 대소문자SQL 키워드는 어디서나 대소문자 구분이 없으므로 SELECTselect는 파서에 동일합니다. 관례는 키워드는 대문자, 식별자는 소문자로, 대부분의 스타일 가이드와 ISO 사양이 이를 따릅니다. 혼합 대소문자는 유효하고 실행되지만, sqlfluff와 sqlfmt 같은 도구가 정규화합니다.
주석 스타일두 가지 형식: -- 라인 주석(라인 끝에서 종료)과 /* 블록 주석 */(표준 SQL에서는 중첩 불가, 일부 방언은 허용). MySQL은 확장으로 # 라인 주석도 지원합니다. diff는 주석을 텍스트로 다루며 주석 편집도 다른 변경처럼 표시합니다.
문장 구분자세미콜론(;)은 표준 SQL에서 문장을 종료합니다. 대부분의 클라이언트는 스크립트 안의 문장 사이에 이를 요구합니다. 일부 클라이언트(SSMS)는 대신 GO를 배치 구분자로 사용하는데, 이는 SQL 문법의 일부가 아닌 클라이언트 디렉티브입니다.
달러 인용 문자열 (PostgreSQL)PostgreSQL은 문자열 리터럴에 $tag$ ... $tag$를 지원하므로 안의 작은따옴표를 이스케이프할 필요가 없습니다. 함수 본문에서 특히 유용합니다. $$ SELECT 'hello' $$는 유효하고 CREATE FUNCTION 블록에서 흔합니다. 다른 방언은 이 구문을 파싱하지 않습니다.
파라미터 바인딩 플레이스홀더표준이 없습니다. PostgreSQL과 그것을 겨냥한 ORM은 $1, $2를 사용합니다. JDBC, MySQL, ODBC는 ?를 사용합니다. 이름 있는 플레이스홀더 :name은 Oracle과 많은 ORM 문자열 템플릿에서 흔합니다. 버전 간 ORM 생성 SQL은 종종 플레이스홀더 스타일만 다를 때가 있습니다. diff 전에 양쪽을 포맷하세요.
인코딩UTF-8은 2026년 SQL 파일의 보편적 기본값입니다. Windows에서 유래한 오래된 스크립트는 여전히 BOM이 붙은 UTF-16 LE로 저장돼 있을 수 있고, 텍스트 diff의 1행에 유령 문자로 나타납니다. 두 파일이 같아 보이는데 diff가 시작 부분의 한 글자 변경을 표시하면 BOM을 의심하고 명시적 UTF-8로 다시 저장하세요.

SQL diff: 자주 묻는 질문

두 쿼리에 EXPLAIN ANALYZE를 실행하는 것과 어떻게 다른가요?

스택의 다른 계층입니다. EXPLAIN ANALYZE는 쿼리의 플랜, 즉 옵티마이저가 가지고 있는 통계를 바탕으로 무엇을 하기로 결정했는지 보여줍니다. 이 도구는 쿼리의 텍스트 diff를 떠줍니다. 그것이 저장소에서 바뀐 것입니다. 보통은 둘 다 필요합니다: 이 diff로 리팩터링을 발견하고(JOIN 종류가 바뀌었다, 술어가 옮겨졌다), 그다음 psql이나 클라이언트의 EXPLAIN ANALYZE로 옵티마이저가 새 형태에 대해 실제로 합리적인 플랜을 골랐는지 확인합니다. 둘은 보완 관계이지 대체 관계가 아닙니다.

diff가 방언을 인식하나요 (PostgreSQL 대 MySQL 대 SQL Server)?

아니요. diff는 입력을 텍스트로 다루므로 PostgreSQL, MySQL, SQL Server, SQLite, Oracle을 다르게 파싱하지 않고 방언 비호환 구문에도 플래그를 세우지 않습니다. 의도적입니다: 방언 사이에서 diff를 뜰 수 있다는 뜻이고, 이는 쿼리를 포팅할 때 정확히 원하는 동작입니다. 방언 인식 린팅이 필요하다면 SQL을 sqlfluff나 데이터베이스의 파서를 통해 별도로 처리하세요. 리뷰 사용 사례("이 쿼리에서 무엇이 바뀌었는가")에서는 텍스트 수준이 옳은 입자도입니다.

도구가 SQL을 포맷해 주나요?

네, 각 패널의 Format 버튼이 SQL을 일관된 들여쓰기, 키워드 대문자, 한 줄에 한 문장으로 재배치합니다. 기본 포맷터이지 sqlfmt나 sqlfluff의 대체물은 아닙니다: 암묵 JOIN을 명시 JOIN으로 다시 쓰지 않고, 특정 방언의 스타일 가이드를 강제하지도 않습니다. 핵심은 diff 전에 포맷 노이즈를 없애는 것입니다. 한쪽이 DataGrip으로 포맷되고 다른 쪽이 손으로 입력됐어도 깔끔히 비교되도록 말입니다.

키워드 대소문자(SELECT 대 select)를 정규화하나요?

Format 버튼이 키워드를 대문자로 바꿉니다. 이는 대부분의 스타일 가이드와 ISO SQL 표준의 관례입니다. Format을 누르지 않으면 혼합 대소문자가 diff로 나타납니다. 기반 엔진이 문자 기반이기 때문입니다. SQL 키워드는 모든 데이터베이스에서 대소문자 구분이 없으므로 selectSELECT는 동일하게 실행됩니다. 식별자 대소문자는 다른 문제이고 데이터베이스마다 다릅니다. Postgres, MySQL, SQL Server가 어떻게 다른지는 아래 빠른 참조를 보세요.

ORM이 만드는 t1, t2, generatedAlias0 같은 별칭은 어떻게 다루나요?

ORM 출력은 본디 노이즈가 많습니다: Hibernate는 generatedAlias0, generatedAlias1을 만들고, SQLAlchemy는 anon_1, anon_2를 사용합니다. 두 ORM 버전이 같은 별칭을 다른 순서로 부여하면, 텍스트 diff는 쿼리가 구조적으로 동일하더라도 모든 별칭 교체를 변경으로 강조합니다. 실용적인 해결은 양쪽을 포맷하고, 별칭만의 차이는 무시하고(보통 같은 줄에서 짝지어진 빨강·녹색 블록으로 분명합니다), 실제 구조 편집(새 JOIN, 다른 WHERE 절, 제거된 컬럼)에 집중하는 것입니다.

붙여넣는 SQL에 크기 제한이 있나요?

소프트 제한은 있고 하드 제한은 없습니다. diff가 브라우저에서 동작하므로 상한은 브라우저의 메모리와 사용자가 얼마나 기다릴 의향이 있는가입니다. 5,000줄짜리 pg_dump 출력은 최신 노트북에서 1초도 안 걸려 diff됩니다. 200,000줄짜리 덤프는 몇 초가 걸릴 수 있고 저사양 기기에서는 메모리 한계에 다다를 수 있습니다. 그 정도로 큰 데이터베이스 덤프라면 올바른 흐름은 먼저 스키마만 diff(pg_dump --schema-only)를 하고, 데이터 수준 비교가 필요하면 특정 테이블로 들어가는 것입니다.

개인정보 보호와 동작 방식

당신의 SQL은 절대 브라우저를 떠나지 않습니다. 포맷터와 diff 모두 당신의 기기에서 로컬로 실행됩니다. 입력에 대한 분석도, 로그도, "도움이 되는" 클라우드 왕복도 없습니다. 붙여넣는 SQL이 실제 테이블 이름, 실제 컬럼 이름, 샘플 행에 진짜 프로덕션 데이터를 담고 있을 때 그것이 중요합니다. 언어는 ISO/IEC 9075에 문서화되어 있고, 방언 참조는 PostgreSQL, MySQL, SQL Server, SQLite입니다.