공공부하자개발 · 영어 학습 노트
SQL
DB별 비교 요약Oracle·Tibero·MySQL·MSSQL 대응표0/3 완료
  • 01함수·표현식 대응표
  • 02쿼리 문법 대응표
  • 03DDL·운영 대응표
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › DB별 비교 요약 › 02 / 3

쿼리 문법 대응표

페이징, 조인, 집합, 분석 함수, 계층, 피벗, 소계, MERGE
섹션 9진행 0 / 3

쿼리 문법 대응표: 페이징, 조인, 집합, 분석 함수, 계층, 피벗, 소계, MERGE

같은 일을 하는 쿼리 문법이 Oracle·Tibero, MySQL, MSSQL 에서 어떻게 다른지 한 페이지에 모았습니다. Ctrl+F 로 기능 이름(예: OFFSET, MINUS, PIVOT, MERGE)을 검색하면 됩니다. 원리는 각 절 끝에 적은 레슨에서 다루고, 여기서는 바로 찾아 쓰는 표와 짧은 코드만 담습니다. Tibero 는 Oracle 호환이라 Oracle 과 묶고, 다른 점만 따로 적습니다.

01한눈에 보기

자주 헷갈리는 10가지입니다.

기능 Oracle·Tibero MySQL MSSQL
상위 N건 인라인 뷰 + ROWNUM LIMIT n TOP (n)
OFFSET ... FETCH 12c 부터 없음 (LIMIT만) 2012 부터
FULL OUTER JOIN 지원 어느 버전에도 없음 지원
USING 절 지원 지원 미지원
인라인 뷰 별칭 AS 쓰면 오류 별칭 필수 별칭 필수
차집합 MINUS (21c 부터 EXCEPT도) EXCEPT (8.0.31) EXCEPT
재귀 CTE 키워드 RECURSIVE 없이 WITH RECURSIVE 필수 RECURSIVE 없이
계층 조회 CONNECT BY 재귀 CTE 재귀 CTE
소계·총계 ROLLUP·CUBE·GROUPING SETS WITH ROLLUP만 2008 부터 표준형
없으면 INSERT, 있으면 UPDATE MERGE ON DUPLICATE KEY UPDATE MERGE (끝 ; 필수)

핵심은 세 가지입니다.

  • MySQL 에는 OFFSET ... FETCH 도 FULL OUTER JOIN 도 없습니다.
  • MSSQL 은 USING 이 없고, 재귀 CTE 에 RECURSIVE 를 쓰지 않습니다.
  • Oracle 은 인라인 뷰 별칭 앞에 AS 를 쓰면 오류입니다.
02상위 N·페이징

정렬 후 앞의 N건, 또는 M번째부터 N건을 가져오는 문법입니다.

기능 Oracle·Tibero MySQL MSSQL
앞 N건 ROWNUM <= n (정렬 후 바깥에서) LIMIT n SELECT TOP (n)
건너뛰고 N건 OFFSET m ROWS FETCH NEXT n ROWS ONLY (12c) LIMIT n OFFSET m OFFSET m ROWS FETCH NEXT n ROWS ONLY (2012)
OFFSET 조건 ORDER BY 권장(없으면 순서 비보장) 해당 없음 ORDER BY 필수
순번으로 자르기 ROW_NUMBER() 인라인 뷰 ROW_NUMBER() (8.0) ROW_NUMBER() (2005)
Tibero OFFSET FETCH 는 버전 확인 해당 없음 해당 없음

ROWNUM 은 WHERE 를 거치는 중에 번호가 붙습니다. 그래서 ROWNUM > n 은 항상 0건이고, 정렬도 바깥에서 해야 합니다. 아래 코드는 정렬한 뒤 앞 3건입니다.

sql
-- Oracle · Tibero
SELECT
       t.*
  FROM (
        SELECT
               e.name
             , e.sal
          FROM emp e
         ORDER BY e.sal DESC
       ) t
 WHERE ROWNUM <= 3;

