SQL을 처음 접하는 사람을 위한 도입입니다. 구문을 외우기 전에 "데이터베이스가 뭔가, 테이블이 뭔가, SQL이 뭔가" 세 가지만 이해하고 넘어가면 됩니다.
데이터베이스와 테이블
같은 개념, 다른 이름 (정처기 빈출 용어)
| 가로 줄 | 세로 줄 |
|---|---|
| 행(Row) = 튜플(Tuple) = 레코드(Record) | 열(Column) = 컬럼 = 속성(Attribute) = 필드(Field) |
정처기 기출에는 보통 '튜플'과 '속성'이 등장합니다. 튜플 = 행, 속성 = 열이라는 1:1 대응만 외워두면 됩니다.
SQL은 테이블에 말을 거는 언어
SQL(Structured Query Language, 구조화된 질의 언어)은 테이블에 데이터를 넣고, 꺼내고, 지우는 명령을 DBMS에 전달하는 언어입니다.
SELECT 이름 FROM 학생;
SELECT -> "가져와라"이름 -> "이름 컬럼을"FROM 학생 -> "학생 테이블에서"; -> 명령 끝즉 "학생 테이블에서 이름 컬럼을 가져와"라는 의미입니다. 무엇을 원하는지만 말하면 어떻게 찾을지는 DBMS가 처리합니다.
SQL 키워드는 대소문자를 구분하지 않습니다. SELECT·select·Select 모두 같은 명령이고 시험에서도 정답 인정됩니다. 단, 작은따옴표 안의 값(예: '김감자')은 대소문자를 구분하므로 'ABC'와 'abc'는 다른 값입니다.
SQL 3대 분류
| 분류 | 이름 | 주요 명령어 | 설명 |
|---|---|---|---|
| DDL | 데이터 정의어 | CREATE, ALTER, DROP, TRUNCATE | 테이블 구조를 정의/수정/삭제하거나 데이터를 전체 비움 |
| DML | 데이터 조작어 | INSERT, UPDATE, DELETE, SELECT | 데이터를 조회/추가/수정/삭제 |
| DCL | 데이터 제어어 | GRANT, REVOKE | 사용자 권한을 부여/회수 |
-- DDL: 학생 테이블을 새로 만들어라 CREATE TABLE 학생 (학번 INT, 이름 VARCHAR(50)); -- DML: 학생 테이블에 행을 하나 넣어라 INSERT INTO 학생 VALUES (20250101, '김감자'); -- DCL: 영희에게 학생 테이블 조회 권한을 줘라 GRANT SELECT ON 학생 TO 영희;
SQL 작성 순서 vs 실행 순서 (핵심)
SQL문은 작성 순서와 실행 순서가 다릅니다. 실행 결과를 구하는 문제는 이 실행 순서대로 단계별로 풀어야 합니다.
작성 순서: SELECT -> FROM -> WHERE -> GROUP BY -> HAVING -> ORDER BY
실행 순서: FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY
SELECT가 작성 시엔 맨 앞이지만 실행 시엔 맨 뒤로 이동합니다. 이를 모른 채 SELECT부터 따라가면 결과가 엉뚱해집니다.
참고: 정처기 실기 한 회당 SQL은 보통 1~3문제 출제됩니다. 유형은 (1) 키워드 하나를 빈칸으로 두는 빈칸 채우기, (2) 테이블과 쿼리를 주고 실행 결과를 구하는 문제 두 가지입니다.
DDL(Data Definition Language)은 테이블·뷰 등 데이터베이스 구조를 만들고 바꾸고 없애는 명령어입니다. DDL·DCL은 SQL 작성보다 빈칸 채우기로 출제되는 경우가 많습니다.
CREATE TABLE - 테이블 생성
CREATE TABLE 테이블명 ( 컬럼명1 데이터타입 [제약조건], 컬럼명2 데이터타입 [제약조건], ... );
[ ]로 감싼 항목은 생략 가능한 선택 요소입니다.
자주 나오는 제약조건
| 제약조건 | 설명 |
|---|---|
| PRIMARY KEY | 기본키. NULL 불가, 중복 불가, 테이블당 1개 |
| FOREIGN KEY | 외래키. 다른 테이블의 기본키를 참조 |
| NOT NULL | NULL 값 입력 불가 |
| UNIQUE | 중복 값 입력 불가 (NULL은 허용, 테이블에 여러 개 가능) |
| CHECK | 입력 값의 조건 제한 |
| DEFAULT | 값 미입력 시 자동으로 채울 기본값 설정 |
CREATE TABLE 학생 ( 학번 INT PRIMARY KEY, -- INT: 정수형 숫자 이름 VARCHAR(50) NOT NULL, -- VARCHAR(50): 최대 50자 문자열 학년 INT CHECK(학년 BETWEEN 1 AND 4), -- 범위 조건 (1 이상 4 이하) 과목 VARCHAR(20), 이메일 VARCHAR(50) UNIQUE );
제약조건에 이름 붙이기 (named constraint): 컬럼을 다 정의한 뒤 줄을 따로 만들어 CONSTRAINT 제약조건명으로 이름을 줄 수 있습니다. 이름을 붙이면 나중에 특정 제약조건만 골라 삭제·관리하기 좋습니다.
CREATE TABLE 회원 ( 회원ID CHAR(8), 이메일 VARCHAR(100), 등급 VARCHAR(10), CONSTRAINT PK_회원 PRIMARY KEY (회원ID), CONSTRAINT UQ_이메일 UNIQUE (이메일), CONSTRAINT CHK_등급 CHECK (등급 IN ('일반', 'VIP', 'VVIP')) );
외래키도 같은 방식으로 CONSTRAINT 이름 FOREIGN KEY (컬럼) REFERENCES 부모테이블 (컬럼)처럼 이름을 붙입니다.
ALTER TABLE - 테이블 수정
ADD: 새 컬럼 추가MODIFY: 기존 컬럼의 데이터타입·제약조건 변경DROP COLUMN: 컬럼 삭제-- 컬럼 추가 ALTER TABLE 학생 ADD 전화번호 VARCHAR(20); -- 컬럼 수정 (이름 컬럼 최대 길이 100자로 변경) ALTER TABLE 학생 MODIFY 이름 VARCHAR(100); -- 컬럼 삭제 ALTER TABLE 학생 DROP COLUMN 과목;
참고: DROP COLUMN의 COLUMN은 SQL 표준·PostgreSQL·MySQL에서는 생략 가능(ALTER TABLE 학생 DROP 과목;도 동작)하지만, SQL Server 등 일부 DBMS에서는 필수입니다.
DROP - 테이블/뷰 삭제
테이블·뷰를 구조째 삭제합니다(저장된 데이터도 함께 사라짐).
DROP TABLE 테이블명 [CASCADE | RESTRICT];
DROP VIEW 뷰이름 [CASCADE | RESTRICT];
| 옵션 | 설명 |
|---|---|
| CASCADE | 해당 테이블·뷰를 참조하는 다른 테이블·뷰도 함께 삭제 |
| RESTRICT | 다른 테이블·뷰가 참조 중이면 삭제 거부 (옵션 생략 시 기본 적용) |
-- CASCADE: 회사 테이블 삭제 시 이를 참조하는 직원 테이블도 함께 삭제됨 DROP TABLE 회사 CASCADE; -- RESTRICT: 직원 테이블이 회사를 참조 중이므로 삭제 거부(오류) DROP TABLE 회사 RESTRICT;
함정: 문제에 "참조하는 데이터도 함께 제거" 같은 표현이 나오면 CASCADE입니다.
TRUNCATE - 데이터 전체 삭제
테이블 구조는 유지하고 모든 데이터(행)만 삭제합니다.
TRUNCATE TABLE 학생;
DROP vs TRUNCATE vs DELETE: DROP은 테이블 구조째 삭제, TRUNCATE는 데이터 전체 삭제(구조 유지), DELETE는 WHERE 조건에 맞는 행만 삭제합니다.
CREATE INDEX / DROP INDEX - 인덱스
인덱스는 책 뒤의 '찾아보기'와 같아 조회(SELECT) 속도를 높입니다. 인덱스를 추가·삭제해도 테이블의 실제 데이터는 바뀌지 않습니다.
CREATE [UNIQUE] INDEX 인덱스명 ON 테이블명 (컬럼명1, 컬럼명2, ...);
-- 컬럼 하나로 인덱스 생성 CREATE INDEX 부서명인덱스 ON 회사 (부서명); -- 두 컬럼을 묶은 복합 인덱스 CREATE INDEX 부서인덱스 ON 회사 (부서명, 부서위치); -- 중복을 허용하지 않는 UNIQUE 인덱스 CREATE UNIQUE INDEX 이메일인덱스 ON 회원 (이메일); -- 인덱스 삭제 (테이블명 없이 인덱스 이름만) DROP INDEX 부서인덱스;
CREATE VIEW - 뷰 생성
뷰(View)는 하나의 SELECT 문에 이름을 붙여 저장한 가상 테이블입니다. 데이터를 복사하는 것이 아니라, 조회할 때마다 저장된 SELECT가 다시 실행되어 기반 테이블의 최신 데이터를 보여줍니다.
CREATE VIEW 고액사원 AS
SELECT 사번, 이름, 급여
FROM 사원
WHERE 급여 >= 3000;
이후 SELECT * FROM 고액사원;처럼 일반 테이블처럼 조회합니다. 삭제는 DROP VIEW 뷰이름;.
WITH CHECK OPTION: 뷰 정의 끝에 붙이면, 뷰를 통한 INSERT·UPDATE가 뷰의 조건을 만족할 때만 허용되고 조건을 어기는 입력은 거부됩니다.
CREATE VIEW 고액사원 AS
SELECT 사번, 이름, 급여
FROM 사원
WHERE 급여 >= 3000
WITH CHECK OPTION;
-- 급여 2000은 조건(급여 >= 3000)에 어긋나므로 거부됨
INSERT INTO 고액사원 VALUES (4, '당근', 2000);
DML은 테이블에 저장된 데이터를 추가·수정·삭제·조회합니다. INSERT/UPDATE/DELETE는 빈칸 채우기로 자주 나오므로 기본 구문을 정확히 외워두세요. (SELECT는 분량이 많아 별도 토글에서 다룹니다.)
| 명령어 | 기본 구문 | 설명 |
|---|---|---|
| INSERT | INSERT INTO 테이블 VALUES (...) | 데이터 추가 |
| UPDATE | UPDATE 테이블 SET 컬럼=값 WHERE 조건 | 데이터 수정 |
| DELETE | DELETE FROM 테이블 WHERE 조건 | 데이터 삭제 |
| SELECT | SELECT 컬럼 FROM 테이블 WHERE 조건 | 데이터 조회 |
따옴표 규칙: 키워드·테이블명·컬럼명은 대소문자 구분 안 함, 따옴표도 안 씀. 작은따옴표(')는 문자열 값에만 사용(대소문자 구분). 숫자는 따옴표 없이 씁니다.
INSERT - 데이터 추가
INSERT INTO 학생 (학번, 이름, 학년, 과목) VALUES (20250101, '김민지', 1, '프로그래밍');
다른 테이블에서 복사 (INSERT ... SELECT)
INSERT INTO 전체사원 (사원번호, 이름, 나이, 부서) SELECT 사원번호, 이름, 나이, 부서 FROM 개발부사원;
직접 값을 넣을 때는 VALUES, 다른 테이블에서 가져올 때는 SELECT.
UPDATE - 데이터 수정
UPDATE 부서
SET 주소 = '서울'
WHERE 부서 = '개발부';
UPDATE + 서브쿼리: WHERE 절에 다른 SELECT 결과를 넣어 다른 테이블 조건 기준으로 수정할 수 있습니다.
-- 서울 지점 부서에 속한 사원만 급여 10% 인상
UPDATE 사원
SET 급여 = 급여 * 1.1
WHERE 부서번호 IN (SELECT 부서번호 FROM 부서 WHERE 지점 = '서울');
단계: (1) 서브쿼리가 먼저 실행돼 (10, 20) 반환 -> (2) WHERE 부서번호 IN (10, 20)으로 치환 -> (3) 대상 행 급여에 1.1배. 부서번호 30(부산) 사원은 변경되지 않습니다.
DELETE - 데이터 삭제
DELETE FROM 학생
WHERE 이름 = '현진';
함정: DELETE 테이블명이 아니라 반드시 DELETE FROM 테이블명 순서입니다. FROM을 빈칸으로 출제한 적이 있습니다.
ON DELETE CASCADE
외래키로 연결된 경우, 참조 무결성을 지키기 위해 ON DELETE CASCADE를 지정하면 부모 테이블 행이 삭제될 때 이를 참조하는 자식 테이블 행도 자동으로 함께 삭제됩니다.
CREATE TABLE 성적 (
과목코드 INT,
학번 INT,
이름 VARCHAR(50),
FOREIGN KEY (과목코드) REFERENCES 과목(과목코드)
ON DELETE CASCADE
);
-- 과목코드 200을 삭제하면, 성적 테이블에서 과목코드 200인 행도 자동 삭제
DELETE FROM 과목 WHERE 과목코드 = 200;
SELECT는 정보처리기사 실기에서 가장 많이 출제되는 SQL 명령어이고, 실행 결과를 직접 구하는 문제가 자주 나옵니다. 각 절의 실행 순서를 반드시 이해해야 합니다.
| 절 | 역할 | 주요 키워드 |
|---|---|---|
| SELECT | 조회할 컬럼 지정 | DISTINCT, 집계함수, AS(별칭) |
| FROM | 조회할 테이블 지정 | - |
| WHERE | 행 필터링 (조건) | =, IN, LIKE, BETWEEN, AND, OR |
| GROUP BY | 그룹화 | - |
| HAVING | 그룹 조건 필터링 | 집계함수 조건 |
| ORDER BY | 정렬 | ASC(오름차순), DESC(내림차순) |
-- 실행 순서: FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY
SELECT 컬럼1, 컬럼2 FROM 테이블명; -- 기본 구문
SELECT * FROM 회사; -- 모든 컬럼 (*: 와일드카드)
WHERE - 비교 연산자
| 연산자 | 의미 | 예시 |
|---|---|---|
= | 같다 | WHERE DEPT = '개발' |
!= 또는 <> | 같지 않다 | WHERE DEPT != '영업' |
> , < | 크다, 작다 | WHERE SAL > 3000 |
>= , <= | 크거나 같다, 작거나 같다 | WHERE SAL >= 3000 |
IN / LIKE / BETWEEN
-- IN: 여러 값 중 하나와 일치 (= 값1 OR = 값2 OR ...)와 동일
WHERE C1 IN (2, 3);
-- BETWEEN: 범위 (양 끝값 포함, 값1 이상 값2 이하)
WHERE SAL BETWEEN 2000 AND 4000;
-- 위와 동일한 조건
WHERE SAL >= 2000 AND SAL <= 4000;
-- LIKE: 패턴 매칭
WHERE 컬럼명 LIKE '패턴';
| 와일드카드 | 의미 | 예시 |
|---|---|---|
| % | 0개 이상의 임의 문자 | '개%' -> 개, 개발, 개발팀 모두 일치 (0개도 포함) |
| _ | 정확히 1개의 임의 문자 | '_감자' -> 아무 글자 1개 + 감자 (총 3글자) |
| 패턴 | 의미 | 일치하는 값 |
|---|---|---|
| 'S%' | S로 시작 | SMITH, SCOTT |
| '%T%' | T를 포함 | SMITH, SCOTT |
| '_미%' | 두번째 글자가 미 | 김미정, 이미란 |
AND / OR 우선순위
AND는 OR보다 우선순위가 높습니다 (곱셈이 덧셈보다 먼저 계산되듯).
WHERE EMPNO > 100 AND SAL > 3000 OR EMPNO = 200
-- 위는 아래와 같이 해석됨
WHERE (EMPNO > 100 AND SAL > 3000) OR (EMPNO = 200)
함정: SAL > 3000은 3000 초과이므로 SAL=3000은 FALSE(3000은 3000보다 크지 않음). 단, EMPNO = 200이 TRUE이면 OR로 최종 TRUE.
ORDER BY - 정렬
ASC는 오름차순(기본값, 생략 가능), DESC는 내림차순. 여러 컬럼은 쉼표로 구분하며, 앞 컬럼이 먼저 정렬되고 값이 같은 행끼리 다음 컬럼으로 정렬합니다.
-- 부서위치 내림차순, 같으면 부서명 내림차순
SELECT * FROM 회사
ORDER BY 부서위치 DESC, 부서명 DESC;
DISTINCT - 중복 제거
-- GAMJA 테이블: 알감자 10, 왕감자 30, 돼지감자 50
SELECT KIND FROM GAMJA; -- 90개 (전체 행)
SELECT DISTINCT KIND FROM GAMJA; -- 3개 (종류만)
SELECT COUNT(DISTINCT KIND) FROM GAMJA WHERE KIND = '왕감자'; -- 1
집계 함수
| 함수 | 설명 |
|---|---|
| COUNT(*) | 모든 행의 개수 (NULL 포함) |
| COUNT(컬럼명) | NULL이 아닌 값의 개수 |
| COUNT(DISTINCT 컬럼명) | 중복을 제거한 값의 개수 (NULL 제외) |
| SUM(컬럼명) | 합계 (NULL 제외) |
| AVG(컬럼명) | 평균 (NULL 제외) |
| MIN(컬럼명) | 최솟값 (NULL 제외) |
| MAX(컬럼명) | 최댓값 (NULL 제외) |
실행 결과 예제 - 테이블 A (C1, C2): (2, NULL), (3, 6), (2, 3), (NULL, 5), (4, 5)
SELECT COUNT(*) FROM A; -- 5 (전체 행 수)
SELECT COUNT(C1) FROM A; -- 4 (C1이 NULL이 아닌 행 수)
SELECT COUNT(C2) FROM A; -- 4 (C2가 NULL이 아닌 행 수)
SELECT COUNT(DISTINCT C1) FROM A; -- 3 (고유값 2, 3, 4)
SELECT COUNT(DISTINCT C2) FROM A; -- 3 (고유값 6, 3, 5)
SELECT SUM(C1) FROM A; -- 11 (2 + 3 + 2 + 4)
SELECT AVG(C1) FROM A; -- 2.75 (11 / 4, 분모가 5가 아니라 4)
함정: AVG는 NULL이 아닌 값끼리만 평균을 냅니다. 위에서 C1은 5행 중 4행만 값이 있어 분모가 4입니다. COUNT(*)만 NULL을 포함합니다.
GROUP BY + HAVING
GROUP BY는 지정 컬럼 값이 같은 행끼리 묶어 그룹별 집계, HAVING은 그 그룹에 조건을 겁니다.
-- 부서명별 부서인원 합계가 10명 이상인 부서 조회
SELECT 부서명, SUM(부서인원) AS 부서인원합계
FROM 회사
GROUP BY 부서명
HAVING SUM(부서인원) >= 10;
단계별 - 원본(부서명, 부서인원): 개발 5 / 영업 8 / 개발 7 / 총무 3 / 영업 4
| 1단계: GROUP BY 후 집계 | 2단계: HAVING >= 10 필터 후 최종 |
|---|---|
| 개발 12, 영업 12, 총무 3 | 개발 12, 영업 12 |
WHERE vs HAVING: WHERE는 그룹화 전 개별 행 필터, HAVING은 그룹화 후 그룹 필터(주로 집계함수와 함께).
별칭 (AS)
조회 결과 컬럼명에 임시 이름을 지정합니다. AS는 생략 가능합니다.
SELECT 과목, MIN(점수) AS 최소점수 FROM 성적; -- AS 사용
SELECT 과목, MIN(점수) 최소점수 FROM 성적; -- AS 생략
-- 오류! WHERE 절에서는 별칭 사용 불가 (SELECT보다 먼저 실행됨)
SELECT SAL AS 급여 FROM 회사 WHERE 급여 > 3000;
함정: 별칭은 출력 화면에만 적용되며 같은 쿼리의 WHERE/HAVING에서는 못 씁니다(SELECT가 나중에 실행되기 때문). 또 AS 별칭에는 따옴표를 쓰지 않는 것이 안전합니다(작은따옴표는 문자열 값 전용).
UNION - 집합 연산
UNION은 두 쿼리의 결과를 위아래(행 방향)로 합칩니다. (JOIN이 좌우로 합치는 것과 대비)
| 연산 | 집합 개념 | 설명 |
|---|---|---|
| UNION | 합집합 | 두 결과 합치기 (중복 제거) |
| UNION ALL | 합집합 | 두 결과 합치기 (중복 포함) |
| INTERSECT | 교집합 | 두 결과 모두에 있는 행만 |
| EXCEPT | 차집합 | 첫 번째 결과에서 두 번째 결과를 뺀 행 |
SELECT C1 FROM T1 UNION SELECT C1 FROM T2 ORDER BY C1 DESC; -- T1: (1,2,3,4) T2: (3,4,5,6) SELECT C1 FROM T1 INTERSECT SELECT C1 FROM T2; -- 3, 4 SELECT C1 FROM T1 EXCEPT SELECT C1 FROM T2; -- 1, 2
JOIN은 두 개 이상의 테이블을 공통 컬럼을 기준으로 결합하는 연산입니다. 실행 결과를 구하는 문제로 출제됩니다.
| JOIN 종류 | 설명 |
|---|---|
| INNER JOIN (동등 조인) | 두 테이블에서 조건이 일치하는 행만 반환 |
| CROSS JOIN | 두 테이블의 모든 조합 반환 (카티션 곱) |
| LEFT OUTER JOIN | 왼쪽 테이블의 모든 행 + 오른쪽에서 일치하는 행 |
| RIGHT OUTER JOIN | 오른쪽 테이블의 모든 행 + 왼쪽에서 일치하는 행 |
| FULL OUTER JOIN | 양쪽 테이블의 모든 행 반환 |
| NATURAL JOIN (자연 조인) | 동등 조인에서 중복 컬럼 제거 |
INNER JOIN - 내부 조인
조건이 일치하는 행만 결합합니다(실기 최빈출). 테이블명.컬럼명 점 표기법으로 어느 테이블 컬럼인지 구분합니다. JOIN과 INNER JOIN은 같은 의미입니다.
-- JOIN ... ON 방식
SELECT 이름, 급여테이블.급여액
FROM 사원 JOIN 급여테이블
ON 사원.사원번호 = 급여테이블.사원번호
WHERE 급여테이블.급여액 >= 500;
-- WHERE 방식 (같은 결과 = 동등 조인)
SELECT 이름, 급여테이블.급여액
FROM 사원, 급여테이블
WHERE 사원.사원번호 = 급여테이블.사원번호 AND 급여테이블.급여액 >= 500;
CROSS JOIN - 교차 조인
두 테이블의 모든 조합을 생성합니다. A가 3행, B가 2행이면 3 x 2 = 6행.
SELECT * FROM A CROSS JOIN B;
A(NAME): SMITH, ALLEN, SCOTT / B(RULE): S%, %T% -> 결과 6행. 여기에 WHERE A.NAME LIKE B.RULE을 추가하면 조건을 만족하는 4행(SMITH/S%, SMITH/%T%, SCOTT/S%, SCOTT/%T%)만 남습니다.
OUTER JOIN - 외부 조인
한쪽 테이블의 모든 행을 포함하고, 반대쪽에 일치가 없으면 NULL로 채웁니다.
-- LEFT: 왼쪽(사원) 전체 유지, 오른쪽에 없으면 NULL
SELECT * FROM 사원 LEFT OUTER JOIN 부서
ON 사원.사원번호 = 부서.사원번호;
-- RIGHT: 오른쪽 테이블 전체 유지
-- FULL: 양쪽 모두 유지, 없는 쪽은 NULL
예: 사원(1 김감자, 2 이고구마, 3 박옥수수) LEFT JOIN 부서(1 개발팀, 2 디자인팀) -> 박옥수수는 부서가 없어 부서명이 NULL로 채워집니다.
SELF JOIN - 자기 참조 조인
같은 테이블을 두 번 참조합니다. 반드시 별칭으로 구분해야 합니다(같은 테이블이라 컬럼 소속을 구분할 수 없기 때문).
-- 각 사원의 상사 이름을 함께 조회
SELECT e1.이름 AS 사원, e2.이름 AS 상사
FROM 사원 e1 JOIN 사원 e2
ON e1.상사번호 = e2.사원번호;
ON e1.상사번호 = e2.사원번호는 "본인(e1)의 상사번호"가 "다른 사원(e2)의 사원번호"와 같은 행끼리 결합합니다. 상사번호가 NULL인 사원은 INNER JOIN이라 제외됩니다(포함하려면 LEFT OUTER JOIN).
조인의 학술적 분류
| 조인 종류 | 설명 |
|---|---|
| 세타 조인 (Theta Join) | 모든 비교 연산자(=, >, <, >=, <=, !=)를 사용하는 조인. θ는 비교 연산자를 나타내는 기호 |
| 동등 조인 (Equi Join) | 세타 조인 중 등호(=)만 사용하는 조인 |
| 자연 조인 (Natural Join) | 동등 조인에서 중복 컬럼을 제거한 조인 |
세타 조인 ⊃ 동등 조인 ⊃ 자연 조인 순으로 범위가 좁아집니다.
-- 세타 조인: = 이외의 연산자도 사용
SELECT * FROM 사원, 부서 WHERE 사원.급여 > 부서.최저급여;
-- 동등 조인: = 만 사용
SELECT * FROM 사원, 부서 WHERE 사원.부서번호 = 부서.부서번호;
-- 자연 조인: 동등 조인 + 중복 컬럼 자동 제거
SELECT * FROM 사원 NATURAL JOIN 부서;
함정: 자연 조인 = 동등 조인 결과에서 중복 속성을 하나만 남긴 것. 세타/동등/자연 조인의 차이를 묻는 문제가 나옵니다.
다른 SQL문 안에 포함된 SELECT문입니다. 먼저 안쪽 서브쿼리가 실행되어 결과를 만들고, 바깥 쿼리가 그 결과를 사용합니다.
SELECT 이름 FROM 사원
WHERE 부서번호 = (SELECT 부서번호 FROM 부서 WHERE 부서명 = '개발팀');
WHERE ... IN (서브쿼리)
-- 서울에 위치한 부서에 근무하는 사원 조회
SELECT 이름 FROM 사원
WHERE 부서코드 IN (SELECT 부서코드 FROM 부서 WHERE 부서위치 = '서울');
중첩 서브쿼리 해석 순서
여러 겹이면 가장 안쪽부터 바깥쪽으로 해석합니다.
SELECT COUNT(*)
FROM 사원 e JOIN 부서 d ON e.부서번호 = d.부서번호
WHERE d.부서명 IN (
SELECT 부서명 FROM 부서 -- (2) 바깥쪽 서브쿼리
WHERE 부서번호 IN (
SELECT 부서번호 FROM 사원 -- (1) 가장 안쪽 서브쿼리
GROUP BY 부서번호
HAVING COUNT(*) < 2
)
); -- (3) 메인 쿼리
상관 서브쿼리 (Correlated Subquery)
바깥 쿼리의 값을 서브쿼리가 참조합니다. 바깥 쿼리의 각 행마다 서브쿼리가 다시 실행됩니다.
-- 각 부서의 평균 급여보다 많이 받는 사원 조회
SELECT 이름, 급여
FROM 사원 e1
WHERE 급여 > (SELECT AVG(급여)
FROM 사원 e2
WHERE e2.부서번호 = e1.부서번호);
함정: 일반(비상관) 서브쿼리는 한 번 실행 후 값이 치환되지만, 상관 서브쿼리는 바깥 행 개수만큼 반복 실행됩니다. 바깥 테이블 별칭을 서브쿼리 안에서 참조하면 상관 서브쿼리입니다.
인라인 뷰 (Inline View) - FROM 절 서브쿼리
FROM 절 안 서브쿼리를 임시 테이블처럼 사용합니다. 반드시 별칭(AS)이 필요합니다.
-- 부서별 평균 급여 중 3000 이상인 부서만 조회
SELECT 부서번호, 평균급여
FROM (SELECT 부서번호, AVG(급여) AS 평균급여
FROM 사원
GROUP BY 부서번호) AS 부서평균
WHERE 평균급여 >= 3000;
ALL / ANY 연산자
| 연산자 | 의미 |
|---|---|
| ALL | 서브쿼리의 모든 값과 비교 (모두 만족해야 참) |
| ANY (= SOME) | 서브쿼리의 하나라도 만족하면 참 |
-- A농장의 모든 감자보다 무거운 감자
SELECT 감자명, 무게, 농장 FROM 감자
WHERE 무게 > ALL (SELECT 무게 FROM 감자 WHERE 농장 = 'A');
-- A농장의 어떤 감자보다라도 무거운 감자
SELECT 감자명, 무게, 농장 FROM 감자
WHERE 무게 > ANY (SELECT 무게 FROM 감자 WHERE 농장 = 'A');
함정: A농장 무게가 (100, 150)일 때, > ALL은 가장 큰 값보다 커야 하므로 무게 > 150, > ANY는 가장 작은 값보다만 크면 되므로 무게 > 100.
DCL(Data Control Language)은 사용자 권한을 부여·회수합니다.
GRANT - 권한 부여
GRANT 권한 ON 테이블명 TO 사용자명;
| 권한 | 설명 |
|---|---|
| SELECT | 조회 권한 |
| INSERT | 삽입 권한 |
| UPDATE | 수정 권한 |
| DELETE | 삭제 권한 |
| ALL | 모든 권한 |
GRANT SELECT ON 학생 TO USER1; -- 조회 권한 GRANT ALL ON 학생 TO USER2; -- 모든 권한 GRANT SELECT, INSERT ON 학생 TO USER3; -- 여러 권한 동시 부여
WITH GRANT OPTION: 권한을 받은 사용자가 다른 사용자에게도 같은 권한을 재부여할 수 있게 합니다.
GRANT SELECT ON 학생 TO USER1 WITH GRANT OPTION; -- 이제 USER1이 USER2에게 SELECT 권한을 다시 부여할 수 있음
REVOKE - 권한 회수
REVOKE 권한 ON 테이블명 FROM 사용자명;
REVOKE SELECT ON 학생 FROM USER1; REVOKE ALL ON 학생 FROM USER2; REVOKE SELECT, INSERT ON 학생 FROM USER3;
함정: 권한을 줄 때는 TO(~에게), 뺏을 때는 FROM(~으로부터).
REVOKE ... CASCADE: WITH GRANT OPTION으로 권한이 퍼진 상태에서, 중간 사용자가 재부여한 권한도 함께 연쇄 회수합니다.
| 옵션 | 설명 |
|---|---|
| CASCADE | 해당 사용자가 재부여한 다른 사용자의 권한도 함께 연쇄 회수 |
| RESTRICT | 재부여한 사용자가 있으면 회수 거부 (옵션 생략 시 기본) |
-- USER1 + USER1이 재부여한 USER2까지 모두 회수 REVOKE SELECT ON 학생 FROM USER1 CASCADE;
REVOKE GRANT OPTION FOR: 본인 권한은 유지하고 재부여 권한만 회수합니다.
-- USER1의 SELECT 권한은 유지, 재부여 권한만 회수 REVOKE GRANT OPTION FOR SELECT ON 학생 FROM USER1;
참고: 참고 사이트는 DDL/DML/DCL만 다루지만 실기 빈출이라 표준 문법을 보충합니다.
TCL(Transaction Control Language)은 트랜잭션(하나의 논리적 작업 단위)의 확정·취소를 제어하는 명령어입니다.
| 명령어 | 설명 |
|---|---|
| COMMIT | 트랜잭션의 변경 내용을 데이터베이스에 영구 저장(확정) |
| ROLLBACK | 트랜잭션의 변경 내용을 취소하고 이전 상태로 복구 |
| SAVEPOINT | 트랜잭션 내 특정 지점을 저장해 두고, 그 지점까지만 부분 ROLLBACK 가능 |
UPDATE 계좌 SET 잔액 = 잔액 - 1000 WHERE 번호 = 1;
SAVEPOINT sp1; -- 여기까지를 저장점으로 지정
UPDATE 계좌 SET 잔액 = 잔액 + 1000 WHERE 번호 = 2;
ROLLBACK TO sp1; -- sp1 이후 변경만 취소 (첫 UPDATE는 유지)
COMMIT; -- 남은 변경을 영구 저장(확정)
함정: COMMIT 이후에는 ROLLBACK으로 되돌릴 수 없습니다. ROLLBACK TO 저장점은 해당 SAVEPOINT 이후의 변경만 취소하며, 그 이전 작업은 그대로 남습니다.
COUNT(컬럼명)·SUM·AVG·MIN·MAX는 NULL 제외. AVG의 분모도 NULL 아닌 행 수.>는 초과, >=는 이상: SAL=3000은 SAL > 3000에서 FALSE.DELETE FROM 테이블 (FROM 필수).> ALL = 최댓값보다 크게, > ANY = 최솟값보다 크게.