이번 문서의 목표: 이 파일을 다 읽으면 06편의 키·무결성 개념과 11편의 관계 스키마 사상 결과를 실제 Oracle SQL의 CREATE TABLE 문으로 옮겨 적을 수 있고, INSERT·UPDATE·DELETE로 데이터를 조작하며, SELECT-FROM-WHERE 질의에 연산자·정렬·집계 함수를 조합해 원하는 결과를 정확히 예측할 수 있게 된다.
왜 SQL이 필요한가
06편에서는 릴레이션·키·무결성을 수학적으로 정의했고, 07·08·09편에서는 관계대수·관계해석이라는 이론적 질의 언어로 “무엇을 어떻게 구할 것인가”를 다뤘다. 그런데 실제 DBMS(Database Management System, 데이터베이스 관리 시스템)에 테이블을 만들고 데이터를 넣고 꺼내려면, 이 이론을 실제로 실행할 수 있는 언어가 필요하다. 그 언어가 SQL(Structured Query Language, 구조화 질의 언어)이다.
쉽게 말하면: SQL은 “표를 만들고, 표에 데이터를 넣고 고치고 지우고, 표에서 원하는 데이터를 꺼내는” 일을 문장으로 표현하는 언어다.
SQL은 하는 일에 따라 세 갈래로 나뉜다.
| 구분 | 이름 | 하는 일 | 대표 문장 |
|---|---|---|---|
| DDL | Data Definition Language(데이터 정의어) | 테이블 구조 자체를 정의·변경·삭제 | CREATE, ALTER, DROP |
| DML | Data Manipulation Language(데이터 조작어) | 테이블 안의 데이터를 조작 | INSERT, UPDATE, DELETE, SELECT |
| DCL | Data Control Language(데이터 제어어) | 데이터 접근 권한을 통제 | GRANT, REVOKE |
이 편은 DDL과 DML(그중 INSERT·UPDATE·DELETE·기본 SELECT)을 다루고, DCL과 SQL의 조인·서브쿼리·집합 연산은 13·14편에서 이어서 다룬다. 이 사이트는 SQLD 과목과 문법을 통일하기 위해 이후 모든 SQL 예제를 Oracle 문법 기준으로 작성한다.
이 편에서 계속 쓸 예제 스키마
11편의 사상 규칙에 따라 학생·과목·수강 관계를 다음 세 릴레이션으로 설계했다고 하자. 이 세 테이블은 이후 13·14편에서도 그대로 이어 쓴다.
STUDENT(학번, 이름, 학과) — 기본키: 학번
COURSE(과목코드, 과목명, 학점) — 기본키: 과목코드
ENROLL(학번, 과목코드, 성적) — 기본키: (학번, 과목코드), 외래키: 학번 → STUDENT, 과목코드 → COURSE
06편에서 배운 개념을 그대로 대응시키면, STUDENT와 COURSE는 독립된 개체이고 ENROLL은 두 개체를 다대다(N:M)로 연결하는 관계 릴레이션이며, ENROLL의 기본키가 두 외래키를 합친 복합키라는 점이 06편의 “여러 속성으로 이뤄진 기본키” 개념과 정확히 일치한다.
1. DDL: CREATE TABLE로 테이블 정의하기
왜 필요한가
릴레이션 스키마를 정했다면, 이제 DBMS에게 “이런 구조의 표를 만들어 달라”고 정확히 지시해야 한다. CREATE TABLE은 테이블 이름, 각 속성(컬럼)의 이름·자료형(도메인)·제약을 한 번에 선언하는 문장이다.
정의
Oracle의 CREATE TABLE 기본 구조는 다음과 같다.
CREATE TABLE 테이블명 (
컬럼명1 자료형 [제약],
컬럼명2 자료형 [제약],
...
[테이블 수준 제약]
);주로 쓰는 Oracle 자료형은 다음과 같다.
| 자료형 | 의미 |
|---|---|
VARCHAR2(n) | 최대 n바이트 가변 길이 문자열 |
NUMBER(p,s) | 전체 자릿수 p, 소수점 이하 자릿수 s인 숫자 |
DATE | 날짜·시간 값 |
CHAR(n) | 고정 길이 n인 문자열 |
계산·적용
STUDENT, COURSE, ENROLL 세 테이블을 실제로 정의한다. 06편의 무결성 제약을 SQL 제약으로 그대로 옮긴다.
CREATE TABLE STUDENT (
학번 VARCHAR2(10) PRIMARY KEY,
이름 VARCHAR2(20) NOT NULL,
학과 VARCHAR2(20)
);
CREATE TABLE COURSE (
과목코드 VARCHAR2(10) PRIMARY KEY,
과목명 VARCHAR2(30) NOT NULL,
학점 NUMBER(1) CHECK (학점 BETWEEN 1 AND 4)
);
CREATE TABLE ENROLL (
학번 VARCHAR2(10),
과목코드 VARCHAR2(10),
성적 VARCHAR2(2),
PRIMARY KEY (학번, 과목코드),
FOREIGN KEY (학번) REFERENCES STUDENT(학번),
FOREIGN KEY (과목코드) REFERENCES COURSE(과목코드)
);한 줄씩 06편의 이론과 대응시켜 보면 다음과 같다.
PRIMARY KEY: 06편의 엔티티 무결성(기본키는 널을 가질 수 없다)을 구현하는 제약이다.PRIMARY KEY로 지정된 컬럼은 자동으로NOT NULL과UNIQUE(중복 불가)가 함께 적용된다.NOT NULL: 기본키가 아니어도 반드시 값이 있어야 하는 컬럼(이름·과목명)에 별도로 붙이는 제약이다.CHECK (학점 BETWEEN 1 AND 4): 06편의 도메인 무결성(속성 값이 정의된 범위를 벗어나면 안 된다)을 구현한다. 학점이 1~4 사이가 아닌 값(예: 0이나 10)은 삽입이 거부된다.FOREIGN KEY ... REFERENCES ...: 06편의 참조 무결성(외래키는 참조 대상 테이블에 실제로 존재하는 기본키 값이거나 널이어야 한다)을 구현한다. ENROLL의 학번에 STUDENT에 없는 값을 넣으려 하면 거부된다.PRIMARY KEY (학번, 과목코드)처럼 괄호 안에 컬럼 두 개를 나열하는 방식은 테이블 수준 제약(table-level constraint)이라 하며, 여러 컬럼을 묶어 하나의 복합키로 지정할 때 반드시 이 형태를 쓴다. 컬럼 옆에 바로 붙이는PRIMARY KEY(컬럼 수준 제약)는 단일 컬럼 키에만 쓸 수 있다.
결과 해석
CREATE TABLE이 실행되면 데이터는 아직 한 행도 없지만, DBMS 내부에는 이 스키마 정보(테이블 이름, 컬럼별 자료형과 제약)가 저장되어 이후의 모든 INSERT·UPDATE·DELETE가 이 규칙을 위반하지 않는지 자동으로 검사한다.
자주 틀리는 함정: 복합키를 PRIMARY KEY (학번, 과목코드)처럼 테이블 수준에서 한 번만 선언해야 하는데, 실수로 학번 VARCHAR2(10) PRIMARY KEY, 과목코드 VARCHAR2(10) PRIMARY KEY처럼 컬럼마다 각각 PRIMARY KEY를 붙이면 “테이블에 기본키가 두 개 정의됨” 오류가 발생한다. 06편에서 강조했듯 하나의 릴레이션에는 기본키가 오직 하나이며, 그 기본키가 여러 속성으로 이뤄질 수 있을 뿐이라는 점을 SQL 문법에서도 정확히 지켜야 한다.
2. DML: INSERT·UPDATE·DELETE로 데이터 조작하기
INSERT — 새 행 추가
INSERT INTO STUDENT (학번, 이름, 학과) VALUES ('S1', '김민준', '컴퓨터공학');
INSERT INTO STUDENT (학번, 이름, 학과) VALUES ('S2', '이서연', '컴퓨터공학');
INSERT INTO STUDENT (학번, 이름, 학과) VALUES ('S3', '박도윤', '전자공학');
INSERT INTO COURSE (과목코드, 과목명, 학점) VALUES ('C001', '데이터베이스', 3);
INSERT INTO COURSE (과목코드, 과목명, 학점) VALUES ('C002', '운영체제', 3);
INSERT INTO ENROLL (학번, 과목코드, 성적) VALUES ('S1', 'C001', 'A');
INSERT INTO ENROLL (학번, 과목코드, 성적) VALUES ('S1', 'C002', 'B');
INSERT INTO ENROLL (학번, 과목코드, 성적) VALUES ('S2', 'C001', 'A');이 상태에서 STUDENT에 없는 학번(예: 'S9')으로 ENROLL에 삽입을 시도하면 참조 무결성 위반으로 오류가 발생한다.
INSERT INTO ENROLL (학번, 과목코드, 성적) VALUES ('S9', 'C001', 'A');
-- ORA-02291: 무결성 제약조건이 위배되었습니다 (부모 키가 없습니다)UPDATE — 기존 행 수정
UPDATE ENROLL
SET 성적 = 'A+'
WHERE 학번 = 'S1' AND 과목코드 = 'C001';실행 후 ENROLL 테이블 상태는 다음과 같다.
| 학번 | 과목코드 | 성적 |
|---|---|---|
| S1 | C001 | A+ |
| S1 | C002 | B |
| S2 | C001 | A |
자주 틀리는 함정: UPDATE 문에서 WHERE 절을 빠뜨리면 테이블의 모든 행이 수정된다. 위 예제에서 WHERE 절 없이 UPDATE ENROLL SET 성적 = 'A+'를 실행하면 ENROLL의 세 행 모두 성적이 'A+'로 바뀌어 버린다. DELETE도 마찬가지로 WHERE 절이 없으면 테이블의 모든 행이 삭제되므로, 실행 전 WHERE 절의 조건 범위를 반드시 확인해야 한다.
DELETE — 행 삭제
DELETE FROM ENROLL WHERE 학번 = 'S2' AND 과목코드 = 'C001';이 문장을 실행하면 ENROLL에서 S2의 C001 수강 기록 한 행만 삭제된다. 만약 STUDENT에서 DELETE FROM STUDENT WHERE 학번 = 'S1'을 시도하면, ENROLL에 S1을 참조하는 행(학번 S1, 과목코드 C001·C002)이 남아 있는 한 참조 무결성 위반으로 삭제가 거부된다.
DELETE FROM STUDENT WHERE 학번 = 'S1';
-- ORA-02292: 무결성 제약조건이 위배되었습니다 (자식 레코드가 있습니다)결과 해석
이 오류는 06편에서 다룬 참조 무결성이 삭제 시점에도 그대로 적용된다는 것을 보여준다. 부모 테이블(STUDENT)의 행을 먼저 지우면 자식 테이블(ENROLL)에 남은 외래키가 존재하지 않는 값을 참조하게 되므로, DBMS는 이를 막아 데이터의 일관성을 지킨다. 이런 상황을 미리 처리하려면 FOREIGN KEY 정의 시 ON DELETE CASCADE(부모 삭제 시 자식도 함께 삭제)나 ON DELETE SET NULL(부모 삭제 시 자식의 외래키를 NULL로 변경) 같은 옵션을 추가로 지정할 수 있으며, 이는 06편에서 언급한 “참조 무결성을 실행 시점에 어떻게 처리할지에 대한 정책”에 해당한다.
3. 기본 SELECT-FROM-WHERE
정의
가장 기본적인 조회 문장의 구조는 다음과 같다.
SELECT 컬럼목록
FROM 테이블명
WHERE 조건;07편에서 배운 관계대수와 대응시키면, SELECT 목록은 투영(), WHERE 조건은 선택()에 해당한다. 다만 07편에서 다뤘듯 관계대수의 투영은 자동으로 중복을 제거하지만 SQL의 SELECT는 그렇지 않다는 차이를 기억해야 한다.
계산·적용: 비교·논리 연산자
SELECT 이름, 학과
FROM STUDENT
WHERE 학과 = '컴퓨터공학';| 이름 | 학과 |
|---|---|
| 김민준 | 컴퓨터공학 |
| 이서연 | 컴퓨터공학 |
여러 조건은 AND·OR·NOT으로 결합한다.
SELECT 과목명, 학점
FROM COURSE
WHERE 학점 >= 3 AND 과목명 <> '운영체제';| 과목명 | 학점 |
|---|---|
| 데이터베이스 | 3 |
문자열 부분 일치는 LIKE와 와일드카드(%는 0개 이상의 임의 문자, _는 정확히 1개의 임의 문자)로 검색한다.
SELECT 이름 FROM STUDENT WHERE 이름 LIKE '김%';이 질의는 “김”으로 시작하는 모든 이름을 찾으며, 결과는 김민준 한 명이다.
계산·적용: 정렬(ORDER BY)
SELECT 과목명, 학점
FROM COURSE
ORDER BY 학점 DESC, 과목명 ASC;DESC(descending, 내림차순)와 ASC(ascending, 오름차순, 생략 시 기본값)를 컬럼마다 지정할 수 있다. 학점이 같은 행이 여러 개면 두 번째 정렬 기준(과목명)으로 다시 정렬한다.
| 과목명 | 학점 |
|---|---|
| 데이터베이스 | 3 |
| 운영체제 | 3 |
계산·적용: 기본 집계 함수
SELECT COUNT(*) AS 학생수, AVG(학점) AS 평균학점
FROM COURSE;COUNT(*): 조건을 만족하는 행의 개수를 센다(컬럼 값이 아니라 행 자체를 세므로NULL여부와 무관하다).AVG(학점): 학점 컬럼의 평균을 계산한다.
| 학생수 | 평균학점 |
|---|---|
| 2 | 3 |
자주 틀리는 함정: COUNT(*)와 COUNT(컬럼명)을 같은 것으로 오해하기 쉽다. COUNT(*)는 행의 개수를 그대로 세지만, COUNT(컬럼명)은 그 컬럼 값이 NULL이 아닌 행만 센다. 예를 들어 ENROLL의 성적 컬럼에 아직 채점되지 않아 NULL인 행이 있다면, COUNT(*)와 COUNT(성적)의 결과가 서로 달라진다. 14편에서 GROUP BY와 함께 집계 함수를 다시 다룰 때 이 차이가 중요한 함정으로 재등장한다.
핵심 정리
- SQL은 DDL(구조 정의:
CREATE·ALTER·DROP), DML(데이터 조작:INSERT·UPDATE·DELETE·SELECT), DCL(권한 제어:GRANT·REVOKE)로 나뉜다. CREATE TABLE의PRIMARY KEY·NOT NULL·CHECK·FOREIGN KEY ... REFERENCES는 각각 06편의 엔티티 무결성·(도메인 무결성 일부)·도메인 무결성·참조 무결성을 구현하는 SQL 제약이다.- 복합키는 반드시 테이블 수준 제약
PRIMARY KEY (컬럼1, 컬럼2)로 선언해야 하며, 컬럼마다 각각PRIMARY KEY를 붙이면 오류가 난다. UPDATE·DELETE는WHERE절을 빠뜨리면 테이블의 모든 행에 적용되며, 참조 무결성 때문에 자식 테이블이 참조 중인 부모 행은 기본적으로 삭제가 거부된다.SELECT(투영)·WHERE(선택)는 관계대수와 대응하지만 SQL은 자동으로 중복을 제거하지 않으며,COUNT(*)는 행 개수를,COUNT(컬럼명)은 NULL이 아닌 값의 개수를 센다.