
1. "SQL은 하나"라는 착각부터 깨기
SQL에는 ISO/IEC 9075라는 국제 표준이 있습니다.
그래서 SQL만 배우면 어느 DB든 통할 것 같지만, 실제 제품들은 각자 방언(dialect) 을 씁니다.
"상위 10건만 조회"라는 똑같은 요구사항이 제품마다 이렇게 갈립니다.
-- MySQL / MariaDB / PostgreSQL / SQLite
SELECT * FROM users LIMIT 10;
-- Oracle (12c 이상)
SELECT * FROM users FETCH FIRST 10 ROWS ONLY;
-- SQL Server
SELECT TOP 10 * FROM users;
그래서 학습을 시작하기 전에 내가 쓰는 DBMS를 확정하는 것이 첫 단계입니다. 그래야 볼 공식 문서가 정해집니다.
내 DBMS 판별하기
아래를 하나씩 실행해서, 성공하는 것 하나가 내가 쓰는 DBMS입니다.
SELECT VERSION(); -- 성공 → MySQL / MariaDB / PostgreSQL
SELECT * FROM v$version; -- 성공 → Oracle
SELECT @@VERSION; -- 성공 → SQL Server
SELECT sqlite_version(); -- 성공 → SQLite
SELECT VERSION()은 MySQL과 PostgreSQL 양쪽에서 다 동작하므로, 출력된 문자열 내용으로 구분합니다.
| DBMS | 출력형태 |
| PostgreSQL | PostgreSQL 16.x on x86_64-pc-linux-gnu, compiled by gcc... |
| MySQL | 8.0.36 (버전 숫자만) |
이 글의 나머지는 전부 PostgreSQL 기준입니다.
2. FROM 없는 SELECT : 첫 번째 방언 체험
위 판별 쿼리를 다시 보면 특이한 점이 있습니다. FROM 절이 없습니다.
SELECT 1 + 1; -- PostgreSQL: 2
SQL 표준상 SELECT는 테이블 없이도 값을 계산해 반환할 수 있고, PostgreSQL · MySQL · SQL Server · SQLite는 이를 허용합니다. 반면 Oracle은 FROM 절을 필수로 요구합니다. 그래서 DUAL이라는 1행짜리 더미 테이블이 존재합니다.
-- Oracle에서는 이렇게 써야 한다
SELECT 1 + 1 FROM DUAL;
🔬 실험 1 :계산기로 써보기
FROM 없는 SELECT는 단순한 문법 잡학이 아니라, SQL 문법을 테이블 없이 실험하는 도구입니다.
SELECT 1 + 1 AS 산술,
'Post' || 'greSQL' AS 문자열연결,
upper('hello') AS 대문자변환,
length('안녕하세요') AS 글자수,
current_date AS 오늘,
10 > 3 AS 비교결과;
실행 결과
산술 | 문자열연결 | 대문자변환 | 글자수 | 오늘 | 비교결과
------+------------+-----------+--------+------------+----------
2 | PostgreSQL | HELLO | 5 | 2026-09-01 | t
여기서 두 가지를 얻습니다.
- ||가 문자열 연결 연산자입니다. 다른 언어의 +가 아닙니다. (SQL 표준)
- 10 > 3의 결과가 t(true)라는 값입니다. 비교는 boolean을 반환하는 연산이며, 이 사실이 나중에 WHERE를 이해하는 열쇠가 됩니다.
3. PostgreSQL의 킬러 기능 : 트랜잭션 DDL
실제 데이터가 든 DB에서 학습할 때 가장 무서운 건 되돌릴 수 없는 실수입니다.
DBeaver나 psql은 기본이 autocommit 켜짐 상태라, UPDATE나 DELETE를 그냥 실행하면 그 즉시 확정됩니다.
(Ctrl+Z는 없습니다.)
그래서 안전장치가 필요한데, 여기서 PostgreSQL의 중요한 특성이 등장합니다.
PostgreSQL은 DDL(CREATE / ALTER / DROP)도 트랜잭션으로 롤백됩니다. Oracle과 MySQL은 DDL 실행 시 암묵적으로 커밋되어 되돌릴 수 없습니다.
🔬 실험 2 : 테이블을 만들고 없던 일로 만들기
BEGIN;
CREATE TABLE test_rollback (id int);
INSERT INTO test_rollback VALUES (1), (2);
SELECT * FROM test_rollback; -- ①
ROLLBACK;
SELECT * FROM test_rollback; -- ②
① 실행 결과 : 정상적으로 2행이 보입니다.
id
----
1
2
② 실행 결과 : 에러가 납니다.