-- MySQL
SELECT e.name, e.sal FROM emp e ORDER BY e.sal DESC LIMIT 3;

-- MSSQL
SELECT TOP (3) e.name, e.sal FROM emp e ORDER BY e.sal DESC;

키셋 페이징과 행 값 비교

OFFSET 은 뒤 페이지로 갈수록 느립니다. 앞 페이지의 마지막 값을 기억해 그다음부터 읽는 키셋 방식은 (정렬 컬럼, id) 인덱스를 탑니다.

기능 Oracle·Tibero MySQL MSSQL
행 값 비교 (a,b) < (x,y) 불가, OR 로 풀기 가능 불가, OR 로 풀기
sql
-- 어느 DB 나 되는 조건 (정렬 sal DESC, id ASC 의 다음 페이지)
SELECT
       e.id
     , e.name
     , e.sal
  FROM emp e
 WHERE e.sal < :last_sal
    OR (e.sal = :last_sal AND e.id > :last_id)
 ORDER BY e.sal DESC, e.id
 FETCH FIRST 10 ROWS ONLY;

마지막 줄은 Oracle 12c·MSSQL 2012 형태입니다. MySQL 은 LIMIT 10, MSSQL 은 OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY 로 바꿉니다.

주의

ROWNUM > n 은 0건입니다. MSSQL 의 OFFSET 은 ORDER BY 없이 쓸 수 없습니다.

자세한 설명은 실무 쿼리의 페이징 레슨을 봅니다.

03조인·서브쿼리
기능 Oracle·Tibero MySQL MSSQL
표준 외부 조인 LEFT/RIGHT OUTER JOIN 같음 같음
구식 외부 조인 a.x = b.x(+) 없음 *= 는 2012 에서 제거
FULL OUTER JOIN 지원 없음 지원
USING (col) 지원 지원 미지원
인라인 뷰 별칭 (...) t (AS t 는 오류) 필수 필수
IN 리터럴 목록 1000개 초과 시 ORA-01795 해당 없음 해당 없음
스칼라 서브쿼리 결과 캐싱 있음 해당 없음 해당 없음

MSSQL 의 구식 *= 는 2005(호환성 수준 90)부터 쓸 수 없고, 2012 에서 완전히 제거됐습니다. 새 코드는 항상 OUTER JOIN 으로 씁니다.

MySQL 에는 FULL OUTER JOIN 이 없어서 왼쪽·오른쪽 조인을 UNION 으로 합칩니다.

sql
-- MySQL: FULL OUTER JOIN 대체
SELECT
       d.code
     , e.name
  FROM dept d
  LEFT JOIN emp e ON e.dept = d.code
 UNION
SELECT
       d.code
     , e.name
  FROM dept d
  RIGHT JOIN emp e ON e.dept = d.code;

IN 목록이 1000개를 넘는 Oracle 쿼리는 목록을 나누거나 임시 테이블·서브쿼리로 바꿉니다.

주의

MySQL 5.5 이하는 IN (서브쿼리) 를 상관 서브쿼리로 바꿔 바깥 행마다 다시 실행했습니다(DEPENDENT SUBQUERY). 5.6 부터 세미 조인입니다.

자세한 설명은 중급 01 서브쿼리를 봅니다.

04집합 연산
기능 Oracle·Tibero MySQL MSSQL
합집합 UNION / UNION ALL 같음 같음
교집합 INTERSECT 8.0.31 부터 지원
차집합 MINUS 8.0.31 부터 EXCEPT EXCEPT
EXCEPT 키워드 Oracle 21c 부터 8.0.31 지원
INTERSECT ALL·EXCEPT ALL Oracle 21c 부터 (MINUS ALL도) 해당 없음 해당 없음
Tibero ALL 옵션 없음 해당 없음 해당 없음

UNION ALL 은 중복을 제거하지 않아 빠르고, UNION 은 중복을 제거합니다.

sql
-- Oracle · Tibero
SELECT dept FROM emp
MINUS
SELECT code FROM dept;

