sqld
1. 데이터 모델링
데이터 모델링 특징
| 특징 | 설명 |
|---|---|
| 추상화 | 현실 세계를 일정 형식으로 표현 |
| 단순화 | 복잡한 현실을 약속된 규약으로 단순 표현 |
| 명확화 | 누구나 이해할 수 있도록 명확하게 표현 |
데이터 모델링 3단계
| 단계 | 명칭 | 설명 |
|---|---|---|
| 1단계 | 개념적 데이터 모델링 | 업무 중심의 핵심 엔터티 도출, ERD 작성 |
| 2단계 | 논리적 데이터 모델링 | 정규화, 속성·관계 정의, 재사용성 높음 |
| 3단계 | 물리적 데이터 모델링 | DBMS 특성 반영, 실제 테이블 생성 |
데이터 독립성
| 구분 | 설명 |
|---|---|
| 논리적 독립성 | 개념 스키마 변경이 외부 스키마에 영향을 주지 않음 |
| 물리적 독립성 | 내부 스키마 변경이 개념 스키마에 영향을 주지 않음 |
ERD 표기법 (까마귀발 / IE 표기법)
| 기호 | 의미 |
|---|---|
| | 1 (필수) |
O | 0 (선택) |
< (까마귀발) | 多 (여러 개) |
2. 엔터티
엔터티 분류
| 분류 기준 | 종류 | 설명 |
|---|---|---|
| 유·무형 | 유형 엔터티 | 물리적 형태 존재 (사원, 제품) |
| 유·무형 | 개념 엔터티 | 개념적 존재 (부서, 과목) |
| 유·무형 | 사건 엔터티 | 업무 수행 시 발생 (주문, 청구) |
| 발생 시점 | 기본 엔터티 | 독립적으로 생성 가능 |
| 발생 시점 | 중심 엔터티 | 기본 엔터티로부터 발생 |
| 발생 시점 | 행위 엔터티 | 두 개 이상의 엔터티 간 행위로 발생 |
엔터티 명명 규칙
- 업무에서 사용하는 용어 사용
- 단수 명사 사용
- 약어 사용 지양
- 유일한 이름 부여
3. 속성 (Attribute)
속성 분류
| 분류 | 종류 | 설명 |
|---|---|---|
| 특성 | 기본 속성 | 업무에서 직접 추출 |
| 특성 | 설계 속성 | 설계 과정에서 도출 (일련번호 등) |
| 특성 | 파생 속성 | 다른 속성으로부터 계산·유도 (합계, 평균) |
| 구성 | 단일값 속성 | 한 개의 값만 존재 |
| 구성 | 다중값 속성 | 여러 값 존재 → 별도 엔터티 분리 필요 |
도메인 (Domain)
속성이 가질 수 있는 값의 범위와 데이터 타입, 제약 사항의 집합
4. 관계 (Relationship)
관계 분류
| 종류 | 설명 |
|---|---|
| 존재 관계 | 엔터티 간 존재에 의한 관계 (사원-부서) |
| 행위 관계 | 특정 행위로 인한 관계 (고객-주문) |
관계 차수 (Cardinality)
| 표기 | 의미 |
|---|---|
| 1:1 | 일대일 |
| 1:M | 일대다 |
| M:N | 다대다 → 실제 구현 시 교차 엔터티로 분리 |
관계 선택성
| 구분 | 설명 |
|---|---|
| 필수 참여 | 반드시 관계가 존재해야 함 |
| 선택 참여 | 관계가 없어도 됨 |
5. 식별자 (Identifier)
주식별자 특성
| 특성 | 설명 |
|---|---|
| 유일성 | 인스턴스를 유일하게 구별 |
| 최소성 | 최소한의 속성으로 구성 |
| 불변성 | 값이 변하지 않아야 함 |
| 존재성 | NULL 불가 |
식별자 분류
| 분류 기준 | 종류 | 설명 |
|---|---|---|
| 대표성 | 주식별자 | 엔터티를 대표하는 식별자 |
| 대표성 | 보조 식별자 | 주식별자 외 유일성 보장 |
| 스스로 | 내부 식별자 | 엔터티 내에서 스스로 생성 |
| 스스로 | 외부 식별자 | 타 엔터티로부터 상속 (FK) |
| 속성 수 | 단일 식별자 | 하나의 속성으로 구성 |
| 속성 수 | 복합 식별자 | 두 개 이상의 속성으로 구성 |
| 대체 여부 | 본질 식별자 | 업무에 의해 만들어진 식별자 |
| 대체 여부 | 인조 식별자 | 인위적으로 만든 식별자 (시퀀스) |
식별 관계 vs 비식별 관계
| 구분 | 식별 관계 | 비식별 관계 |
|---|---|---|
| FK 위치 | 자식의 PK에 포함 | 자식의 일반 속성 |
| 부모 존재 | 부모 필수 | 부모 없어도 존재 가능 |
| 실선/점선 | 실선 | 점선 |
5-1. 키(Key)의 종류
키 개념 분류
| 키 종류 | 설명 | 특징 |
|---|---|---|
| 슈퍼키 (Super Key) | 행을 유일하게 식별할 수 있는 속성의 집합 | 유일성 O, 최소성 X |
| 후보키 (Candidate Key) | 슈퍼키 중 최소 속성으로 구성된 키 | 유일성 O, 최소성 O |
| 기본키 (Primary Key) | 후보키 중 대표로 선택된 키 | NULL 불가, 중복 불가 |
| 대리키 (Surrogate Key) | 기본키 대신 인위적으로 생성한 키 | 시퀀스·일련번호 |
| 대체키 (Alternate Key) | 후보키 중 기본키가 되지 못한 나머지 키 | UNIQUE 제약 |
| 외래키 (Foreign Key) | 다른 테이블의 기본키를 참조하는 키 | 참조 무결성 보장 |
| 복합키 (Composite Key) | 두 개 이상의 속성으로 구성된 키 | - |
키 간의 관계
슈퍼키
└── 후보키 (최소성 추가)
├── 기본키 (대표 선택, NOT NULL)
│ └── 대리키 (기본키 대체 목적)
└── 대체키 (선택받지 못한 후보키)
무결성 제약조건
| 종류 | 설명 |
|---|---|
| 개체 무결성 | 기본키는 NULL 불가, 중복 불가 |
| 참조 무결성 | 외래키는 참조 대상 PK에 존재하는 값이거나 NULL 이어야 함 |
| 도메인 무결성 | 속성 값은 정의된 도메인 범위 내에 있어야 함 |
| NULL 무결성 | NOT NULL로 지정된 속성에 NULL 불가 |
| 유일 무결성 | UNIQUE로 지정된 속성은 중복 불가 |
6. 정규화 (Normalization)
이상 현상 (Anomaly)
| 종류 | 설명 |
|---|---|
| 삽입 이상 | 불필요한 데이터를 함께 삽입해야 하는 경우 |
| 삭제 이상 | 원하지 않는 데이터까지 삭제되는 경우 |
| 갱신 이상 | 일부만 갱신되어 데이터 불일치 발생 |
정규화 단계
| 단계 | 명칭 | 제거 대상 |
|---|---|---|
| 1NF | 제1정규형 | 반복 그룹 제거, 원자값으로 구성 |
| 2NF | 제2정규형 | 부분 함수 종속 제거 (복합 PK일 때) |
| 3NF | 제3정규형 | 이행 함수 종속 제거 (A→B→C) |
| BCNF | 보이스-코드 정규형 | 결정자가 후보키가 아닌 함수 종속 제거 |
| 4NF | 제4정규형 | 다치 종속 제거 |
| 5NF | 제5정규형 | 조인 종속 제거 |
반정규화 (De-normalization)
| 기법 | 설명 |
|---|---|
| 테이블 병합 | 1:1 또는 1:M 관계 테이블 합침 |
| 테이블 분할 | 수평/수직 분할 |
| 중복 컬럼 추가 | 조인 비용 줄이기 위해 속성 중복 |
| 파생 컬럼 추가 | 집계값을 컬럼으로 저장 |
7. 관계형 데이터 모델
관계형 데이터베이스 용어
| 관계형 용어 | 파일 시스템 용어 | 설명 |
|---|---|---|
| 릴레이션 (Relation) | 파일 | 테이블 |
| 튜플 (Tuple) | 레코드 | 행 (Row) |
| 속성 (Attribute) | 필드 | 열 (Column) |
| 도메인 (Domain) | - | 속성 값의 범위 |
| 카디널리티 | 레코드 수 | 행의 수 |
| 차수 | 필드 수 | 열의 수 |
관계 대수 연산자
| 연산자 | 기호 | 설명 |
|---|---|---|
| 셀렉션 | σ | 조건에 맞는 튜플 추출 (행 선택) |
| 프로젝션 | π | 특정 속성만 추출 (열 선택) |
| 조인 | ⋈ | 두 릴레이션을 공통 속성으로 결합 |
| 합집합 | ∪ | 두 릴레이션의 합 |
| 교집합 | ∩ | 두 릴레이션의 교 |
| 차집합 | - | 한쪽에만 있는 튜플 |
| 카티션 프로덕트 | × | 모든 튜플의 조합 |
| 디비전 | ÷ | 나눗셈 연산 |
8. 성능 데이터 모델링
인덱스 (Index)
| 항목 | 내용 |
|---|---|
| 목적 | 검색 속도 향상 |
| B-Tree 구조 | 일반적 인덱스 구조, 범위 검색 유리 |
| 인덱스 손익 분기 | 전체 데이터의 약 10~15% 이하 조회 시 유리 |
| 인덱스 컬럼 순서 | = 조건 → 범위 조건 순서로 구성 |
분산 데이터베이스 투명성
| 종류 | 설명 |
|---|---|
| 위치 투명성 | 데이터 저장 위치를 몰라도 접근 가능 |
| 복제 투명성 | 복제 여부를 몰라도 접근 가능 |
| 분할 투명성 | 분할 여부를 몰라도 접근 가능 |
| 병행 투명성 | 다수 트랜잭션 동시 수행 시 결과 보장 |
| 장애 투명성 | 장애 발생 시 자동 복구 |
SQL 문법
SQL 명령어 분류 (DDL / DML / TCL / DCL)
| 분류 | 명령어 | 설명 | AUTO COMMIT |
|---|---|---|---|
| DDL | CREATE | 객체 생성 | O (자동) |
| DDL | ALTER | 객체 구조 변경 | O (자동) |
| DDL | DROP | 객체 삭제 | O (자동) |
| DDL | TRUNCATE | 테이블 전체 행 삭제 (롤백 불가) | O (자동) |
| DDL | RENAME | 객체 이름 변경 | O (자동) |
| DML | SELECT | 데이터 조회 | X |
| DML | INSERT | 데이터 삽입 | X |
| DML | UPDATE | 데이터 수정 | X |
| DML | DELETE | 데이터 삭제 (롤백 가능) | X |
| TCL | COMMIT | 트랜잭션 확정 | - |
| TCL | ROLLBACK | 트랜잭션 취소 | - |
| TCL | SAVEPOINT | 중간 저장점 설정 | - |
| DCL | GRANT | 권한 부여 | - |
| DCL | REVOKE | 권한 회수 | - |
TRUNCATE vs DELETE: TRUNCATE는 DDL이므로 롤백 불가, DELETE는 DML이므로 롤백 가능 DDL은 자동 커밋, DML은 수동 커밋
SELECT 쿼리 실행 순서
SELECT col -- 5. 열 선택 및 표현식 계산
FROM table -- 1. 테이블 접근
WHERE condition -- 2. 행 필터링
GROUP BY col -- 3. 그룹화
HAVING condition -- 4. 그룹 필터링
ORDER BY col -- 6. 정렬
LIMIT n -- 7. 행 수 제한
| 실행 순서 | 절 | 설명 |
|---|---|---|
| 1 | FROM | 참조 테이블 결정 |
| 2 | WHERE | 개별 행 조건 필터 |
| 3 | GROUP BY | 그룹 집계 |
| 4 | HAVING | 그룹 조건 필터 |
| 5 | SELECT | 출력 컬럼 결정 |
| 6 | ORDER BY | 정렬 (SELECT 별칭 사용 가능) |
| 7 | LIMIT / FETCH | 행 수 제한 |
WHERE에서는 SELECT 별칭(alias) 사용 불가 - SELECT보다 먼저 실행되기 때문
Alias (별칭)
-- 컬럼 별칭
SELECT salary * 12 AS annual_salary
FROM employees;
-- 공백/특수문자 포함 시 큰따옴표 사용
SELECT salary * 12 AS "연봉 합계"
FROM employees;
-- 테이블 별칭
SELECT e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id;
| 규칙 | 내용 |
|---|---|
| AS 생략 | 가능 (col "별칭") |
| 큰따옴표 | 공백·특수문자·대소문자 구분 시 필수 |
| WHERE 사용 | 불가 (실행 순서상 SELECT 이후) |
| ORDER BY 사용 | 가능 |
| HAVING 사용 | 불가 |
식별자 구분
SQL에서 식별자(Identifier)는 테이블명·컬럼명·별칭 등 객체 이름을 가리키며, 리터럴(문자열 값)과 구분해서 사용해야 함
따옴표 종류
| 기호 | 종류 | 용도 | 예시 |
|---|---|---|---|
' (작은따옴표) | 문자열 리터럴 | 값 비교, 문자열 데이터 | WHERE name = '홍길동' |
" (큰따옴표) | 인용 식별자 | 컬럼명·테이블명·별칭에 사용 | SELECT sal AS "연봉 합계" |
` (백틱, MySQL) | 인용 식별자 | MySQL에서 컬럼명·테이블명 | SELECT `order` FROM t |
Oracle/표준 SQL은 큰따옴표, MySQL은 백틱을 식별자 인용에 사용
일반 식별자 vs 인용 식별자
| 구분 | 일반 식별자 | 인용 식별자 |
|---|---|---|
| 표기 | 따옴표 없음 | 큰따옴표로 감쌈 |
| 대소문자 | 구분 안 함 (내부적으로 대문자로 처리) | 작성한 대로 구분 |
| 예약어 사용 | 불가 | 가능 ("SELECT", "FROM") |
| 공백·특수문자 | 불가 | 가능 ("연봉 합계") |
| 시작 문자 | 문자 또는 _ | 제한 없음 |
-- 일반 식별자: 대소문자 무시
SELECT EmpName FROM Employees; -- empname, EMPNAME 모두 동일
-- 인용 식별자: 대소문자 구분, 공백/예약어 허용
SELECT "EmpName", "연봉 합계" FROM "My Table";
-- 예약어를 컬럼명으로 쓸 경우 큰따옴표 필수
CREATE TABLE t ("select" NUMBER, "from" VARCHAR2(10));
식별자 명명 규칙
| 규칙 | 내용 |
|---|---|
| 시작 문자 | 반드시 문자(알파벳) 또는 _ 로 시작 |
| 사용 가능 문자 | 문자, 숫자, _, $, # |
| 최대 길이 | Oracle 30자, SQL Server 128자 |
| 예약어 | 일반 식별자로 사용 불가 (인용 식별자는 가능) |
| 중복 | 동일 스코프 내 중복 불가 |
NULL 특징
| 특징 | 설명 |
|---|---|
| NULL의 정의 | 알 수 없는 값, 부재 값 (0이나 공백과 다름) |
| 산술 연산 | NULL 포함 연산 결과는 항상 NULL |
| 비교 연산 | NULL = NULL → FALSE (비교 불가) |
| NULL 비교 | IS NULL / IS NOT NULL 사용 |
| 집계 함수 | COUNT(*) 제외, NULL 행은 집계에서 제외 |
| 정렬 | Oracle: NULL 가장 크게 취급 / SQL Server: 가장 작게 취급 |
-- NULL 처리 함수
NVL(col, 0) -- Oracle: NULL이면 0으로 대체
ISNULL(col, 0) -- SQL Server
COALESCE(col1, col2, 0) -- 표준 SQL: 첫 번째 비-NULL 반환
NULLIF(col, 0) -- col이 0이면 NULL 반환
-- NULL 비교
WHERE col IS NULL
WHERE col IS NOT NULL
-- NULL 포함 연산 예시
SELECT 1 + NULL; -- NULL
SELECT NULL = NULL; -- NULL (FALSE가 아님)
데이터 타입
| 분류 | 타입 | 설명 |
|---|---|---|
| 문자 | CHAR(n) | 고정 길이, 남은 자리 공백 패딩 |
| 문자 | VARCHAR2(n) / VARCHAR(n) | 가변 길이, 실제 길이만 저장 |
| 문자 | CLOB | 대용량 문자 데이터 |
| 숫자 | NUMBER(p, s) | 전체 자릿수 p, 소수점 이하 s |
| 숫자 | INT / INTEGER | 정수 |
| 날짜 | DATE | 날짜+시간 (Oracle: 년월일시분초) |
| 날짜 | TIMESTAMP | DATE보다 정밀한 시간 (나노초) |
| 이진 | BLOB | 대용량 이진 데이터 |
CHAR vs VARCHAR: CHAR('A') = CHAR('A ') 비교 시 공백 무시하여 같음, VARCHAR는 공백 포함하여 다름
WHERE 조건 연산자
| 연산자 | 설명 | 예시 |
|---|---|---|
= | 같음 | WHERE age = 30 |
<> / != | 다름 | WHERE age <> 30 |
BETWEEN A AND B | A 이상 B 이하 (경계 포함) | WHERE age BETWEEN 20 AND 30 |
IN (list) | 목록 중 하나 | WHERE dept IN ('HR', 'IT') |
LIKE | 패턴 매칭 | WHERE name LIKE '김%' |
IS NULL | NULL 여부 | WHERE col IS NULL |
NOT | 부정 | WHERE NOT age = 30 |
-- LIKE 와일드카드
% -- 0개 이상 임의 문자
_ -- 정확히 1개 임의 문자
WHERE name LIKE '김%' -- 김으로 시작
WHERE name LIKE '%수' -- 수로 끝남
WHERE name LIKE '_수_' -- 3글자 중간이 수
WHERE name LIKE '\_홍%' ESCAPE '\' -- _ 문자 자체를 검색
JOIN
| 종류 | 설명 |
|---|---|
| INNER JOIN | 양쪽 모두 일치하는 행만 반환 |
| LEFT OUTER JOIN | 왼쪽 전체 + 오른쪽 일치 (없으면 NULL) |
| RIGHT OUTER JOIN | 오른쪽 전체 + 왼쪽 일치 (없으면 NULL) |
| FULL OUTER JOIN | 양쪽 전체 (일치 없으면 NULL) |
| CROSS JOIN | 카티션 곱 (모든 조합) |
| SELF JOIN | 같은 테이블끼리 조인 |
| NATURAL JOIN | 동일 컬럼명 자동 조인 (ON/USING 불필요) |
-- ANSI 표준 JOIN
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id;
-- OUTER JOIN
SELECT e.name, d.dept_name
FROM employees e
LEFT OUTER JOIN departments d ON e.dept_id = d.id;
-- Oracle 구문 (+): 오른쪽 테이블이 기준
WHERE e.dept_id = d.id(+) -- LEFT OUTER JOIN 과 동일
집합 연산자
| 연산자 | 중복 제거 | 설명 |
|---|---|---|
| UNION | O | 합집합 (중복 제거) |
| UNION ALL | X | 합집합 (중복 포함) |
| INTERSECT | O | 교집합 |
| MINUS / EXCEPT | O | 차집합 |
조건: 양쪽 SELECT의 컬럼 수와 데이터 타입이 동일해야 함
SELECT name FROM employees
UNION
SELECT name FROM contractors;
단일행 함수
문자 함수
| 함수 | 설명 | 예시 |
|---|---|---|
UPPER(s) | 대문자 변환 | UPPER('hello') → HELLO |
LOWER(s) | 소문자 변환 | LOWER('HELLO') → hello |
SUBSTR(s, n, len) | 부분 문자열 | SUBSTR('HELLO', 2, 3) → ELL |
LENGTH(s) | 문자열 길이 | LENGTH('ABC') → 3 |
TRIM(s) | 앞뒤 공백 제거 | TRIM(' AB ') → AB |
LPAD(s, n, c) | 왼쪽 패딩 | LPAD('5', 3, '0') → 005 |
RPAD(s, n, c) | 오른쪽 패딩 | RPAD('5', 3, '0') → 500 |
REPLACE(s, a, b) | 치환 | REPLACE('ABC', 'B', 'X') → AXC |
INSTR(s, c) | 위치 반환 | INSTR('ABC', 'B') → 2 |
CONCAT(a, b) | 문자열 연결 | CONCAT('AB', 'CD') → ABCD |
숫자 함수
| 함수 | 설명 | 예시 |
|---|---|---|
ROUND(n, d) | 반올림 | ROUND(3.567, 2) → 3.57 |
TRUNC(n, d) | 버림 | TRUNC(3.567, 2) → 3.56 |
CEIL(n) | 올림 | CEIL(3.1) → 4 |
FLOOR(n) | 내림 | FLOOR(3.9) → 3 |
MOD(a, b) | 나머지 | MOD(10, 3) → 1 |
ABS(n) | 절대값 | ABS(-5) → 5 |
SIGN(n) | 부호 반환 | SIGN(-3) → -1 |
POWER(a, b) | 거듭제곱 | POWER(2, 3) → 8 |
날짜 함수
| 함수 | 설명 |
|---|---|
SYSDATE | 현재 날짜+시간 (Oracle) |
GETDATE() | 현재 날짜+시간 (SQL Server) |
CURRENT_DATE | 현재 날짜 (표준) |
ADD_MONTHS(d, n) | n개월 더하기 |
MONTHS_BETWEEN(d1, d2) | 두 날짜의 개월 차 |
LAST_DAY(d) | 해당 월의 마지막 날 |
NEXT_DAY(d, '요일') | 다음 지정 요일 날짜 |
TO_DATE(s, fmt) | 문자열 → 날짜 변환 |
TO_CHAR(d, fmt) | 날짜 → 문자열 변환 |
-- 날짜 연산
SELECT SYSDATE + 7 FROM dual; -- 7일 후
SELECT SYSDATE - 1 FROM dual; -- 어제
SELECT MONTHS_BETWEEN(SYSDATE, hire_date) -- 근무 개월 수
FROM employees;
변환 함수
| 함수 | 설명 |
|---|---|
TO_CHAR(n/d, fmt) | 숫자·날짜 → 문자 |
TO_NUMBER(s, fmt) | 문자 → 숫자 |
TO_DATE(s, fmt) | 문자 → 날짜 |
CAST(expr AS type) | 표준 타입 변환 |
조건 함수
-- CASE WHEN
SELECT name,
CASE grade
WHEN 'A' THEN '우수'
WHEN 'B' THEN '보통'
ELSE '미흡'
END AS grade_label
FROM students;
-- DECODE (Oracle)
SELECT DECODE(grade, 'A', '우수', 'B', '보통', '미흡')
FROM students;
-- CASE WHEN 범위 조건
SELECT name,
CASE
WHEN score >= 90 THEN 'A'
WHEN score >= 80 THEN 'B'
ELSE 'C'
END AS grade
FROM students;
집계 함수 (그룹 함수)
| 함수 | 설명 | NULL 처리 |
|---|---|---|
COUNT(*) | 전체 행 수 | NULL 포함 |
COUNT(col) | 해당 컬럼 행 수 | NULL 제외 |
SUM(col) | 합계 | NULL 제외 |
AVG(col) | 평균 | NULL 제외 |
MAX(col) | 최대값 | NULL 제외 |
MIN(col) | 최소값 | NULL 제외 |
STDDEV(col) | 표준편차 | NULL 제외 |
VARIANCE(col) | 분산 | NULL 제외 |
-- GROUP BY / HAVING
SELECT dept_id, COUNT(*), AVG(salary)
FROM employees
GROUP BY dept_id
HAVING AVG(salary) > 5000
ORDER BY dept_id;
WHERE vs HAVING: WHERE는 개별 행 필터(집계 함수 불가), HAVING은 그룹 필터(집계 함수 사용 가능)
서브쿼리 (Subquery)
| 종류 | 위치 | 설명 |
|---|---|---|
| 단일행 서브쿼리 | WHERE | 결과가 1행 1열, = < > 사용 |
| 다중행 서브쿼리 | WHERE | 결과가 여러 행, IN ANY ALL 사용 |
| 다중열 서브쿼리 | WHERE | 결과가 여러 열 |
| 인라인 뷰 | FROM | FROM 절의 서브쿼리, 임시 테이블처럼 사용 |
| 스칼라 서브쿼리 | SELECT | SELECT 절의 서브쿼리, 단일값 반환 |
| 상관 서브쿼리 | WHERE | 외부 쿼리 참조, 행마다 실행 |
-- 다중행 서브쿼리
SELECT name FROM employees
WHERE dept_id IN (SELECT id FROM departments WHERE location = 'Seoul');
-- ANY / ALL
WHERE salary > ANY (SELECT salary FROM employees WHERE grade = 'B') -- B 중 하나라도 크면
WHERE salary > ALL (SELECT salary FROM employees WHERE grade = 'B') -- B 전부보다 크면
-- 인라인 뷰
SELECT name, salary
FROM (SELECT name, salary, RANK() OVER (ORDER BY salary DESC) AS rnk FROM employees) t
WHERE rnk <= 3;
-- 스칼라 서브쿼리
SELECT name, (SELECT dept_name FROM departments WHERE id = e.dept_id) AS dept
FROM employees e;
윈도우 함수 (분석 함수)
| 분류 | 함수 | 설명 | RANK/ROW_NUMBER 차이 |
|---|---|---|---|
| 순위 | ROW_NUMBER() | 고유 순위 - 동일값도 항상 다른 번호 | 1, 2, 3, 4 |
| 순위 | RANK() | 동일값 동순위, 다음 순위 건너뜀 | 1, 1, 3, 4 |
| 순위 | DENSE_RANK() | 동일값 동순위, 건너뜀 없음 | 1, 1, 2, 3 |
| 분포 | NTILE(n) | 전체 행을 n개 그룹으로 균등 분할 | - |
| 분포 | PERCENT_RANK() | 순위 백분율 (0~1) | - |
| 분포 | CUME_DIST() | 누적 분포 비율 | - |
| 행 참조 | LAG(col, n, default) | 현재 행 기준 이전 n번째 행 값 | - |
| 행 참조 | LEAD(col, n, default) | 현재 행 기준 이후 n번째 행 값 | - |
| 행 참조 | FIRST_VALUE(col) | 파티션 내 첫 번째 행 값 | - |
| 행 참조 | LAST_VALUE(col) | 파티션 내 마지막 행 값 (RANGE 주의) | - |
| 집계 | SUM(col) OVER() | 파티션 내 누적 합 | - |
| 집계 | AVG(col) OVER() | 파티션 내 평균 | - |
| 집계 | COUNT(col) OVER() | 파티션 내 건수 | - |
RANK vs DENSE_RANK vs ROW_NUMBER: 동점자 처리가 핵심 차이.
RANK는 다음 순위를 건너뛰고,DENSE_RANK는 연속,ROW_NUMBER는 항상 고유
-- 기본 구조: 함수() OVER (PARTITION BY ... ORDER BY ...)
SELECT name, dept_id, salary,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS row_num,
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rnk,
DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dense_rnk,
LAG(salary, 1, 0) OVER (ORDER BY hire_date) AS prev_salary,
LEAD(salary, 1, 0) OVER (ORDER BY hire_date) AS next_salary,
SUM(salary) OVER (PARTITION BY dept_id) AS dept_total
FROM employees;
-- 같은 salary가 100, 100, 90 일 때 각 함수 결과:
-- ROW_NUMBER: 1, 2, 3
-- RANK: 1, 1, 3
-- DENSE_RANK: 1, 1, 2
ROLLUP / CUBE / GROUPING SETS
| 구문 | 생성되는 그룹 조합 | 설명 |
|---|---|---|
GROUP BY a, b | (a, b) | 일반 그룹화, 소계 없음 |
ROLLUP(a, b) | (a, b) → (a) → () | 계층적 소계, 오른쪽부터 제거 |
CUBE(a, b) | (a, b), (a), (b), () | 모든 조합 소계 |
GROUPING SETS(a, b) | (a), (b) | 지정한 그룹만 개별 소계 |
GROUPING(col) | - | 소계 행이면 1, 일반 행이면 0 반환 |
SELECT dept_id, job, SUM(salary)
FROM employees
GROUP BY ROLLUP(dept_id, job);
ROLLUP 실행 전 (원본 데이터)
dept_id | job | salary
---------+-------+--------
10 | DEV | 5000
10 | DEV | 4000
10 | QA | 3000
20 | MGR | 7000
ROLLUP 실행 후 (결과)
dept_id | job | SUM(salary) -- 의미
---------+-------+-------------
10 | DEV | 9000 -- (dept=10, job=DEV) 소계
10 | QA | 3000 -- (dept=10, job=QA) 소계
10 | NULL | 12000 -- (dept=10) 소계 ← ROLLUP 생성
20 | MGR | 7000 -- (dept=20, job=MGR) 소계
20 | NULL | 7000 -- (dept=20) 소계 ← ROLLUP 생성
NULL | NULL | 19000 -- 전체 합계 ← ROLLUP 생성
GROUPING(dept_id) = 1이면 소계 행,= 0이면 일반 행임을 구분할 수 있음
-- CUBE: 모든 조합
SELECT dept_id, job, SUM(salary)
FROM employees
GROUP BY CUBE(dept_id, job);
-- 결과: (dept, job), (dept), (job), (전체) 4가지 조합 모두 생성
-- GROUPING SETS: 원하는 조합만
SELECT dept_id, job, SUM(salary)
FROM employees
GROUP BY GROUPING SETS((dept_id), (job));
-- 결과: dept 소계 + job 소계만 생성 (전체 합계 미포함)
-- GROUPING() 활용
SELECT
CASE GROUPING(dept_id) WHEN 1 THEN '전체' ELSE TO_CHAR(dept_id) END AS dept,
SUM(salary)
FROM employees
GROUP BY ROLLUP(dept_id);
PIVOT / UNPIVOT
행(Row)을 열(Column)로, 또는 열을 행으로 변환하는 구문
| 구문 | 방향 | 설명 |
|---|---|---|
PIVOT | 행 → 열 | 특정 컬럼 값을 새로운 컬럼으로 전환 |
UNPIVOT | 열 → 행 | 컬럼을 행으로 풀어서 정규화 |
PIVOT 전 (원본)
year | quarter | sales
------+---------+-------
2024 | Q1 | 100
2024 | Q2 | 200
2024 | Q3 | 150
2024 | Q4 | 300
PIVOT 후
year | Q1 | Q2 | Q3 | Q4
------+------+------+------+------
2024 | 100 | 200 | 150 | 300
-- Oracle PIVOT
SELECT *
FROM (
SELECT year, quarter, sales FROM quarterly_sales
)
PIVOT (
SUM(sales) -- 집계 함수
FOR quarter IN ('Q1', 'Q2', 'Q3', 'Q4') -- 열로 만들 값
);
-- Oracle UNPIVOT
SELECT year, quarter, sales
FROM pivot_result
UNPIVOT (
sales -- 값을 담을 컬럼명
FOR quarter IN (Q1, Q2, Q3, Q4) -- 열 → 행으로 풀 컬럼들
);
-- CASE WHEN으로 동일 효과 (표준 SQL)
SELECT year,
SUM(CASE WHEN quarter = 'Q1' THEN sales END) AS Q1,
SUM(CASE WHEN quarter = 'Q2' THEN sales END) AS Q2,
SUM(CASE WHEN quarter = 'Q3' THEN sales END) AS Q3,
SUM(CASE WHEN quarter = 'Q4' THEN sales END) AS Q4
FROM quarterly_sales
GROUP BY year;
DCL / TCL 상세
-- 권한 부여/회수
GRANT SELECT, INSERT ON employees TO user1;
REVOKE INSERT ON employees FROM user1;
-- WITH GRANT OPTION: 권한 받은 사용자가 다른 사용자에게 재부여 가능
GRANT SELECT ON employees TO user1 WITH GRANT OPTION;
-- 트랜잭션
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
SAVEPOINT sp1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
ROLLBACK TO sp1; -- sp1 이후 작업만 취소
COMMIT;
트랜잭션 특성 (ACID)
| 특성 | 영문 | 설명 |
|---|---|---|
| 원자성 | Atomicity | 전부 성공하거나 전부 실패 |
| 일관성 | Consistency | 트랜잭션 전후 데이터 무결성 유지 |
| 격리성 | Isolation | 다른 트랜잭션 간섭 없음 |
| 지속성 | Durability | COMMIT 후 영구 반영 |
DDL 구문 상세
-- 테이블 생성
CREATE TABLE employees (
id NUMBER(10) PRIMARY KEY,
name VARCHAR2(50) NOT NULL,
dept_id NUMBER(10) REFERENCES departments(id),
salary NUMBER(10, 2) DEFAULT 0,
hire_date DATE
);
-- 컬럼 추가
ALTER TABLE employees ADD phone VARCHAR2(20);
-- 컬럼 수정
ALTER TABLE employees MODIFY phone VARCHAR2(30);
-- 컬럼 삭제
ALTER TABLE employees DROP COLUMN phone;
-- 테이블 삭제
DROP TABLE employees;
DROP TABLE employees CASCADE CONSTRAINTS; -- 참조 제약조건도 함께 제거
-- 전체 데이터 삭제 (롤백 불가)
TRUNCATE TABLE employees;
제약조건 (Constraint)
| 제약조건 | 키워드 | NULL 허용 | 중복 허용 | 설명 |
|---|---|---|---|---|
| 기본키 | PRIMARY KEY | X | X | NOT NULL + UNIQUE 조합, 테이블당 1개 |
| 외래키 | FOREIGN KEY | O | O | 참조 대상 PK/UK 값이거나 NULL |
| 유일 | UNIQUE | O | X | NULL은 중복 허용 (DBMS마다 상이) |
| NOT NULL | NOT NULL | X | O | NULL 불가 |
| 체크 | CHECK | - | - | 지정 조건 만족해야 함 |
| 기본값 | DEFAULT | - | - | 값 미입력 시 기본값 사용 |
외래키 참조 동작 (ON DELETE / ON UPDATE)
| 옵션 | 설명 |
|---|---|
CASCADE | 부모 행 삭제/수정 시 자식 행도 함께 삭제/수정 |
SET NULL | 부모 행 삭제/수정 시 자식 FK 컬럼을 NULL로 변경 |
SET DEFAULT | 부모 행 삭제/수정 시 자식 FK 컬럼에 기본값 설정 |
RESTRICT | 자식 행이 존재하면 부모 행 삭제/수정 불가 |
NO ACTION | RESTRICT와 유사, 트랜잭션 종료 시 검사 |
-- 제약조건 컬럼 레벨
CREATE TABLE orders (
order_id NUMBER PRIMARY KEY,
cust_id NUMBER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
amount NUMBER CHECK (amount > 0),
status VARCHAR2(10) DEFAULT 'PENDING'
);
-- 제약조건 테이블 레벨 (이름 부여)
CREATE TABLE orders (
order_id NUMBER,
cust_id NUMBER,
CONSTRAINT pk_order PRIMARY KEY (order_id),
CONSTRAINT fk_order_cust FOREIGN KEY (cust_id)
REFERENCES customers(id) ON DELETE SET NULL
);
-- 제약조건 추가/삭제
ALTER TABLE employees ADD CONSTRAINT emp_dept_fk
FOREIGN KEY (dept_id) REFERENCES departments(id) ON DELETE CASCADE;
ALTER TABLE employees DROP CONSTRAINT emp_dept_fk;
-- 제약조건 비활성화/활성화 (Oracle)
ALTER TABLE employees DISABLE CONSTRAINT emp_dept_fk;
ALTER TABLE employees ENABLE CONSTRAINT emp_dept_fk;