[SQLD] Part 02 - SQL 기본 (1)
이 글은 내가 SQLD 자격증을 위한 공부하면서 정리하는 메모이다.
Section 01. 관계형 데이터베이스 개요
관계형 데이터베이스란
데이터를 행(Row)과 열(Column)로 이루어진 테이블로 구성하고, 테이블 간 관계로 데이터를 관리하는 데이터베이스
1970년 E.F. Codd가 제안한 모델이다. 데이터를 테이블로 표현하고, 집합 연산을 통해 원하는 데이터를 조회한다.
관계형 데이터베이스의 특징:
- 정형화된 구조: 스키마가 미리 정의된다
- 데이터 독립성: 내부 구조가 바뀌어도 사용자 뷰는 유지된다
- 무결성 보장: 제약조건으로 잘못된 데이터를 막는다
- 집합 기반 처리: 한 번에 여러 행을 처리한다
SQL이란
Structured Query Language. 관계형 데이터베이스에서 데이터를 정의하고 조작하기 위한 표준 언어
1986년 ANSI, 1987년 ISO에서 표준으로 채택되었다. Oracle, MySQL, SQL Server 등 DBMS마다 방언(dialect)이 있지만 기본 문법은 거의 같다.
SQLD는 Oracle 문법을 기준으로 출제된다.
Section 02. SQL 종류
SQL은 역할에 따라 네 가지로 분류된다.
| 분류 | 이름 | 설명 | 주요 명령어 |
|---|---|---|---|
| DDL | Data Definition Language | 테이블 등 객체의 구조를 정의 | CREATE, ALTER, DROP, RENAME, TRUNCATE |
| DML | Data Manipulation Language | 데이터를 조작 | INSERT, UPDATE, DELETE, SELECT |
| DCL | Data Control Language | 접근 권한을 제어 | GRANT, REVOKE |
| TCL | Transaction Control Language | 트랜잭션을 제어 | COMMIT, ROLLBACK, SAVEPOINT |
DCL은 SQLD 범위에서 크게 안 나온다. DDL, DML, TCL, 그리고 WHERE 절까지가 이번 파트다.
Section 03. DDL
DDL은 테이블과 같은 데이터베이스 객체의 구조를 정의하는 언어다. DDL은 자동으로 COMMIT된다. ROLLBACK이 되지 않으므로 실행 전에 신중해야 한다.
CREATE TABLE
테이블을 생성한다.
CREATE TABLE 테이블명 (
컬럼명 데이터타입 [제약조건],
...
);
예:
CREATE TABLE MEMBER (
MEMBER_ID NUMBER NOT NULL,
NAME VARCHAR2(50) NOT NULL,
EMAIL VARCHAR2(100) UNIQUE,
AGE NUMBER CHECK (AGE >= 0),
STATUS VARCHAR2(10) DEFAULT 'ACTIVE',
CONSTRAINT PK_MEMBER PRIMARY KEY (MEMBER_ID)
);
주요 데이터 타입
Oracle 기준 자주 나오는 데이터 타입이다.
| 데이터 타입 | 설명 |
|---|---|
NUMBER(p, s) |
숫자. p: 전체 자릿수, s: 소수점 이하 자릿수 |
VARCHAR2(n) |
가변 길이 문자열. 최대 n바이트 |
CHAR(n) |
고정 길이 문자열. n바이트로 고정 |
DATE |
날짜와 시간 (년/월/일/시/분/초) |
CLOB |
대용량 문자 데이터 |
BLOB |
대용량 이진 데이터 |
VARCHAR2와 CHAR의 차이가 종종 나온다. CHAR(10)에 'ABC'를 저장하면 7칸이 공백으로 채워지고, VARCHAR2(10)에 저장하면 3바이트만 사용한다.
제약조건
| 제약조건 | 설명 |
|---|---|
PRIMARY KEY |
기본키. NOT NULL + UNIQUE. 테이블당 하나 |
FOREIGN KEY |
외래키. 참조 무결성 유지 |
UNIQUE |
중복 불가. NULL은 허용 |
NOT NULL |
NULL 불가 |
CHECK |
특정 조건 만족 여부 |
DEFAULT |
값 미입력 시 기본값 |
외래키 설정:
CREATE TABLE ORDERS (
ORDER_ID NUMBER PRIMARY KEY,
MEMBER_ID NUMBER,
ORDER_DATE DATE,
CONSTRAINT FK_ORDERS_MEMBER
FOREIGN KEY (MEMBER_ID) REFERENCES MEMBER(MEMBER_ID)
);
ALTER TABLE
기존 테이블의 구조를 변경한다.
컬럼 추가
ALTER TABLE MEMBER ADD (PHONE VARCHAR2(20));
컬럼 수정
ALTER TABLE MEMBER MODIFY (NAME VARCHAR2(100));
데이터가 이미 있으면 크기를 줄이거나 타입을 바꾸는 것이 제한될 수 있다.
컬럼 삭제
ALTER TABLE MEMBER DROP COLUMN PHONE;
컬럼 이름 변경
ALTER TABLE MEMBER RENAME COLUMN STATUS TO MEMBER_STATUS;
DROP TABLE, RENAME, TRUNCATE
테이블 삭제
DROP TABLE MEMBER;
참조하는 자식 테이블이 있으면 삭제가 안 된다. CASCADE CONSTRAINTS를 붙이면 참조 제약조건까지 함께 삭제된다.
DROP TABLE MEMBER CASCADE CONSTRAINTS;
테이블 이름 변경
RENAME MEMBER TO USER_ACCOUNT;
데이터 전체 삭제 (구조 유지)
TRUNCATE TABLE ORDERS;
DELETE, TRUNCATE, DROP의 차이가 자주 출제된다.
| 구분 | TRUNCATE | DROP | DELETE |
|---|---|---|---|
| 분류 | DDL | DDL | DML |
| ROLLBACK | 불가 | 불가 | 가능 |
| 구조 | 유지 | 삭제 | 유지 |
| 속도 | 빠름 | 빠름 | 느림 (행 단위) |
| WHERE | 사용 불가 | - | 사용 가능 |
| 로그 | 최소 | 없음 | 전체 |
TRUNCATE는 WHERE 절 없이 전체를 날리는데, 롤백이 안 된다는 점이 핵심이다.
Section 04. DML
DML은 테이블의 데이터를 조작하는 언어다. DDL과 달리 자동 커밋되지 않는다. COMMIT을 해야 변경이 확정된다.
INSERT
행을 삽입한다.
INSERT INTO MEMBER (MEMBER_ID, NAME, EMAIL)
VALUES (1, '홍길동', 'hong@email.com');
컬럼 목록을 생략하면 테이블 정의 순서대로 값을 입력해야 한다.
INSERT INTO MEMBER
VALUES (1, '홍길동', 'hong@email.com', 25, 'ACTIVE');
UPDATE
기존 데이터를 수정한다.
UPDATE MEMBER
SET EMAIL = 'new@email.com', AGE = 26
WHERE MEMBER_ID = 1;
WHERE 절이 없으면 전체 행이 수정된다. 항상 주의해야 한다.
DELETE
데이터를 삭제한다.
DELETE FROM MEMBER
WHERE MEMBER_ID = 1;
마찬가지로 WHERE 절이 없으면 전체 행이 삭제된다.
SELECT
데이터를 조회한다. SQL에서 가장 많이 쓰는 명령어다.
SELECT MEMBER_ID, NAME, EMAIL
FROM MEMBER;
전체 컬럼을 조회할 때는 *를 쓴다.
SELECT *
FROM MEMBER;
DISTINCT
중복된 결과를 제거한다.
SELECT DISTINCT STATUS
FROM MEMBER;
DISTINCT는 SELECT 바로 뒤에 한 번만 쓸 수 있고, 이후에 오는 모든 컬럼에 적용된다.
Alias (별칭)
컬럼이나 테이블에 임시 이름을 붙인다.
SELECT NAME AS 이름, EMAIL AS 이메일
FROM MEMBER M;
- 컬럼 별칭:
AS키워드 사용.AS는 생략 가능 - 별칭에 공백이 있거나 특수문자가 포함되면 큰따옴표로 감싼다
- 테이블 별칭: FROM 절에서 테이블명 뒤에 붙임.
AS불가 (Oracle 기준)
SELECT M.NAME AS "회원 이름"
FROM MEMBER M;
Section 05. TCL
트랜잭션이란
하나의 논리적인 작업 단위. 데이터베이스에서 분리할 수 없는 최소 작업 단위
"계좌 이체"를 예로 들면, A계좌에서 출금하고 B계좌에 입금하는 두 작업이 하나의 트랜잭션이다. 둘 다 성공하거나 둘 다 실패해야 한다. 중간 상태는 허용되지 않는다.
ACID
트랜잭션이 보장해야 하는 네 가지 성질이다.
| 특성 | 이름 | 설명 |
|---|---|---|
| A | Atomicity (원자성) | 트랜잭션은 전부 실행되거나 전혀 실행되지 않는다 |
| C | Consistency (일관성) | 트랜잭션 완료 후에도 데이터베이스가 일관된 상태를 유지한다 |
| I | Isolation (고립성) | 실행 중인 트랜잭션은 다른 트랜잭션의 영향을 받지 않는다 |
| D | Durability (지속성) | 커밋된 트랜잭션은 장애가 발생해도 영구적으로 반영된다 |
COMMIT
트랜잭션의 변경 내용을 영구적으로 저장한다.
UPDATE MEMBER SET NAME = '이순신' WHERE MEMBER_ID = 1;
COMMIT;
COMMIT 이후에는 ROLLBACK으로 되돌릴 수 없다.
ROLLBACK
트랜잭션을 취소하고 이전 상태로 되돌린다.
UPDATE MEMBER SET NAME = '이순신' WHERE MEMBER_ID = 1;
ROLLBACK;
마지막 COMMIT 시점으로 되돌아간다.
SAVEPOINT
트랜잭션 중간에 저장 지점을 설정한다.
UPDATE MEMBER SET NAME = '이순신' WHERE MEMBER_ID = 1;
SAVEPOINT SP1;
UPDATE MEMBER SET AGE = 30 WHERE MEMBER_ID = 1;
SAVEPOINT SP2;
UPDATE MEMBER SET EMAIL = 'new@test.com' WHERE MEMBER_ID = 1;
ROLLBACK TO SP1; -- SP1 이후 작업만 취소
ROLLBACK TO 저장점명으로 특정 지점까지만 취소할 수 있다.
DDL은 자동 COMMIT, DML은 명시적 COMMIT이 필요하다는 점을 헷갈리지 않아야 한다.
Section 06. WHERE 절
SELECT, UPDATE, DELETE에서 처리할 행을 필터링하는 절이다.
SELECT *
FROM MEMBER
WHERE 조건;
비교 연산자
| 연산자 | 의미 |
|---|---|
= |
같다 |
<> 또는 != |
같지 않다 |
> |
크다 |
>= |
크거나 같다 |
< |
작다 |
<= |
작거나 같다 |
SELECT * FROM MEMBER WHERE AGE >= 20;
SELECT * FROM MEMBER WHERE STATUS <> 'INACTIVE';
BETWEEN
범위 조건. 양 끝값을 포함한다.
SELECT * FROM MEMBER WHERE AGE BETWEEN 20 AND 30;
AGE >= 20 AND AGE <= 30과 같다. 날짜 범위 조회에도 자주 쓴다.
IN
목록 중 하나와 일치하는 조건.
SELECT * FROM MEMBER WHERE STATUS IN ('ACTIVE', 'PENDING');
STATUS = 'ACTIVE' OR STATUS = 'PENDING'과 같다.
반대는 NOT IN. 단, NULL이 포함된 목록에 NOT IN을 쓰면 결과가 빈 집합이 될 수 있어서 주의해야 한다.
LIKE
패턴 매칭.
| 와일드카드 | 의미 |
|---|---|
% |
0개 이상의 임의 문자 |
_ |
1개의 임의 문자 |
SELECT * FROM MEMBER WHERE NAME LIKE '김%'; -- 김으로 시작
SELECT * FROM MEMBER WHERE EMAIL LIKE '%@gmail.com'; -- gmail.com으로 끝남
SELECT * FROM MEMBER WHERE NAME LIKE '김_수'; -- 김으로 시작하고 수로 끝나는 세 글자
IS NULL / IS NOT NULL
NULL 여부 확인.
SELECT * FROM MEMBER WHERE EMAIL IS NULL;
SELECT * FROM MEMBER WHERE EMAIL IS NOT NULL;
NULL은 = NULL로 비교하면 안 된다. = NULL의 결과는 항상 FALSE다. 반드시 IS NULL을 써야 한다.
논리 연산자
| 연산자 | 설명 |
|---|---|
AND |
두 조건 모두 참 |
OR |
두 조건 중 하나 이상 참 |
NOT |
조건을 부정 |
SELECT * FROM MEMBER
WHERE AGE >= 20 AND STATUS = 'ACTIVE';
SELECT * FROM MEMBER
WHERE STATUS = 'INACTIVE' OR AGE < 18;
SELECT * FROM MEMBER
WHERE NOT STATUS = 'ACTIVE';
연산자 우선순위
우선순위가 헷갈리면 결과가 달라질 수 있다. 중요한 것만 기억하면 된다.
높음 1. 산술 연산자 (+, -, *, /)
2. 비교 연산자 (=, <>, >, <, >=, <=)
3. NOT
4. AND
낮음 5. OR
AND가 OR보다 우선순위가 높다는 것이 핵심이다.
-- 의도: (STATUS = 'ACTIVE' OR STATUS = 'PENDING') AND AGE >= 20
-- 실제: STATUS = 'ACTIVE' OR (STATUS = 'PENDING' AND AGE >= 20)
WHERE STATUS = 'ACTIVE' OR STATUS = 'PENDING' AND AGE >= 20
괄호로 명확하게 표현하는 것이 좋다.
WHERE (STATUS = 'ACTIVE' OR STATUS = 'PENDING') AND AGE >= 20