-- MySQL 8.0.31+ · MSSQL
SELECT dept FROM emp
EXCEPT
SELECT code FROM dept;
주의

MySQL 8.0.31 에서 추가된 것은 INTERSECT 와 EXCEPT 입니다. FULL OUTER JOIN 이 아닙니다.

자세한 설명은 중급 01 서브쿼리를 봅니다.

05WITH·재귀 CTE
기능 Oracle·Tibero MySQL MSSQL
WITH 절 9i 8.0 2005
재귀 CTE 11gR2 8.0 2005
재귀 키워드 WITH (RECURSIVE 없음) WITH RECURSIVE 필수 WITH (RECURSIVE 없음)
컬럼 목록 WITH c (a, b) 필수 선택 선택
재귀 한도 해당 없음 cte_max_recursion_depth 1000 MAXRECURSION 100 (0 은 무제한)
순환·정렬 SEARCH·CYCLE 절 직접 조건 직접 조건
앞 문장과 구분 불필요 불필요 앞 문장 끝에 ; 필요

앵커 문자열 길이가 재귀부까지 고정되므로, 경로를 이어 붙일 때는 앵커에서 CAST 로 길이를 늘려 둡니다.

sql
-- MySQL
WITH RECURSIVE t (id, name, lvl) AS (
    SELECT id, name, 1 FROM emp WHERE mgr IS NULL
    UNION ALL
    SELECT e.id, e.name, t.lvl + 1
      FROM emp e
      JOIN t ON e.mgr = t.id
)
SELECT * FROM t;

Oracle·MSSQL 은 WITH RECURSIVE 대신 WITH t (id, name, lvl) AS (...) 로 씁니다.

DML 과 조합

기능 Oracle·Tibero MySQL MSSQL
INSERT INSERT ... WITH ... SELECT INSERT ... WITH ... SELECT WITH c ... INSERT
UPDATE·DELETE 서브쿼리 안에서 WITH WITH ... UPDATE/DELETE WITH c ... UPDATE c
참고

H2 는 WITH 로 시작하는 DML 을 지원하지 않아 실행 근거로 쓸 수 없습니다.

자세한 설명은 고급 계층·재귀 쿼리와 WITH 절 레슨을 봅니다.

06분석 함수
기능 Oracle·Tibero MySQL MSSQL
순위 함수·OVER(PARTITION BY) 8i 8.0 2005
집계 OVER(ORDER BY)·프레임 8i 8.0 2012
LAG·LEAD 8i 8.0 2012
FIRST_VALUE·LAST_VALUE 8i 8.0 2012
NTH_VALUE 11gR2 8.0 없음
IGNORE NULLS 지원 (LAG·LEAD 는 11gR2) 미지원 (RESPECT NULLS만) 2022
RATIO_TO_REPORT Oracle·Tibero 전용 없음 없음
RANGE 숫자·INTERVAL 오프셋 가능 8.0 가능 불가 (UNBOUNDED·CURRENT ROW만)

MySQL 5.7 이하는 사용자 변수로 흉내 내며 평가 순서가 보장되지 않습니다.

위치와 프레임

  • 분석 함수는 SELECT·ORDER BY 에만 씁니다. WHERE 에 쓰려면 인라인 뷰나 CTE 로 감쌉니다.
  • ORDER BY 만 있고 프레임을 생략하면 기본은 RANGE UNBOUNDED PRECEDING ~ CURRENT ROW 입니다.
  • 이 기본 프레임은 정렬 값이 같은 행을 함께 더합니다. 행 단위 누적은 ROWS 를 씁니다.
  • LAG·LEAD 는 프레임과 무관하지만, FIRST_VALUE·LAST_VALUE 는 프레임의 영향을 받습니다.
  • LAST_VALUE 의 기본 프레임은 현재 행까지라서, 마지막 값이 아니라 현재 행 값이 나옵니다.
sql
-- 세 DB 공통 (MySQL 8.0 · MSSQL 2012 이상)
SELECT
       e.name
     , e.sal
     , SUM(e.sal) OVER (ORDER BY e.hired, e.id
                        ROWS BETWEEN UNBOUNDED PRECEDING
                                 AND CURRENT ROW) AS running
  FROM emp e;
