이번 문서의 목표: 이 파일을 다 읽으면 SQLD 시험 범위인 Oracle 문법을 기준으로, 실무에서 자주 마주치는 MySQL과의 대표적인 문법 차이를 표로 정확히 대응시킬 수 있다.
왜 필요한가: SQLD는 Oracle 기준, 그러나 현업은 그렇지 않다
01편에서 언급했듯 SQLD는 공식적으로 특정 벤더에 종속되지 않는다고 표방하지만, 실제 출제 기준과 대부분의 학습 자료는 Oracle SQL을 표준으로 삼는다. 여기까지 배운 ROWNUM(22편), DECODE(23편), 계층형 질의(22편) 등이 모두 Oracle 고유 기능이다. 그런데 막상 실무에 나가면 MySQL, PostgreSQL 등 다른 DBMS(Database Management System, 데이터베이스 관리 시스템)를 쓰는 회사도 많다. Oracle에서 배운 지식을 그대로 다른 DBMS에 옮기면 문법 오류가 나거나 아예 존재하지 않는 기능을 찾아 헤매게 된다. 이 편은 공식 출제범위에는 없지만, 학습방향에서 밝혔듯 실무 전환 시 바로 부딪히는 문제이므로 보너스로 다룬다.
쉽게 말하면: Oracle과 MySQL은 같은 SQL이라는 언어를 쓰지만 사투리가 다른 두 지역이라, 같은 뜻을 표현하는 단어(문법)가 서로 다르다.
행 제한: ROWNUM vs LIMIT
22편에서 다룬 Oracle의 ROWNUM은 “결과 행이 만들어지는 도중에 순차적으로 부여되는 번호”였고, 이 때문에 정렬 후 상위 N개를 뽑으려면 인라인뷰로 감싸야 했다. MySQL은 이 과정을 훨씬 단순한 LIMIT 절 하나로 해결한다.
-- Oracle: 인라인뷰로 감싸야 정확한 Top 3
SELECT * FROM (SELECT * FROM 사원 ORDER BY 급여 DESC) WHERE ROWNUM <= 3;
-- MySQL: ORDER BY 뒤에 LIMIT만 붙이면 끝
SELECT * FROM 사원 ORDER BY 급여 DESC LIMIT 3;MySQL의 LIMIT은 ORDER BY 다음 단계로 정의되어 있어 정렬이 끝난 뒤 상위 N개를 그대로 잘라내므로, ROWNUM처럼 인라인뷰로 감쌀 필요가 없다. LIMIT 3 OFFSET 2처럼 쓰면 앞의 2개를 건너뛰고 그다음 3개를 가져오는 페이징도 가능하며, 이는 Oracle 12c 이상의 OFFSET 2 ROWS FETCH FIRST 3 ROWS ONLY(22편 참고)와 정확히 같은 역할을 한다.
날짜 변환: TO_DATE vs STR_TO_DATE
14편에서 다룬 Oracle의 TO_DATE는 문자열을 지정한 형식에 맞춰 날짜(DATE) 타입으로 변환하는 함수였다.
-- Oracle
SELECT * FROM 결제내역 WHERE 결제일시 >= TO_DATE('20251201', 'YYYYMMDD');MySQL에는 TO_DATE가 없고, 같은 역할을 STR_TO_DATE 함수가 담당한다. 형식 지정 기호도 Oracle의 YYYY, MM, DD와 달리 %Y, %m, %d처럼 퍼센트 기호를 쓴다.
-- MySQL
SELECT * FROM 결제내역 WHERE 결제일시 >= STR_TO_DATE('20251201', '%Y%m%d');반대로 날짜를 문자열로 바꾸는 함수도 이름이 다르다. Oracle의 TO_CHAR(날짜, 'YYYY-MM-DD')에 대응하는 MySQL 함수는 DATE_FORMAT(날짜, '%Y-%m-%d')다. 형식 코드 체계 자체가 다르므로, Oracle 형식 문자열을 MySQL에 그대로 옮기면 오류가 난다는 점에 특히 주의해야 한다.
NULL 처리 함수: NVL vs IFNULL
09편에서 다룬 대로 NULL은 “값이 없음”을 뜻하며 일반 연산에 그대로 섞이면 결과도 NULL이 되어버린다. 그래서 NULL을 다른 기본값으로 바꿔주는 함수가 필요한데, 이 함수의 이름이 DBMS마다 다르다.
-- Oracle: NVL(대상, 대체값)
SELECT NVL(보너스, 0) FROM 사원;
-- MySQL: IFNULL(대상, 대체값)
SELECT IFNULL(보너스, 0) FROM 사원;두 함수 모두 “첫 번째 인자가 NULL이면 두 번째 인자로 대체하라”는 동일한 의미이며 인자 순서와 개수도 같다. 다만 이름만 다르므로 헷갈리기 쉽다. 참고로 Oracle에는 여러 인자를 순서대로 검사해 NULL이 아닌 첫 값을 반환하는 COALESCE도 있는데, 이 함수는 표준 SQL 함수라 MySQL에서도 이름 그대로 동일하게 쓸 수 있다.
문자열 연결: || vs CONCAT
여러 문자열을 하나로 이어붙이는 문법도 다르다. Oracle은 표준 SQL이 정의한 연결 연산자 ||(파이프 두 개)를 쓴다.
-- Oracle
SELECT 성 || 이름 AS 전체이름 FROM 사원;MySQL은 ||를 기본적으로 논리 OR 연산자로 해석하므로(SQL 모드 설정에 따라 다를 수 있으나 기본값 기준), 문자열 연결에는 반드시 CONCAT 함수를 써야 한다.
-- MySQL
SELECT CONCAT(성, 이름) AS 전체이름 FROM 사원;CONCAT 함수 자체는 Oracle에도 존재하지만, Oracle의 CONCAT은 인자를 정확히 2개만 받는 반면 MySQL의 CONCAT은 인자를 몇 개든 받을 수 있다는 차이도 있다.
더미 테이블: DUAL
DUAL은 실제 데이터를 저장하는 목적이 아니라, “테이블 없이 값 하나만 계산하고 싶을 때” 형식상 필요한 FROM 절을 채워주는 더미(dummy, 가짜) 테이블이다. SELECT 1+1;처럼 FROM 없이 계산만 하고 싶어도 SQL 문법상 FROM이 필요한 경우가 많은데, 이때 SELECT 1+1 FROM DUAL;처럼 쓴다. DUAL은 컬럼 하나(DUMMY)와 행 하나('X'라는 값)만 가진 시스템 테이블이며, SELECT COUNT(*) FROM DUAL;을 실행하면 항상 1이 나온다.
Oracle에서는 FROM 절이 문법상 필수이므로 DUAL을 자주 쓴다. MySQL은 FROM 절 자체를 생략할 수 있어 SELECT 1+1;처럼 바로 실행이 가능하다(다만 MySQL도 호환을 위해 DUAL을 지원하므로 SELECT 1+1 FROM DUAL;도 그대로 실행된다).
자동 증가: SEQUENCE vs AUTO_INCREMENT
04편에서 다룬 기본키는 대개 중복 없이 순서대로 증가하는 숫자를 쓴다. 이 값을 자동으로 채워주는 방식도 DBMS마다 다르다.
Oracle은 SEQUENCE라는 별도의 객체를 테이블과 독립적으로 만들어 관리한다.
CREATE SEQUENCE 사원_seq START WITH 1 INCREMENT BY 1;
INSERT INTO 사원 (사번, 이름) VALUES (사원_seq.NEXTVAL, '김민준');사원_seq.NEXTVAL을 호출할 때마다 시퀀스 값이 1씩 증가한 새 번호를 내어준다. SEQUENCE는 특정 테이블에 종속되지 않는 독립 객체이므로, 여러 테이블이 하나의 시퀀스를 공유할 수도 있고, 테이블과 무관하게 번호만 미리 발급받아 둘 수도 있다.
MySQL은 컬럼 자체에 AUTO_INCREMENT 속성을 지정하는 훨씬 간단한 방식을 쓴다.
CREATE TABLE 사원 (
사번 INT AUTO_INCREMENT PRIMARY KEY,
이름 VARCHAR(50)
);
INSERT INTO 사원 (이름) VALUES ('김민준');AUTO_INCREMENT가 붙은 컬럼은 INSERT할 때 값을 생략하기만 하면 자동으로 이전 최댓값 더하기 1이 채워진다. 다만 AUTO_INCREMENT는 SEQUENCE와 달리 그 컬럼이 속한 테이블에 종속되며, 별도의 독립 객체로 존재하지 않는다는 차이가 있다.
한눈에 보는 Oracle vs MySQL 문법 대조표
| 구분 | Oracle | MySQL | 비고 |
|---|---|---|---|
| 상위 N행 제한 | ROWNUM + 인라인뷰, 또는 FETCH FIRST n ROWS ONLY | LIMIT n (LIMIT offset, n으로 페이징) | ROWNUM은 정렬 전 값에 매겨지므로 인라인뷰 필수(22편) |
| 문자열 → 날짜 변환 | TO_DATE(문자열, 'YYYYMMDD') | STR_TO_DATE(문자열, '%Y%m%d') | 형식 기호 체계(YYYY vs %Y)가 서로 다름 |
| 날짜 → 문자열 변환 | TO_CHAR(날짜, 'YYYY-MM-DD') | DATE_FORMAT(날짜, '%Y-%m-%d') | 함수 이름과 형식 기호 모두 다름 |
| NULL 대체 | NVL(값, 대체값) | IFNULL(값, 대체값) | COALESCE는 표준 SQL이라 양쪽 모두 동일하게 지원 |
| 문자열 연결 | 문자열1 || 문자열2 | CONCAT(문자열1, 문자열2) | MySQL은 기본 설정에서 ||를 논리 OR로 해석 |
| 더미 테이블 | DUAL (FROM 절 필수라서 사용) | FROM 생략 가능(DUAL도 호환 지원) | Oracle은 FROM 없는 SELECT를 허용하지 않음 |
| 자동 증가 값 | SEQUENCE 객체 + NEXTVAL | 컬럼의 AUTO_INCREMENT 속성 | SEQUENCE는 테이블과 독립된 객체, AUTO_INCREMENT는 컬럼에 종속 |
자주 틀리는 점: 시험 자체는 Oracle 기준으로 출제되므로
LIMIT이나IFNULL,STR_TO_DATE,CONCAT,AUTO_INCREMENT를 Oracle 문법 문제의 보기로 섞어 놓고 “이 중 Oracle에서 사용할 수 없는 것은?”처럼 묻는 함정이 나올 수 있다. 표의 왼쪽(Oracle) 칼럼이 시험에서 실제로 쓰이는 문법이고, 오른쪽(MySQL) 칼럼은 실무 참고용이라는 우선순위를 명확히 기억해야 한다.
직접 해보기 1: Oracle 전용 문법이 실제로 동작하는지 확인
00편에서 만든 환경에서 진행합니다. 아직 안 만들었다면 00편을 먼저 보세요.
이 편의 대조표에 나온 Oracle 전용 문법 네 가지를 DUAL로 하나씩 직접 실행해 본다.
SELECT SYSDATE FROM DUAL;
SELECT NVL(NULL, '기본값') FROM DUAL;
SELECT DECODE(1, 1, '일치', '불일치') FROM DUAL;
SELECT ROWNUM FROM DUAL;무엇을 보아야 하나: 네 문장 모두 오류 없이 값 하나씩을 돌려주는지 확인한다. DUAL은 행이 딱 하나뿐인 테이블이라 ROWNUM도 항상 1만 나온다는 점도 함께 눈여겨본다.
왜 이걸 해보나: 위 표에서 본 것처럼
SYSDATE·NVL·DECODE·ROWNUM·DUAL은 MySQL과 SQLite에는 아예 존재하지 않는다. 다른 DBMS로 연습했다면 이 네 문장 중 어느 것도 실행해 볼 수 없었을 것이다. Oracle 환경에서만 이 실습이 가능하다는 사실이 곧 “다른 DBMS로 연습하면 안 되는” 이유다.
직접 해보기 2: 표준 문법과 Oracle 전용 문법 비교
같은 목적(상위 2명 조회)을 표준 문법과 Oracle 전용 문법으로 각각 실행해 결과를 맞춰 본다.
-- 표준 SQL: FETCH FIRST
SELECT * FROM 사원 ORDER BY 급여 DESC FETCH FIRST 2 ROWS ONLY;
-- Oracle 전용: ROWNUM (정렬 후 값에 적용하려면 인라인뷰로 감싸야 한다)
SELECT * FROM (SELECT * FROM 사원 ORDER BY 급여 DESC) WHERE ROWNUM <= 2;무엇을 보아야 하나: 두 쿼리가 같은 2행을 돌려주는지 확인한다. 그리고 ROWNUM 쪽만 인라인뷰(서브쿼리)로 한 번 감싸야 했다는 차이를 실제 코드 모양으로 비교해 본다.
왜 이걸 해보나:
WHERE ROWNUM <= 2를 인라인뷰 없이ORDER BY와 나란히 쓰면 정렬 전 순서에서 잘려나가 원하는 결과가 안 나온다는 것이 22·25편의 단골 함정이다. 두 방식을 나란히 실행해 보면 왜ROWNUM에는 인라인뷰가 필요하고FETCH FIRST에는 필요 없는지가 훨씬 분명해진다.
핵심 정리
- 상위 N개 추출은 Oracle이 ROWNUM(인라인뷰 필요) 또는 FETCH FIRST를, MySQL이 더 단순한 LIMIT을 쓴다.
- 날짜 변환은 Oracle이 TO_DATE·TO_CHAR를, MySQL이 STR_TO_DATE·DATE_FORMAT을 쓰며 형식 기호 체계(
YYYYvs%Y)도 다르다. - NULL 대체는 Oracle이 NVL을, MySQL이 IFNULL을 쓰지만 표준 함수인 COALESCE는 양쪽에서 공통으로 쓸 수 있다.
- 문자열 연결은 Oracle이
||를, MySQL이 CONCAT 함수를 쓴다(MySQL의||는 기본적으로 논리 OR). - DUAL은 FROM 절이 문법상 필수인 Oracle에서 값 계산용으로 자주 쓰는 더미 테이블이고, MySQL은 FROM을 생략할 수 있다.
- 자동 증가 값은 Oracle이 테이블과 독립된 SEQUENCE 객체를, MySQL이 컬럼에 종속된 AUTO_INCREMENT 속성을 쓴다.