SQL DDL·DML·DCL·TCL 차이: 데이터 변경과 COMMIT·ROLLBACK
1장. UPDATE가 성공했다고 데이터 변경이 끝난 것은 아니다#
직원 급여를 수정한다고 하겠습니다.
UPDATE employee
SET salary = 5600000
WHERE emp_id = 1001;DBMS가 다음과 비슷한 결과를 반환했습니다.
UPDATE 1한 행이 수정됐습니다.
그렇다면 급여 560만 원이 완전히 저장된 것일까요?
반드시 그렇지는 않습니다.
트랜잭션 안에서 실행했다면 아직 COMMIT하지 않은 상태일 수 있습니다.
UPDATE 실행
↓
현재 트랜잭션 안에서는 변경됨
↓
COMMIT
↓
변경 확정반대로 ROLLBACK을 실행하면 해당 트랜잭션의 변경을 취소할 수 있습니다.
SQL을 배울 때 UPDATE는 DML, COMMIT은 TCL이라고 외우는 것만으로는 부족한 이유가 여기에 있습니다.
실제 업무에서는 다음을 구분해야 합니다.
어떤 대상을 바꾸는 명령인가?
그리고:
그 변경은 언제 확정되는가?
SQL 명령 분류는 이 두 문제를 이해하기 위한 기준입니다.
2장. SQL 명령은 무엇을 바꾸느냐에 따라 구분할 수 있다#
SQL 명령은 일반적으로 다음과 같이 나누어 설명합니다.
| 분류 | 의미 | 대표 명령 |
|---|---|---|
| DDL | 데이터 구조 정의 | CREATE, ALTER, DROP |
| DML | 데이터 조회·변경 | SELECT, INSERT, UPDATE, DELETE, MERGE |
| DCL | 권한 제어 | GRANT, REVOKE |
| TCL | 트랜잭션 제어 | COMMIT, ROLLBACK, SAVEPOINT |
자료에 따라 SELECT를 별도의 DQL로 구분하기도 합니다.
분류 방식 자체보다 각 명령이 실제로 무엇을 다루는지 이해하는 것이 중요합니다.
DDL
→ 테이블과 제약 같은 구조
DML
→ 실제 행과 값
DCL
→ 사용자와 역할의 권한
TCL
→ 변경의 확정과 취소 범위3장. DDL은 데이터가 들어갈 구조와 규칙을 정의한다#
먼저 부서와 직원 테이블을 만들어 보겠습니다.
CREATE TABLE department (
dept_id integer PRIMARY KEY,
dept_name varchar(100) NOT NULL UNIQUE
);
CREATE TABLE employee (
emp_id integer PRIMARY KEY,
emp_name varchar(100) NOT NULL,
dept_id integer,
salary numeric(12, 2) NOT NULL DEFAULT 0,
hire_date date NOT NULL,
email varchar(200) UNIQUE,
CONSTRAINT fk_employee_department
FOREIGN KEY (dept_id)
REFERENCES department(dept_id)
ON DELETE SET NULL,
CONSTRAINT chk_employee_salary
CHECK (salary >= 0)
);이 SQL은 직원 데이터를 입력한 것이 아닙니다.
직원 데이터를 어떤 구조와 규칙으로 저장할 것인지 정의한 것입니다.
4장. 자료형과 제약조건은 서로 다른 역할을 한다#
급여 컬럼은 다음과 같습니다.
salary numeric(12, 2)이것만 보면 숫자를 저장할 수 있다는 의미입니다.
하지만 숫자라고 해서 자동으로 음수가 금지되는 것은 아닙니다.
다음 값도 숫자입니다.
-500000업무에서 음수 급여를 허용하지 않는다면 별도 제약이 필요합니다.
CHECK (salary >= 0)즉 다음 두 개념은 다릅니다.
자료형
→ 어떤 형태의 값을 저장할 수 있는가
제약조건
→ 그중 어떤 값을 허용할 것인가5장. 기본키는 행을 식별하고 중복을 막는다#
다음 정의를 보겠습니다.
emp_id integer PRIMARY KEY기본키는 직원 한 명을 식별합니다.
따라서 다음 데이터는 저장할 수 있습니다.
1001 | 가람
1002 | 나래하지만 다음은 허용되지 않습니다.
1001 | 가람
1001 | 다온같은 기본키가 두 번 존재하기 때문입니다.
기본키에는 기본적으로 다음 특성이 있습니다.
중복 불가
NULL 불가6장. 외래키는 다른 테이블과의 관계를 보호한다#
직원 테이블에는 dept_id가 있습니다.
FOREIGN KEY (dept_id)
REFERENCES department(dept_id)부서 테이블에 다음 데이터만 있다고 하겠습니다.
10 | 개발
20 | 운영그런데 직원에게 다음 부서를 입력하려고 합니다.
dept_id = 9090번 부서가 존재하지 않는다면 외래키가 입력을 막을 수 있습니다.
직원
dept_id = 90
↓
department 확인
↓
90 없음
↓
입력 거부외래키는 두 테이블 사이의 참조 무결성을 유지하는 역할을 합니다.
7장. ON DELETE SET NULL은 부모 삭제 시 자식 값을 비운다#
직원 1001과 1002가 개발 부서 10에 있다고 하겠습니다.
1001 → 부서 10
1002 → 부서 10외래키가 다음과 같이 정의되어 있습니다.
ON DELETE SET NULL부서 10을 삭제하면 직원 행 자체를 삭제하지 않고 dept_id만 NULL로 바꿀 수 있습니다.
1001 | NULL
1002 | NULL이 정책이 옳은지는 업무 규칙에 따라 다릅니다.
직원이 반드시 부서에 속해야 한다면 이런 구조는 적합하지 않을 수 있습니다.
DDL은 단순히 문법을 정의하는 것이 아니라 업무 규칙을 데이터베이스 구조로 표현하는 작업입니다.
8장. ALTER는 이미 존재하는 구조를 변경한다#
테이블을 만든 뒤 전화번호 컬럼을 추가한다고 하겠습니다.
ALTER TABLE employee
ADD COLUMN phone varchar(30);급여 상한 규칙을 추가할 수도 있습니다.
ALTER TABLE employee
ADD CONSTRAINT salary_ceiling
CHECK (salary <= 100000000);필요 없는 컬럼을 삭제할 수도 있습니다.
ALTER TABLE employee
DROP COLUMN phone;하지만 운영 환경에서 ALTER는 단순한 문법 문제가 아닙니다.
다음을 확인해야 합니다.
기존 데이터가 새 제약을 만족하는가?
변경 중 테이블 잠금이 발생하는가?
애플리케이션이 삭제할 컬럼을 사용하고 있지 않은가?
대용량 테이블에서 얼마나 오래 걸리는가?실행 가능하다는 것과 안전하게 운영에 적용할 수 있다는 것은 다른 문제입니다.
9장. DML은 실제 데이터를 읽고 변경한다#
테이블 구조를 만들었다면 실제 데이터를 넣을 수 있습니다.
INSERT INTO department (dept_id, dept_name)
VALUES
(10, '개발'),
(20, '운영');직원도 입력합니다.
INSERT INTO employee
(emp_id, emp_name, dept_id, salary, hire_date, email)
VALUES
(1001, '가람', 10, 5000000, DATE '2026-01-10', 'garam@example.com'),
(1002, '나래', 10, 4500000, DATE '2026-02-12', 'narae@example.com'),
(1003, '다온', 20, 4800000, DATE '2026-03-05', 'daon@example.com');결과는 다음과 같습니다.
| 사번 | 이름 | 부서 | 급여 |
|---|---|---|---|
| 1001 | 가람 | 10 | 5,000,000 |
| 1002 | 나래 | 10 | 4,500,000 |
| 1003 | 다온 | 20 | 4,800,000 |
10장. SELECT는 현재 데이터를 확인한다#
모든 직원을 확인합니다.
SELECT
emp_id,
emp_name,
dept_id,
salary
FROM employee;개발 부서 직원만 찾는다면:
SELECT
emp_id,
emp_name,
salary
FROM employee
WHERE dept_id = 10;SELECT는 데이터를 변경하지 않지만 DML 범주에 포함해 설명하는 자료도 있고 DQL로 따로 분류하는 자료도 있습니다.
명칭보다 중요한 것은 읽기 명령이라는 역할입니다.
11장. UPDATE에서 가장 위험한 것은 WHERE 누락이다#
개발 부서 급여를 10% 인상한다고 하겠습니다.
UPDATE employee
SET salary = salary * 1.10
WHERE dept_id = 10;결과는 다음과 같습니다.
1001
5,000,000 → 5,500,000
1002
4,500,000 → 4,950,0001003은 그대로입니다.
그런데 WHERE가 빠지면:
UPDATE employee
SET salary = salary * 1.10;모든 직원 급여가 변경됩니다.
운영 데이터 변경에서는 문법 오류보다 조건 오류가 더 위험한 경우가 많습니다.
12장. UPDATE 전에 같은 WHERE 조건으로 SELECT해 보는 습관이 중요하다#
다음 UPDATE를 실행하려고 한다고 하겠습니다.
UPDATE employee
SET salary = 5600000
WHERE emp_id = 1001;먼저 다음 SELECT를 실행합니다.
SELECT *
FROM employee
WHERE emp_id = 1001;확인할 내용은 다음과 같습니다.
원하는 직원이 맞는가?
현재 값은 무엇인가?
몇 행이 선택되는가?그다음 트랜잭션 안에서 UPDATE를 수행하면 실수를 줄일 수 있습니다.
13장. DELETE도 먼저 삭제 대상부터 확인해야 한다#
직원 1003을 삭제합니다.
DELETE FROM employee
WHERE emp_id = 1003;실행 전에:
SELECT *
FROM employee
WHERE emp_id = 1003;으로 대상을 확인하는 것이 안전합니다.
다음 SQL은 전혀 다른 의미입니다.
DELETE FROM employee;모든 행을 삭제합니다.
테이블 구조는 남지만 데이터는 사라집니다.
14장. INSERT ... SELECT는 조회 결과를 다른 테이블에 저장한다#
개발 부서 직원의 현재 목록을 스냅샷으로 보관한다고 하겠습니다.
CREATE TABLE employee_snapshot (
emp_id integer,
emp_name varchar(100),
captured_on date
);다음과 같이 조회 결과를 저장할 수 있습니다.
INSERT INTO employee_snapshot (
emp_id,
emp_name,
captured_on
)
SELECT
emp_id,
emp_name,
DATE '2026-09-01'
FROM employee
WHERE dept_id = 10;이때 저장된 이름은 수집 시점의 정보입니다.
원본 직원 이름이 바뀌었다고 스냅샷까지 무조건 바꿔야 하는 것은 아닙니다.
스냅샷의 목적은 과거 시점의 상태를 남기는 것이기 때문입니다.
15장. MERGE는 존재 여부에 따라 UPDATE와 INSERT를 나눌 수 있다#
급여 대상 테이블과 새로운 급여 데이터가 있다고 하겠습니다.
CREATE TABLE pay_target (
emp_id integer PRIMARY KEY,
amount numeric(12, 2) NOT NULL
);
CREATE TABLE pay_source (
emp_id integer PRIMARY KEY,
amount numeric(12, 2) NOT NULL
);기존 데이터:
INSERT INTO pay_target
VALUES (1001, 5000000);새 데이터:
INSERT INTO pay_source
VALUES
(1001, 5500000),
(1004, 4000000);MERGE를 실행합니다.
MERGE INTO pay_target AS t
USING pay_source AS s
ON t.emp_id = s.emp_id
WHEN MATCHED THEN
UPDATE SET amount = s.amount
WHEN NOT MATCHED THEN
INSERT (emp_id, amount)
VALUES (s.emp_id, s.amount);결과는 다음과 같습니다.
| 사번 | 변경 전 | 원본 | 변경 후 |
|---|---|---|---|
| 1001 | 5,000,000 | 5,500,000 | 5,500,000 |
| 1004 | 없음 | 4,000,000 | 4,000,000 |
16장. MERGE도 원본 중복 문제를 생각해야 한다#
다음 원본 데이터가 있다고 하겠습니다.
1001 | 5,500,000
1001 | 5,700,000하나의 목표 행에 두 원본 행이 동시에 매칭됩니다.
어느 금액으로 수정해야 할까요?
명확하지 않습니다.
DBMS에 따라 오류가 발생할 수도 있습니다.
그래서 앞의 pay_source에서는 emp_id를 기본키로 두었습니다.
emp_id integer PRIMARY KEYMERGE 역시 입력 데이터의 유일성과 업무 규칙이 먼저 명확해야 합니다.
17장. DELETE·TRUNCATE·DROP은 무엇이 사라지는지가 다르다#
세 명령 모두 데이터를 없애는 데 사용할 수 있지만 의미가 다릅니다.
| 명령 | 데이터 | 테이블 구조 |
|---|---|---|
| DELETE | 일부 또는 전체 행 삭제 | 유지 |
| TRUNCATE | 전체 행 제거 | 유지 |
| DROP | 데이터 제거 | 테이블도 제거 |
DELETE#
DELETE FROM employee
WHERE dept_id = 20;조건을 사용할 수 있습니다.
TRUNCATE#
TRUNCATE TABLE employee;전체 행을 비웁니다.
DROP#
DROP TABLE employee;테이블 자체를 제거합니다.
18장. TRUNCATE가 항상 롤백 불가능한 것은 아니다#
SQL 학습 자료에서 흔히 다음과 같은 설명을 볼 수 있습니다.
TRUNCATE
→ 자동 COMMIT
→ ROLLBACK 불가이 설명을 모든 DBMS에 적용해서는 안 됩니다.
PostgreSQL에서는 명시적인 트랜잭션 안에서 TRUNCATE를 롤백할 수 있습니다.
실습용 테이블로 확인해 보겠습니다.
CREATE TABLE scratch_delete (
id integer PRIMARY KEY
);
INSERT INTO scratch_delete
VALUES (1), (2);트랜잭션을 시작합니다.
BEGIN;테이블을 비웁니다.
TRUNCATE scratch_delete;현재 트랜잭션 안에서는:
SELECT count(*)
FROM scratch_delete;결과가 0입니다.
하지만:
ROLLBACK;을 실행하고 다시 조회하면 기존 두 행이 복원됩니다.
219장. DROP도 PostgreSQL에서는 트랜잭션 안에서 롤백할 수 있다#
같은 실습 테이블을 사용해 보겠습니다.
BEGIN;
DROP TABLE scratch_delete;
ROLLBACK;그 뒤:
SELECT *
FROM scratch_delete;를 실행하면 테이블이 다시 존재할 수 있습니다.
즉:
DDL = 무조건 자동 커밋이라는 설명을 PostgreSQL에 일반적으로 적용하면 틀릴 수 있습니다.
20장. Oracle에서는 DDL의 트랜잭션 동작이 다르다#
Oracle에서는 DDL 문장과 관련해 암시적 COMMIT 동작을 고려해야 합니다.
따라서 다음 명령들의 트랜잭션 동작을 PostgreSQL과 동일하다고 생각하면 안 됩니다.
CREATE
ALTER
DROP
TRUNCATEDDL과 TRUNCATE의 롤백 가능 여부는 사용 중인 DBMS를 기준으로 확인해야 합니다.
SQL 문법은 비슷해 보여도 트랜잭션 의미는 제품마다 다를 수 있습니다.
21장. DCL은 누가 어떤 데이터를 사용할 수 있는지를 정한다#
직원 데이터는 민감할 수 있습니다.
보고서 사용자에게 읽기 권한만 주고 급여 수정은 막고 싶다고 하겠습니다.
역할을 만듭니다.
CREATE ROLE hr_readonly;스키마 사용 권한을 줍니다.
GRANT USAGE ON SCHEMA public
TO hr_readonly;직원과 부서 테이블 조회 권한을 줍니다.
GRANT SELECT
ON department, employee
TO hr_readonly;사용자에게 역할을 연결합니다.
GRANT hr_readonly
TO report_user;이제 이 역할은 테이블을 읽을 수 있지만 UPDATE 권한까지 자동으로 얻는 것은 아닙니다.
22장. GRANT는 권한을 추가한다#
다음 SQL은 직원 테이블 조회 권한을 부여합니다.
GRANT SELECT
ON employee
TO hr_readonly;다음은 수정 권한을 추가합니다.
GRANT UPDATE
ON employee
TO hr_manager;권한을 설계할 때는 필요 이상의 권한을 주지 않는 것이 중요합니다.
조회 사용자
→ SELECT
관리 사용자
→ 필요한 UPDATE
전체 관리자
→ 더 넓은 권한역할별 책임을 나누는 것이 좋습니다.
23장. REVOKE는 특정 권한 경로를 회수한다#
다음과 같이 조회 권한을 회수할 수 있습니다.
REVOKE SELECT
ON employee
FROM hr_readonly;하지만 report_user가 다른 역할을 통해 같은 SELECT 권한을 가지고 있다면 여전히 조회할 수 있을 수 있습니다.
즉:
REVOKE
=
사용자의 모든 권한 삭제는 아닙니다.
실제 권한은 직접 권한과 여러 역할을 함께 확인해야 합니다.
24장. 특정 컬럼만 보여주고 싶다면 VIEW를 이용할 수 있다#
직원 전체 테이블에는 급여가 있습니다.
emp_id
emp_name
dept_id
salary일반 사내 디렉터리에서는 급여를 보여주고 싶지 않다고 하겠습니다.
뷰를 만듭니다.
CREATE VIEW employee_directory AS
SELECT
emp_id,
emp_name,
dept_id
FROM employee;뷰에만 권한을 부여합니다.
GRANT SELECT
ON employee_directory
TO hr_readonly;하지만 사용자에게 원본 employee 테이블의 SELECT 권한이 이미 있다면 뷰만 추가해서 급여를 숨길 수는 없습니다.
실제 권한 경로 전체를 점검해야 합니다.
25장. TCL은 여러 SQL을 하나의 업무 단위로 묶는다#
은행 계좌 이체를 생각해 보겠습니다.
A 계좌에서 10만 원을 빼고 B 계좌에 10만 원을 넣습니다.
A 계좌 -100,000
B 계좌 +100,000두 SQL 가운데 첫 번째만 성공하고 두 번째가 실패하면 문제가 됩니다.
A 계좌에서는 돈이 빠짐
B 계좌에는 돈이 들어오지 않음따라서 두 변경은 하나의 트랜잭션으로 묶어야 합니다.
BEGIN;
UPDATE account
SET balance = balance - 100000
WHERE account_id = 'A';
UPDATE account
SET balance = balance + 100000
WHERE account_id = 'B';
COMMIT;26장. COMMIT은 현재 트랜잭션의 변경을 확정한다#
다음 SQL을 보겠습니다.
BEGIN;
UPDATE employee
SET salary = 5600000
WHERE emp_id = 1001;
COMMIT;흐름은 다음과 같습니다.
flowchart LR
B["BEGIN"] --> U["UPDATE"]
U --> C["COMMIT"]
C --> F["변경 확정"]COMMIT 이후에는 같은 트랜잭션에 대한 일반적인 ROLLBACK으로 변경을 되돌리는 것이 아닙니다.
업무상 잘못된 변경이라면 별도의 정정 작업이 필요합니다.
27장. ROLLBACK은 아직 확정하지 않은 변경을 취소한다#
다음 SQL을 실행한다고 하겠습니다.
BEGIN;
UPDATE employee
SET salary = 5600000
WHERE emp_id = 1001;
ROLLBACK;UPDATE 자체는 실행됐지만 트랜잭션을 롤백했습니다.
최종 데이터는 변경 이전 상태로 돌아갑니다.
UPDATE 성공
≠
변경 최종 확정이 차이를 이해해야 합니다.
28장. 이미 COMMIT한 거래는 ROLLBACK으로 취소하는 것이 아니다#
주문 결제가 완료되어 COMMIT됐다고 하겠습니다.
그런데 고객이 하루 뒤 취소했습니다.
다음처럼 생각하면 안 됩니다.
어제 거래를 ROLLBACK이미 확정된 거래입니다.
업무에서는 새로운 취소 또는 환불 거래를 만들어야 할 수 있습니다.
기존 결제
+100,000
새 환불
-100,000이렇게 해야 과거에 무슨 일이 있었는지도 기록할 수 있습니다.
29장. SAVEPOINT는 트랜잭션 안에 중간 복구 지점을 만든다#
다음 작업을 생각해 보겠습니다.
직원 1001의 급여를 수정합니다.
그다음 직원 1002를 삭제합니다.
하지만 삭제는 실수였습니다.
급여 수정은 유지하고 삭제만 되돌리고 싶습니다.
이때 SAVEPOINT를 사용할 수 있습니다.
BEGIN;
UPDATE employee
SET salary = 5600000
WHERE emp_id = 1001;
SAVEPOINT before_delete;
DELETE FROM employee
WHERE emp_id = 1002;
ROLLBACK TO SAVEPOINT before_delete;
COMMIT;30장. SAVEPOINT의 결과를 단계별로 보면 이해하기 쉽다#
시작 상태:
| 사번 | 급여 | 존재 여부 |
|---|---|---|
| 1001 | 5,500,000 | 존재 |
| 1002 | 4,950,000 | 존재 |
급여 변경:
1001
5,500,000 → 5,600,000저장점 생성:
before_delete1002 삭제:
1002
→ 없음저장점으로 롤백:
1002
→ 다시 존재하지만 1001의 급여 수정은 저장점 이전 작업이므로 남아 있습니다.
마지막으로 COMMIT하면:
1001 급여 5,600,000
→ 확정
1002
→ 존재가 됩니다.
31장. SAVEPOINT는 COMMIT이 아니다#
SAVEPOINT를 만들었다고 변경이 확정되는 것은 아닙니다.
UPDATE
↓
SAVEPOINT상태는 여전히 미확정입니다.
그 뒤 전체 ROLLBACK을 실행하면 SAVEPOINT 이전 변경까지 모두 취소됩니다.
BEGIN;
UPDATE employee
SET salary = 5600000
WHERE emp_id = 1001;
SAVEPOINT s1;
DELETE FROM employee
WHERE emp_id = 1002;
ROLLBACK;결과는 다음과 같습니다.
1001 급여
→ 원래 값
1002
→ 존재SAVEPOINT는 트랜잭션 안에서 돌아갈 위치일 뿐입니다.
32장. ROLLBACK TO와 전체 ROLLBACK은 범위가 다르다#
다음 흐름을 보겠습니다.
flowchart LR
A["트랜잭션 시작"] --> B["급여 변경"]
B --> C["SAVEPOINT"]
C --> D["직원 삭제"]
D --> E["ROLLBACK TO SAVEPOINT"]
E --> F["COMMIT"]ROLLBACK TO SAVEPOINT는 저장점 이후 변경을 취소합니다.
전체 ROLLBACK은 트랜잭션 전체를 취소합니다.
| 명령 | 취소 범위 |
|---|---|
| ROLLBACK TO SAVEPOINT | 저장점 이후 |
| ROLLBACK | 현재 트랜잭션 전체 |
33장. 트랜잭션 경계는 업무 성공 조건과 맞아야 한다#
다음 업무를 생각해 보겠습니다.
직원 급여 변경
급여 변경 이력 저장급여는 수정됐는데 이력 저장이 실패했습니다.
업무상 두 작업이 항상 함께 성공해야 한다면 하나의 트랜잭션으로 묶는 것이 자연스럽습니다.
BEGIN;
UPDATE employee
SET salary = 5600000
WHERE emp_id = 1001;
INSERT INTO salary_history (
emp_id,
changed_salary,
changed_at
)
VALUES (
1001,
5600000,
CURRENT_TIMESTAMP
);
COMMIT;이력 INSERT가 실패하면 전체를 ROLLBACK할 수 있습니다.
34장. SQL 문장 하나와 업무 트랜잭션 하나는 같은 것이 아니다#
한 업무가 여러 SQL로 구성될 수 있습니다.
주문 생성이라면:
주문 INSERT
주문항목 INSERT
재고 UPDATE
결제 기록 INSERT가 모두 필요할 수 있습니다.
각 SQL이 문법적으로 성공했다고 해서 전체 업무가 성공한 것은 아닙니다.
업무 성공 조건에 따라 어느 SQL까지 하나의 트랜잭션으로 묶을지를 결정해야 합니다.
35장. 자동 커밋 설정은 반드시 확인해야 한다#
SQL 클라이언트나 개발 도구에 따라 자동 커밋이 활성화되어 있을 수 있습니다.
자동 커밋 환경에서는 한 문장이 끝날 때마다 변경이 확정될 수 있습니다.
예를 들어:
UPDATE 실행
↓
자동 COMMIT이 되어버리면 그 뒤 ROLLBACK을 입력해도 이미 확정된 UPDATE를 되돌리지 못할 수 있습니다.
실습과 운영 모두 다음을 확인하는 것이 중요합니다.
현재 자동 커밋 설정은 무엇인가?
명시적으로 BEGIN을 사용했는가?
어느 시점에 COMMIT됐는가?36장. 다른 세션에 언제 보이는지도 트랜잭션의 일부다#
세션 A에서:
BEGIN;
UPDATE employee
SET salary = 5600000
WHERE emp_id = 1001;을 실행했지만 아직 COMMIT하지 않았다고 하겠습니다.
세션 A에서는 변경된 값을 볼 수 있습니다.
하지만 다른 세션 B에서는 일반적인 격리 수준에서 아직 확정되지 않은 값을 그대로 보지 않을 수 있습니다.
세션 A
→ 미확정 변경 보유
세션 B
→ 확정된 상태 기준 조회따라서 트랜잭션 실습에서는 같은 세션의 결과와 다른 세션에서 보이는 값을 구분해야 합니다.
37장. SQL 오류가 나면 트랜잭션 상태도 확인해야 한다#
PostgreSQL에서 트랜잭션 중 SQL 오류가 발생하면 현재 트랜잭션이 실패 상태가 되어 이후 명령이 정상 진행되지 않을 수 있습니다.
이 경우 적절한 ROLLBACK이나 저장점 처리가 필요합니다.
따라서 다음처럼 단순하게 생각하면 안 됩니다.
한 SQL 오류
→ 그 SQL만 실패
→ 나머지는 계속 정상 실행DBMS와 트랜잭션 구조에 따라 오류 이후 처리 방식이 달라질 수 있습니다.
38장. UPDATE의 완료 여부는 다른 연결에서 다시 확인하는 것이 좋다#
중요한 데이터 변경이 완료됐다고 하겠습니다.
다음처럼 확인할 수 있습니다.
- UPDATE 대상 행 수 확인
- 현재 트랜잭션 안에서 값 확인
- COMMIT 수행
- 별도의 새 세션에서 값 재확인
이렇게 하면:
UPDATE 문장이 실행됐는가?뿐 아니라:
실제로 최종 상태로 확정됐는가?까지 확인할 수 있습니다.
39장. DDL·DML·DCL·TCL을 하나의 흐름으로 연결해 보기#
직원 관리 시스템을 처음 만든다고 하겠습니다.
먼저 테이블을 만듭니다.
CREATE TABLE
→ DDL직원 데이터를 입력합니다.
INSERT
→ DML급여를 수정합니다.
UPDATE
→ DML보고서 사용자에게 조회 권한을 줍니다.
GRANT
→ DCL급여 변경을 확정합니다.
COMMIT
→ TCL구조를 그림으로 표현하면 다음과 같습니다.
flowchart TD
DDL["DDL<br/>구조 정의"] --> DML["DML<br/>데이터 입력·변경"]
DML --> TCL["TCL<br/>변경 확정·취소"]
DCL["DCL<br/>접근 권한"] --> DML각 분류는 따로 떨어진 개념이 아니라 실제 데이터베이스 운영 과정에서 함께 사용됩니다.
40장. DELETE·TRUNCATE·DROP을 선택하는 기준#
행 일부만 삭제하려면:
DELETE를 사용합니다.
전체 행을 빠르게 비우고 구조는 유지하려면 제품 특성을 확인한 뒤:
TRUNCATE를 검토할 수 있습니다.
객체 자체가 더 이상 필요 없다면:
DROP을 사용합니다.
선택 기준은 단순한 속도만이 아닙니다.
조건 삭제가 필요한가?
테이블 구조를 남길 것인가?
외래키가 있는가?
트리거가 필요한가?
롤백이 필요한가?
어떤 잠금이 발생하는가?를 함께 봐야 합니다.
41장. 명령별 변경 대상을 정리하면#
| 명령 | 주로 변경하는 대상 |
|---|---|
| CREATE | 데이터베이스 객체 구조 |
| ALTER | 기존 객체 구조 |
| DROP | 객체 자체 |
| INSERT | 새로운 행 |
| UPDATE | 기존 행의 값 |
| DELETE | 기존 행 |
| MERGE | 조건에 따라 INSERT 또는 UPDATE |
| GRANT | 권한 |
| REVOKE | 권한 |
| COMMIT | 미확정 트랜잭션 상태 |
| ROLLBACK | 미확정 변경 |
| SAVEPOINT | 트랜잭션 내부 복구 위치 |
이 표에서 중요한 것은 분류명이 아니라 무엇이 달라지는가입니다.
42장. SQL 변경 작업 전 체크해야 할 것#
운영 데이터에 변경 SQL을 실행한다면 다음을 확인하는 것이 좋습니다.
- 어떤 DBMS에서 실행하는가?
- 자동 커밋은 켜져 있는가?
- UPDATE나 DELETE의 WHERE 조건은 정확한가?
- 동일 조건 SELECT 결과는 몇 행인가?
- DDL이 어떤 잠금을 발생시키는가?
- 외래키나 트리거로 다른 데이터가 바뀌는가?
- 실패했을 때 ROLLBACK 가능한가?
- 이미 COMMIT된 작업은 어떻게 정정할 것인가?
- 변경 후 어떤 SQL로 검증할 것인가?
- 권한 변경이 다른 역할을 통해 우회되지 않는가?
이 질문만 확인해도 단순한 SQL 실수를 상당수 줄일 수 있습니다.
43장. 핵심 정리#
SQL의 DDL·DML·DCL·TCL 분류는 명령어 암기표가 아닙니다.
각 명령이 데이터베이스의 어떤 상태를 바꾸는지 구분하기 위한 기준입니다.
DDL은 구조와 제약을 정의합니다.
CREATE
ALTER
DROPDML은 데이터를 조회하고 변경합니다.
SELECT
INSERT
UPDATE
DELETE
MERGEDCL은 접근 권한을 제어합니다.
GRANT
REVOKETCL은 트랜잭션의 변경 범위를 제어합니다.
COMMIT
ROLLBACK
SAVEPOINT특히 다음 차이는 반드시 구분해야 합니다.
UPDATE가 실행됨
≠
UPDATE가 최종 확정됨트랜잭션 안에서 변경한 데이터는 COMMIT 전까지 미확정 상태일 수 있습니다.
ROLLBACK TO SAVEPOINT는 저장점 이후의 변경만 되돌리고, 전체 ROLLBACK은 현재 트랜잭션의 변경을 취소합니다.
반면 이미 COMMIT된 업무는 일반적인 ROLLBACK으로 되돌리는 것이 아니라 별도의 정정이나 역거래로 처리해야 할 수 있습니다.
또한 다음과 같은 단순 암기식 설명도 피해야 합니다.
DDL은 항상 자동 COMMIT
TRUNCATE는 항상 ROLLBACK 불가이 동작은 DBMS마다 차이가 있습니다.
PostgreSQL처럼 명시적인 트랜잭션 안에서 일부 DDL과 TRUNCATE를 롤백할 수 있는 시스템도 있고, Oracle처럼 DDL의 암시적 COMMIT을 고려해야 하는 시스템도 있습니다.
SQL을 제대로 이해하려면 마지막으로 항상 두 가지를 확인해야 합니다.
무엇이 변경되었는가?
그리고:
그 변경은 어느 시점에 확정되었는가?
이 두 질문을 기준으로 SQL을 보면 DDL·DML·DCL·TCL은 단순한 분류명이 아니라 데이터베이스의 상태를 안전하게 변화시키는 역할 체계로 이해할 수 있습니다.