주의

RATIO_TO_REPORT 는 Oracle·Tibero 에만 있습니다. 다른 DB 는 sal / SUM(sal) OVER () 로 씁니다. MSSQL 은 정수 나눗셈이 잘리니 * 100.0 을 곱합니다.

자세한 설명은 고급 01 순위 함수와 고급 02 를 봅니다.

07계층·피벗·소계

계층 조회

기능 Oracle·Tibero MySQL MSSQL
계층 전개 START WITH ... CONNECT BY PRIOR 재귀 CTE (8.0) 재귀 CTE (2005)
루트·리프·순환 CONNECT_BY_ROOT·ISLEAF·NOCYCLE·ISCYCLE (10g) 직접 계산 직접 계산
순환 데이터 NOCYCLE 없으면 오류 직접 조건 직접 조건

CONNECT BY 는 Oracle·Tibero 전용입니다. WHERE 는 전개가 끝난 뒤 걸러 내므로, 가지를 미리 자르려면 CONNECT BY 조건에 넣습니다.

sql
-- Oracle · Tibero
SELECT
       LEVEL
     , e.name
  FROM emp e
 START WITH e.mgr IS NULL
CONNECT BY PRIOR e.id = e.mgr;

PIVOT·UNPIVOT

기능 Oracle·Tibero MySQL MSSQL
PIVOT (행→열) 11g 없음, 조건부 집계 2005
UNPIVOT (열→행) 11g 없음 2005
UNPIVOT 의 NULL 기본 제외, INCLUDE NULLS 해당 없음 늘 제외
열→행 다른 방법 해당 없음 해당 없음 CROSS APPLY (VALUES ...)
IN 목록 정적 해당 없음 정적

Tibero 의 PIVOT 은 버전 확인이 필요합니다. IN 목록은 고정된 값이라, 열이 바뀌면 동적 SQL 로 문장을 만들어야 합니다.

sql
-- 조건부 집계 (MySQL 은 이 방법뿐, 다른 DB 도 가능)
SELECT
       o.cust
     , SUM(CASE WHEN o.status = 'PAID' THEN o.amt ELSE 0 END) AS paid
     , SUM(CASE WHEN o.status = 'CANCEL' THEN o.amt ELSE 0 END) AS cancel
  FROM orders o
 GROUP BY o.cust;

MSSQL PIVOT 은 나머지 모든 컬럼으로 묶습니다. 필요한 컬럼만 고른 파생 테이블을 PIVOT 에 넘깁니다.

소계·총계

기능 Oracle·Tibero MySQL MSSQL
ROLLUP GROUP BY ROLLUP(a, b) (8i) GROUP BY a, b WITH ROLLUP 표준형 2008, WITH ROLLUP 2005 이하
CUBE 8i 없음 표준형 2008, WITH CUBE 2005 이하
GROUPING SETS 8i 없음 2008
GROUPING(col) 8i 8.0 2005 이상
GROUPING_ID 9i 없음 2008

GROUPING_ID(a,b) 는 GROUPING(a)*2 + GROUPING(b) 와 같습니다. 소계 행의 위치는 ORDER BY GROUPING() 으로 고정합니다.

sql
-- Oracle · Tibero · MSSQL 2008+
SELECT
       e.dept
     , COUNT(*) AS cnt
  FROM emp e
 GROUP BY ROLLUP(e.dept);

-- MySQL
SELECT
       e.dept
     , COUNT(*) AS cnt
  FROM emp e
 GROUP BY e.dept WITH ROLLUP;
팁

MySQL 은 CUBE·GROUPING SETS 가 없으니 GROUP BY 를 여러 번 쓰고 UNION ALL 로 합칩니다. 소계 라벨에 CAST 를 쓸 때 MySQL 은 VARCHAR 대신 CHAR 를 씁니다.

자세한 설명은 고급 계층 쿼리, 피벗, 소계 레슨을 봅니다.