ERROR: relation "test_rollback" does not exist
이 에러 메시지가 핵심입니다. "테이블은 있는데 데이터만 없다"가 아니라 "테이블 자체가 존재하지 않는다" 고 나옵니다. CREATE TABLE이라는 DDL 자체가 되돌려졌다는 증거입니다.
🔬 실험 3 : 단어 하나만 바꿔보기
같은 코드에서 ROLLBACK을 COMMIT으로만 바꿔 실행해보세요.
BEGIN;
CREATE TABLE test_rollback (id int);
INSERT INTO test_rollback VALUES (1), (2);
COMMIT; -- ← ROLLBACK에서 COMMIT으로 변경
SELECT * FROM test_rollback; -- 이번엔 1, 2가 그대로 조회된다
DROP TABLE test_rollback; -- 실습 후 정리
같은 코드에서 마지막 한 단어만 바꿨는데 결과가 정반대입니다. 이것이 트랜잭션의 본질입니다.
💡 학습용 안전 패턴 이 시리즈의 모든 실험은 BEGIN ... ROLLBACK으로 감쌉니다. 실제 데이터가 든 DB에서도 흔적 없이 실험할 수 있습니다.
모든 것이 롤백되지는 않는다
다음 명령들은 트랜잭션 블록 안에서 실행 자체가 거부됩니다.
- CREATE DATABASE
- CREATE TABLESPACE
- CREATE INDEX CONCURRENTLY
- VACUUM
📖 근거: PostgreSQL 문서 → SQL Commands → 각 명령어 페이지의 Notes 항목
4. psql 메타명령어는 SQL이 아니다
PostgreSQL 학습 자료에서 자주 보는 이런 명령들이 있습니다.
\dt -- 테이블 목록
\d 테이블명 -- 컬럼, 타입, 인덱스, 제약조건
\dn -- 스키마 목록
\l -- 데이터베이스 목록
역슬래시로 시작하는 이것들은 SQL이 아닙니다. psql이라는 클라이언트 프로그램의 기능입니다.
그래서 DBeaver, pgAdmin, 또는 애플리케이션 코드에서는 동작하지 않습니다. 어디서든 통하는 SQL 버전은 이렇습니다.
SELECT table_schema, table_name, table_type
FROM information_schema.tables
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY table_schema, table_name;
🔬 실험 4 : WHERE 절의 존재 이유 체감하기
위 쿼리에서 WHERE 절을 지우고 실행해보세요.
조건 결과 행 수
| 조건 | 결과 행 수 |
| WHERE 있음 | 내 테이블만 (수십~수백 건) |
| WHERE 없음 | 시스템 카탈로그 포함 (수백~수천 건) |
pg_catalog와 information_schema는 PostgreSQL이 내부적으로 쓰는 시스템 스키마입니다. 이걸 걸러내지 않으면 내 테이블을 찾을 수 없습니다.
🔬 실험 5 : psql이 뒤에서 던지는 SQL 훔쳐보기
psql을 쓸 수 있는 환경이라면, 이 기능이 강력한 학습 도구가 됩니다.
\set ECHO_HIDDEN on
\dt
이렇게 하면 psql이 \dt를 처리하려고 내부적으로 실행하는 진짜 SQL이 화면에 출력됩니다.
pg_catalog.pg_class와 pg_namespace를 조인하는 쿼리가 나옵니다.
편리한 단축 명령어처럼 보였던 것이 결국 평범한 SELECT문이었다는 뜻입니다. 앞으로 "이건 어떻게 조회하지?" 싶을 때 이 기능이 정답지 역할을 합니다.
📖 근거: PostgreSQL 문서 → Reference → Client Applications → psql → Variables 항목
5. 정리
개념 요점
| 개념 | 요점 |
| SQL 방언 | 표준은 있지만 제품마다 문법이 다르다. 내 DBMS를 먼저 확정할 것 |
| FROM 없는 SELECT | PostgreSQL은 허용, Oracle은 DUAL 필요. 문법 실험 도구로 활용 가능 |
| || | 문자열 연결 연산자 (+ 아님) |
| 비교 연산 | boolean 값을 반환하는 연산. WHERE 이해의 기초 |
| 트랜잭션 DDL | PostgreSQL은 CREATE TABLE도 롤백된다. 학습 시 필수 안전장치 |
| psql 메타명령 | SQL이 아닌 클라이언트 기능. information_schema가 이식 가능한 대안 |