08변경 문법

MERGE 와 UPSERT

기능 Oracle·Tibero MySQL MSSQL
없으면 INSERT, 있으면 UPDATE MERGE (9i) INSERT ... ON DUPLICATE KEY UPDATE MERGE (2008)
WHEN 절 10g 부터 하나만 써도 됨 해당 없음 BY SOURCE 가능
삭제 UPDATE 뒤 DELETE WHERE 해당 없음 WHEN MATCHED AND 조건 THEN DELETE
끝 ; 불필요 불필요 필수
원천 중복 키 ORA-30926 해당 없음 오류
ON 절 컬럼 UPDATE ORA-38104 해당 없음 해당 없음
통째로 교체 해당 없음 REPLACE (DELETE 후 INSERT) 해당 없음
결과 확인 해당 없음 해당 없음 OUTPUT $action

Oracle MERGE 의 DELETE 는 방금 갱신된 행에만 적용됩니다. MSSQL 은 힌트를 별칭 앞에 씁니다.

sql
-- Oracle · Tibero
MERGE INTO emp t
USING (SELECT 1 AS id, '김대표' AS name FROM dual) s
   ON (t.id = s.id)
 WHEN MATCHED THEN UPDATE SET t.name = s.name
 WHEN NOT MATCHED THEN INSERT (id, name) VALUES (s.id, s.name);

-- MySQL
INSERT INTO emp (id, name)
VALUES (1, '김대표') AS new
ON DUPLICATE KEY UPDATE name = new.name;

-- MSSQL (힌트는 별칭 앞)
MERGE INTO emp WITH (HOLDLOCK) AS t
USING (SELECT 1 AS id, N'김대표' AS name) AS s
   ON t.id = s.id
 WHEN MATCHED THEN UPDATE SET t.name = s.name
 WHEN NOT MATCHED THEN INSERT (id, name) VALUES (s.id, s.name);

MySQL 은 VALUES() 함수가 8.0.20 에서 폐기 예정이 되어, 8.0.19 부터 AS new 별칭을 씁니다. REPLACE 는 지우고 다시 넣습니다.

조인 UPDATE·DELETE

기능 Oracle·Tibero MySQL MSSQL
조인 UPDATE 상관 서브쿼리·MERGE·조인 뷰 UPDATE t JOIN s ... SET UPDATE t SET ... FROM t JOIN s
조인 DELETE WHERE EXISTS·조인 뷰, 23ai 부터 DELETE ... FROM DELETE t FROM t JOIN s DELETE t FROM t JOIN s
UPDATE ... FROM 23ai 부터 해당 없음 지원
SET (a,b) = (SELECT ...) 지원 없음 없음
원천 중복 행 ORA-01427 해당 없음 오류 없이 임의 한 행 적용

Oracle 조인 뷰는 키 보존 테이블이 아니면 ORA-01779 입니다. 상관 UPDATE 에 WHERE EXISTS 가 없으면 짝이 없는 행이 NULL 로 바뀝니다.

sql
-- Oracle · Tibero: 짝 있는 행만 갱신
UPDATE emp e
   SET e.sal = (SELECT s.sal FROM sal_new s WHERE s.id = e.id)
 WHERE EXISTS (SELECT 1 FROM sal_new s WHERE s.id = e.id);

INSERT ALL 과 여러 행 VALUES

기능 Oracle·Tibero MySQL MSSQL
INSERT ALL·INSERT FIRST 9i 없음 없음
여러 행 VALUES 23ai 부터 지원 2008 (1000행)

N건만 UPDATE·DELETE

기능 Oracle·Tibero MySQL MSSQL
건수 제한 FETCH FIRST 는 서브쿼리 안 UPDATE/DELETE ... LIMIT n UPDATE/DELETE TOP (n)
제한 사항 UPDATE 에 FETCH FIRST 직접 불가 단일 표만 순서 비보장
서브쿼리 안 제한 FETCH FIRST 가능 불가 (IN 안 LIMIT) 해당 없음
sql
-- Oracle · Tibero: 서브쿼리 안에서 제한
DELETE FROM orders
 WHERE id IN (
        SELECT
               o.id
          FROM orders o
         WHERE o.status = 'CANCEL'
         FETCH FIRST 1000 ROWS ONLY
       );

-- MySQL
DELETE FROM orders WHERE status = 'CANCEL' LIMIT 1000;

-- MSSQL
DELETE TOP (1000) FROM orders WHERE status = 'CANCEL';

변경 결과 돌려받기

기능 Oracle·Tibero MySQL MSSQL
변경한 행 값 ... RETURNING col INTO :v 해당 없음 OUTPUT inserted.col
삭제한 행 값 DELETE ... RETURNING col INTO :v 해당 없음 OUTPUT deleted.*
다른 표로 저장 해당 없음 해당 없음 OUTPUT ... INTO
채번 한 문장 UPDATE ... RETURNING INTO LAST_INSERT_ID(식) UPDATE ... OUTPUT inserted.x

자세한 설명은 실무 쿼리 데이터 이관·검증, 대용량·배치 06 대량 DML·배치 커밋, 08 대용량 삭제·아카이빙을 봅니다.

09자주 틀리는 것
틀린 생각 실제
MySQL 8.0.31 에 FULL OUTER JOIN 이 생겼다 추가된 것은 INTERSECT·EXCEPT
MySQL 도 OFFSET ... FETCH 를 쓴다 LIMIT 만. Oracle 12c·MSSQL 2012 부터
Tibero 도 ALL 옵션을 쓴다 Oracle 21c 부터만. Tibero 는 없음
MSSQL 은 *= 를 아직 쓸 수 있다 2005 부터 불가, 2012 에서 완전 제거
Oracle 인라인 뷰에 AS t 를 쓴다 오류. (...) t
MSSQL 도 USING 을 쓴다 미지원
Oracle UPDATE 에 FETCH FIRST 를 바로 쓴다 불가. 서브쿼리 안에서
Oracle FOR UPDATE 와 FETCH FIRST 를 같이 쓴다 ORA-02014
MSSQL MERGE 힌트는 별칭 뒤에 쓴다 별칭 앞 MERGE INTO t WITH (HOLDLOCK) AS a
MySQL 은 서브쿼리 안에서 FETCH FIRST 가 된다 불가
재귀 CTE 는 세 DB 가 똑같이 쓴다 MySQL 만 RECURSIVE 필수, Oracle 은 컬럼 목록 필수
INT 컬럼 AVG·나눗셈이 소수로 나온다 MSSQL 은 정수로 잘림, * 100.0
별칭을 GROUP BY 에 쓴다 MySQL 가능, MSSQL 불가, Oracle 23ai 이전 불가
자기 표 NOT IN 삭제는 모든 DB 에서 된다 MySQL 은 ERROR 1093, 파생 테이블로 감싸기
분석 함수를 WHERE 에 쓴다 SELECT·ORDER BY 만, 인라인 뷰·CTE 로 감싸기
PIVOT 열 목록이 자동으로 만들어진다 IN 목록은 정적, 동적은 동적 SQL
핵심

버전 표를 다 외우지 못했다면 "MySQL 은 LIMIT·WITH ROLLUP·ON DUPLICATE KEY" 세 가지만 기억합니다. 나머지는 Oracle 과 MSSQL 이 비슷하고 MySQL 이 다릅니다.

자세한 설명은 고급 01 순위 함수와 실무 쿼리 레슨을 봅니다.

목차
  • 한눈에 보기
  • 상위 N·페이징
  • 조인·서브쿼리
  • 집합 연산
  • WITH·재귀 CTE
  • 분석 함수
  • 계층·피벗·소계
  • 변경 문법
  • 자주 틀리는 것
참고 영상 · 인터넷 연결 시 유튜브 검색이 열립니다쿼리 문법 대응표sql 대응표 비교 요약
이전01 함수·표현식 대응표다음03 DDL·운영 대응표