5편 - SQLD 2과목 1장 : SQL 기본
SQLD 과목2: SQL 기본 및 활용
제1장. SQL 기본
1.1 관계형 데이터베이스 개요
1.1.1 데이터베이스와 관계형 데이터베이스
관계형 데이터베이스(RDBMS)는 데이터를 2차원 테이블 구조로 저장하며, 테이블 간의 관계(Relationship)를 통해 데이터를 관리한다.
1.1.2 SQL (Structured Query Language)
SQL은 관계형 데이터베이스를 조작하기 위한 표준 언어이다.
| SQL 분류 | 설명 | 주요 명령어 |
|---|---|---|
| DDL (Data Definition Language) | 데이터 구조 정의 | CREATE, ALTER, DROP, RENAME, TRUNCATE |
| DML (Data Manipulation Language) | 데이터 조작 | SELECT, INSERT, UPDATE, DELETE |
| DCL (Data Control Language) | 권한 제어 | GRANT, REVOKE |
| TCL (Transaction Control Language) | 트랜잭션 제어 | COMMIT, ROLLBACK, SAVEPOINT |
시험 핵심: DDL은 AUTO COMMIT이 발생한다 (Oracle 기준). DML은 명시적 COMMIT이 필요하다.
1.1.3 테이블 용어 정리
| 용어 | 다른 표현 | 설명 |
|---|---|---|
| 테이블 (Table) | 릴레이션, 엔터티 | 데이터를 저장하는 2차원 구조 |
| 행 (Row) | 튜플, 레코드, 인스턴스 | 하나의 데이터 단위 |
| 열 (Column) | 필드, 속성, 칼럼 | 데이터의 항목 |
| 기본키 (PK) | Primary Key | 행을 유일하게 식별하는 칼럼 |
| 외래키 (FK) | Foreign Key | 다른 테이블의 PK를 참조하는 칼럼 |
1.2 DDL (Data Definition Language)
1.2.1 CREATE TABLE
CREATE TABLE 사원 ( 사원번호 NUMBER(10) NOT NULL, 사원명 VARCHAR2(50) NOT NULL, 입사일 DATE DEFAULT SYSDATE, 급여 NUMBER(10,2), 부서코드 VARCHAR2(10), CONSTRAINT PK_사원 PRIMARY KEY (사원번호), CONSTRAINT FK_사원_부서 FOREIGN KEY (부서코드) REFERENCES 부서(부서코드) );
주요 데이터 타입 (Oracle)
| 타입 | 설명 | 예시 |
|---|---|---|
| CHAR(n) | 고정 길이 문자열 (남는 공간은 공백 패딩) | CHAR(10) → 'ABC ' |
| VARCHAR2(n) | 가변 길이 문자열 | VARCHAR2(10) → 'ABC' |
| NUMBER(p,s) | 숫자 (전체자릿수, 소수점자릿수) | NUMBER(10,2) |
| DATE | 날짜와 시간 | SYSDATE |
| CLOB | 대용량 문자 데이터 | 4GB까지 |
시험 핵심: CHAR vs VARCHAR2
- CHAR: 고정 길이. 비교 시 공백을 채워서 비교한다.
- VARCHAR2: 가변 길이. 실제 저장된 값만으로 비교한다.
- CHAR('AA') = CHAR('AA ') → TRUE (공백 패딩 비교)
- VARCHAR2('AA') = VARCHAR2('AA ') → FALSE (공백도 문자로 인식)
1.2.2 제약조건 (Constraint)
| 제약조건 | 설명 |
|---|---|
| PRIMARY KEY | 유일성 + NOT NULL. 테이블당 1개만 가능 |
| UNIQUE | 유일성 보장. NULL 허용 (Oracle에서 NULL은 여러 개 가능) |
| NOT NULL | NULL 값 불허 |
| CHECK | 특정 조건을 만족하는 값만 허용 |
| FOREIGN KEY | 참조 무결성 보장 |
-- CHECK 제약조건 예시 CREATE TABLE 사원 ( 사원번호 NUMBER PRIMARY KEY, 급여 NUMBER CHECK (급여 > 0), 성별 CHAR(1) CHECK (성별 IN ('M', 'F')) );
FK 삭제 옵션
| 옵션 | 동작 |
|---|---|
| CASCADE | 부모 삭제 시 자식도 함께 삭제 |
| SET NULL | 부모 삭제 시 자식의 FK를 NULL로 설정 |
| SET DEFAULT | 부모 삭제 시 자식의 FK를 기본값으로 설정 |
| RESTRICT | 자식이 있으면 부모 삭제 불가 |
| NO ACTION | RESTRICT와 유사 (트랜잭션 종료 시 체크) |
1.2.3 ALTER TABLE
-- 칼럼 추가 ALTER TABLE 사원 ADD (이메일 VARCHAR2(100)); -- 칼럼 수정 ALTER TABLE 사원 MODIFY (사원명 VARCHAR2(100)); -- 칼럼 삭제 ALTER TABLE 사원 DROP COLUMN 이메일; -- 칼럼명 변경 ALTER TABLE 사원 RENAME COLUMN 급여 TO 월급; -- 제약조건 추가 ALTER TABLE 사원 ADD CONSTRAINT CK_사원_급여 CHECK (급여 >= 0); -- 제약조건 삭제 ALTER TABLE 사원 DROP CONSTRAINT CK_사원_급여;
1.2.4 DROP TABLE & TRUNCATE TABLE
-- 테이블 삭제 (구조 + 데이터 모두 삭제) DROP TABLE 사원; DROP TABLE 사원 CASCADE CONSTRAINTS; -- FK 제약조건도 함께 삭제 -- 테이블 데이터만 삭제 (구조는 유지, 복구 불가) TRUNCATE TABLE 사원;
DROP vs TRUNCATE vs DELETE 비교
| 구분 | DROP | TRUNCATE | DELETE |
|---|---|---|---|
| 분류 | DDL | DDL (일부 DML 특성) | DML |
| 테이블 구조 | 삭제됨 | 유지됨 | 유지됨 |
| 저장 공간 | 반환 | 반환 | 유지 (반환 안 됨) |
| ROLLBACK | 불가 | 불가 | 가능 (COMMIT 전) |
| 로그 | 기록 안 됨 | 기록 안 됨 | 기록됨 |
| WHERE 절 | 사용 불가 | 사용 불가 | 사용 가능 |
| AUTO COMMIT | O | O | X |
1.3 DML (Data Manipulation Language)
1.3.1 INSERT
-- 전체 칼럼 입력 INSERT INTO 사원 VALUES (1001, '홍길동', SYSDATE, 5000000, 'D01'); -- 특정 칼럼만 입력 INSERT INTO 사원 (사원번호, 사원명, 부서코드) VALUES (1002, '김철수', 'D02'); -- 다른 테이블로부터 입력 INSERT INTO 사원_백업 SELECT * FROM 사원 WHERE 부서코드 = 'D01';
1.3.2 UPDATE
UPDATE 사원 SET 급여 = 급여 * 1.1, 부서코드 = 'D03' WHERE 사원번호 = 1001;
주의: WHERE 절을 생략하면 테이블의 모든 행이 수정된다!
1.3.3 DELETE
DELETE FROM 사원 WHERE 사원번호 = 1001; -- 전체 삭제 (ROLLBACK 가능) DELETE FROM 사원;
1.3.4 MERGE (Oracle)
MERGE INTO 사원_대상 T USING 사원_원본 S ON (T.사원번호 = S.사원번호) WHEN MATCHED THEN UPDATE SET T.급여 = S.급여 WHEN NOT MATCHED THEN INSERT (사원번호, 사원명, 급여) VALUES (S.사원번호, S.사원명, S.급여);
1.4 SELECT 문
1.4.1 기본 SELECT
SELECT 사원번호, 사원명, 급여 FROM 사원 WHERE 부서코드 = 'D01' ORDER BY 급여 DESC;
SQL 실행 순서 (매우 중요!)
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
① ② ③ ④ ⑤ ⑥
시험 핵심: SELECT에서 정의한 별칭(Alias)은 ORDER BY에서만 사용 가능하다. WHERE, GROUP BY, HAVING에서는 사용 불가하다.
1.4.2 별칭 (Alias)
-- 칼럼 별칭 SELECT 사원명 AS 이름, 급여 * 12 AS 연봉 FROM 사원; -- 테이블 별칭 SELECT e.사원명, d.부서명 FROM 사원 e, 부서 d WHERE e.부서코드 = d.부서코드;
1.4.3 산술 연산자
-- NULL과의 연산 결과는 항상 NULL SELECT 사원명, 급여, 급여 + 보너스 AS 총급여 FROM 사원; -- 만약 보너스가 NULL이면 총급여도 NULL! -- NVL 함수로 해결 SELECT 사원명, 급여, 급여 + NVL(보너스, 0) AS 총급여 FROM 사원;
1.4.4 DISTINCT
-- 중복 제거 SELECT DISTINCT 부서코드 FROM 사원; -- 여러 칼럼에 DISTINCT → 칼럼 조합이 유일한 행만 반환 SELECT DISTINCT 부서코드, 직급 FROM 사원;
1.5 WHERE 절
1.5.1 비교 연산자
| 연산자 | 설명 | 예시 |
|---|---|---|
| = | 같다 | 급여 = 5000 |
| >, <, >=, <= | 크다/작다 | 급여 >= 3000 |
| <>, !=, ^= | 같지 않다 | 부서코드 <> 'D01' |
1.5.2 SQL 연산자
| 연산자 | 설명 | 예시 |
|---|---|---|
| BETWEEN a AND b | a 이상 b 이하 | 급여 BETWEEN 3000 AND 5000 |
| IN (list) | 목록 중 하나라도 일치 | 부서코드 IN ('D01', 'D02', 'D03') |
| LIKE | 패턴 매칭 | 사원명 LIKE '김%' |
| IS NULL | NULL 확인 | 보너스 IS NULL |
LIKE 와일드카드
| 와일드카드 | 설명 | 예시 |
|---|---|---|
| % | 0개 이상의 임의 문자 | '김%' → 김으로 시작하는 모든 값 |
| _ | 정확히 1개의 임의 문자 | '김__' → 김 + 정확히 2글자 |
-- ESCAPE 사용 (%, _ 를 실제 문자로 검색할 때) SELECT * FROM 상품 WHERE 상품명 LIKE '%20\%%' ESCAPE '\'; -- '20%'를 포함하는 상품명 검색
1.5.3 논리 연산자
| 우선순위 | 연산자 |
|---|---|
| 1 (높음) | NOT |
| 2 | AND |
| 3 (낮음) | OR |
-- AND가 OR보다 먼저 실행됨! SELECT * FROM 사원 WHERE 부서코드 = 'D01' OR 부서코드 = 'D02' AND 급여 >= 5000; -- 실제 실행: 부서코드='D01' OR (부서코드='D02' AND 급여>=5000) -- 의도한 대로 하려면 괄호 사용 SELECT * FROM 사원 WHERE (부서코드 = 'D01' OR 부서코드 = 'D02') AND 급여 >= 5000;
1.5.4 NULL의 특성
- NULL은 '알 수 없는 값(Unknown)'이다
- NULL과의 모든 비교 연산 결과는 Unknown(FALSE 처리)이다
- NULL과의 모든 산술 연산 결과는 NULL이다
- NULL = NULL → Unknown (FALSE)
- NULL <> NULL → Unknown (FALSE)
- NULL IS NULL → TRUE (IS NULL/IS NOT NULL로만 비교)
3치 논리 (Three-Valued Logic)
| AND | TRUE | FALSE | UNKNOWN |
|---|---|---|---|
| TRUE | TRUE | FALSE | UNKNOWN |
| FALSE | FALSE | FALSE | FALSE |
| UNKNOWN | UNKNOWN | FALSE | UNKNOWN |
| OR | TRUE | FALSE | UNKNOWN |
|---|---|---|---|
| TRUE | TRUE | TRUE | TRUE |
| FALSE | TRUE | FALSE | UNKNOWN |
| UNKNOWN | TRUE | UNKNOWN | UNKNOWN |
| NOT | 결과 |
|---|---|
| TRUE | FALSE |
| FALSE | TRUE |
| UNKNOWN | UNKNOWN |
시험 핵심: NOT IN 사용 시 서브쿼리 결과에 NULL이 포함되면 결과가 공집합이 된다!
-- 위험한 예시 SELECT * FROM 사원 WHERE 부서코드 NOT IN (SELECT 부서코드 FROM 부서 WHERE 부서코드 IS NOT NULL); -- 서브쿼리에 NULL이 있다면 결과 0건
1.6 함수 (Function)
1.6.1 문자열 함수
| 함수 | 설명 | Oracle 예시 | 결과 |
|---|---|---|---|
| UPPER(s) | 대문자 변환 | UPPER('hello') | 'HELLO' |
| LOWER(s) | 소문자 변환 | LOWER('HELLO') | 'hello' |
| INITCAP(s) | 첫 글자만 대문자 | INITCAP('hello world') | 'Hello World' |
| LENGTH(s) | 문자열 길이 | LENGTH('ABC') | 3 |
| SUBSTR(s,m,n) | 부분 문자열 | SUBSTR('ABCDEF',2,3) | 'BCD' |
| INSTR(s1,s2) | 문자열 위치 | INSTR('ABCABC','B') | 2 |
| LPAD(s,n,c) | 왼쪽 패딩 | LPAD('123',5,'0') | '00123' |
| RPAD(s,n,c) | 오른쪽 패딩 | RPAD('123',5,'0') | '12300' |
| LTRIM(s,c) | 왼쪽 제거 | LTRIM(' ABC') | 'ABC' |
| RTRIM(s,c) | 오른쪽 제거 | RTRIM('ABC ') | 'ABC' |
| TRIM(s) | 양쪽 공백 제거 | TRIM(' ABC ') | 'ABC' |
| REPLACE(s,a,b) | 문자 치환 | REPLACE('ABCABC','B','X') | 'AXCAXC' |
| CONCAT(s1,s2) | 문자 결합 | CONCAT('AB','CD') | 'ABCD' |
| || | 문자 결합 (Oracle) | 'AB' || 'CD' | 'ABCD' |
1.6.2 숫자 함수
| 함수 | 설명 | 예시 | 결과 |
|---|---|---|---|
| ROUND(n,m) | 반올림 | ROUND(15.567, 1) | 15.6 |
| TRUNC(n,m) | 버림 | TRUNC(15.567, 1) | 15.5 |
| CEIL(n) | 올림 (정수) | CEIL(15.1) | 16 |
| FLOOR(n) | 내림 (정수) | FLOOR(15.9) | 15 |
| MOD(n,m) | 나머지 | MOD(10, 3) | 1 |
| ABS(n) | 절대값 | ABS(-15) | 15 |
| SIGN(n) | 부호 (양수1, 0, 음수-1) | SIGN(-15) | -1 |
| POWER(n,m) | 거듭제곱 | POWER(2,3) | 8 |
1.6.3 날짜 함수 (Oracle)
| 함수 | 설명 | 예시 |
|---|---|---|
| SYSDATE | 현재 날짜/시간 | SYSDATE |
| ADD_MONTHS(d,n) | n개월 후 날짜 | ADD_MONTHS(SYSDATE, 3) |
| MONTHS_BETWEEN(d1,d2) | 두 날짜 간 개월 수 | MONTHS_BETWEEN('2024-06-01','2024-01-01') → 5 |
| LAST_DAY(d) | 해당 월의 마지막 날 | LAST_DAY('2024-02-01') → 2024-02-29 |
| NEXT_DAY(d, day) | 다음 특정 요일 | NEXT_DAY(SYSDATE, '월요일') |
| TRUNC(d, fmt) | 날짜 절삭 | TRUNC(SYSDATE, 'MM') → 해당 월 1일 |
| ROUND(d, fmt) | 날짜 반올림 | ROUND(SYSDATE, 'YYYY') |
-- 날짜 연산 SELECT SYSDATE + 7 FROM DUAL; -- 7일 후 SELECT SYSDATE - 7 FROM DUAL; -- 7일 전 SELECT SYSDATE + 1/24 FROM DUAL; -- 1시간 후 SELECT 날짜1 - 날짜2 FROM DUAL; -- 날짜 간 일수 차이 (숫자 반환) -- 날짜 + 숫자 = 날짜 -- 날짜 - 날짜 = 숫자 (일수) -- 날짜 + 날짜 = 에러!
1.6.4 변환 함수
(1) 명시적 형변환 vs 암시적 형변환
문자 ←→ 숫자: TO_CHAR / TO_NUMBER
문자 ←→ 날짜: TO_CHAR / TO_DATE
시험 핵심: 암시적 형변환은 성능 저하의 원인이 될 수 있다. 인덱스 칼럼에 암시적 형변환이 발생하면 인덱스를 사용하지 못할 수 있다.
-- TO_CHAR: 숫자/날짜 → 문자 SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') FROM DUAL; -- '2024-05-15 14:30:00' SELECT TO_CHAR(1234567, '9,999,999') FROM DUAL; -- '1,234,567' -- TO_DATE: 문자 → 날짜 SELECT TO_DATE('2024-05-15', 'YYYY-MM-DD') FROM DUAL; -- TO_NUMBER: 문자 → 숫자 SELECT TO_NUMBER('1,234', '9,999') FROM DUAL; -- 1234
날짜 포맷 모델
| 포맷 | 설명 | 예시 |
|---|---|---|
| YYYY | 4자리 연도 | 2024 |
| MM | 2자리 월 | 05 |
| DD | 2자리 일 | 15 |
| HH24 | 24시간 형식 시 | 14 |
| MI | 분 | 30 |
| SS | 초 | 00 |
| DAY | 요일 (전체) | 수요일 |
| DY | 요일 (약어) | 수 |
1.6.5 NULL 관련 함수
| 함수 | 설명 | 예시 | 결과 |
|---|---|---|---|
| NVL(a, b) | a가 NULL이면 b 반환 | NVL(보너스, 0) | 보너스가 NULL이면 0 |
| NVL2(a, b, c) | a가 NULL이 아니면 b, NULL이면 c | NVL2(보너스, '있음', '없음') | |
| NULLIF(a, b) | a=b이면 NULL, 아니면 a 반환 | NULLIF(10, 10) | NULL |
| COALESCE(a,b,c..) | 인자 중 첫 번째 NULL이 아닌 값 | COALESCE(NULL, NULL, 3) | 3 |
-- COALESCE는 NVL의 확장판 SELECT COALESCE(보너스, 성과급, 수당, 0) AS 추가급여 FROM 사원; -- 보너스 → 성과급 → 수당 순서로 NULL이 아닌 첫 번째 값 반환
1.6.6 CASE 표현식
-- Simple CASE SELECT 사원명, CASE 직급 WHEN '부장' THEN 급여 * 1.3 WHEN '과장' THEN 급여 * 1.2 WHEN '대리' THEN 급여 * 1.1 ELSE 급여 END AS 인상급여 FROM 사원; -- Searched CASE (더 유연) SELECT 사원명, CASE WHEN 급여 >= 8000 THEN '고액' WHEN 급여 >= 5000 THEN '중간' WHEN 급여 >= 3000 THEN '보통' ELSE '저액' END AS 급여등급 FROM 사원;
DECODE 함수 (Oracle 전용)
-- CASE의 간편 버전 (등호 비교만 가능) SELECT 사원명, DECODE(직급, '부장', 급여*1.3, '과장', 급여*1.2, '대리', 급여*1.1, 급여) AS 인상급여 FROM 사원;
1.7 ORDER BY 절
1.7.1 정렬 기본
-- 오름차순 (기본값) SELECT * FROM 사원 ORDER BY 급여 ASC; -- 내림차순 SELECT * FROM 사원 ORDER BY 급여 DESC; -- 다중 정렬 SELECT * FROM 사원 ORDER BY 부서코드 ASC, 급여 DESC; -- 칼럼 번호로 정렬 SELECT 사원명, 급여, 부서코드 FROM 사원 ORDER BY 3, 2 DESC; -- 3번째 칼럼(부서코드) 오름차순, 2번째 칼럼(급여) 내림차순
1.7.2 NULL과 정렬
Oracle에서 NULL은 가장 큰 값으로 취급된다.
-- Oracle: NULL이 마지막에 위치 (ASC) SELECT * FROM 사원 ORDER BY 보너스 ASC; -- 100, 200, 300, NULL, NULL -- NULL을 제어하려면 SELECT * FROM 사원 ORDER BY 보너스 ASC NULLS FIRST; -- NULL, NULL, 100, 200, 300 SELECT * FROM 사원 ORDER BY 보너스 DESC NULLS LAST; -- 300, 200, 100, NULL, NULL
연습문제
기본 문제
문제 1. 다음 SQL의 결과로 올바른 것은?
SELECT NVL(NULL, 'A') || NVL('B', 'C') || NVL(NULL, NULL) FROM DUAL;
① ABC ② AB ③ ABNull ④ AB (NULL)
정답: ② 해설: NVL(NULL,'A')='A', NVL('B','C')='B', NVL(NULL,NULL)=NULL. 문자열과 NULL을 ||로 연결하면 NULL이 무시되어 'AB'가 된다. (Oracle에서 NULL || 'A' = 'A')
문제 2. 다음 중 TRUNCATE에 대한 설명으로 올바르지 않은 것은?
① DDL이므로 AUTO COMMIT이 발생한다 ② 테이블 구조는 유지된다 ③ WHERE 조건을 사용할 수 있다 ④ ROLLBACK이 불가능하다
정답: ③ 해설: TRUNCATE는 WHERE 절을 사용할 수 없다. 테이블의 전체 데이터를 삭제한다.
문제 3. 다음 SQL의 실행 순서로 올바른 것은?
SELECT 부서코드, COUNT(*) AS 인원 FROM 사원 WHERE 급여 >= 3000 GROUP BY 부서코드 HAVING COUNT(*) >= 3 ORDER BY 인원 DESC;
① SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY ② FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY ③ FROM → GROUP BY → WHERE → HAVING → SELECT → ORDER BY ④ FROM → WHERE → HAVING → GROUP BY → SELECT → ORDER BY
정답: ② 해설: SQL 실행 순서는 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY이다.
문제 4. 다음 테이블에서 SQL의 결과는?
사원 테이블:
| 사원번호 | 급여 | 보너스 |
|---|---|---|
| 1001 | 5000 | 1000 |
| 1002 | 3000 | NULL |
| 1003 | 4000 | NULL |
| 1004 | 6000 | 2000 |
SELECT COUNT(*), COUNT(보너스), AVG(보너스) FROM 사원;
① 4, 4, 750 ② 4, 2, 1500 ③ 4, 2, 750 ④ 2, 2, 1500
정답: ② 해설: COUNT(*)=4(전체행), COUNT(보너스)=2(NULL제외), AVG(보너스)=(1000+2000)/2=1500 (NULL 제외하고 계산)
문제 5. RANK, DENSE_RANK, ROW_NUMBER의 차이를 묻는 문제:
급여 데이터: 5000, 5000, 4000, 3000
DENSE_RANK() OVER (ORDER BY 급여 DESC)의 결과로 올바른 것은?
① 1, 1, 3, 4 ② 1, 2, 3, 4 ③ 1, 1, 2, 3 ④ 1, 1, 2, 4
정답: ③ 해설: DENSE_RANK는 동일 순위가 있어도 다음 순위를 건너뛰지 않는다. 5000(1위), 5000(1위), 4000(2위), 3000(3위).
문제 6. 다음 중 LEFT OUTER JOIN의 Oracle 전통 표기법으로 올바른 것은?
-- ANSI 표준: SELECT e.사원명, d.부서명 FROM 사원 e LEFT OUTER JOIN 부서 d ON e.부서코드 = d.부서코드;
① WHERE e.부서코드(+) = d.부서코드 ② WHERE e.부서코드 = d.부서코드(+) ③ WHERE e.부서코드 = d.부서코드 LEFT(+) ④ WHERE (+)e.부서코드 = d.부서코드
정답: ② 해설: Oracle에서 (+)는 데이터가 없는(부족한) 쪽에 붙인다. LEFT OUTER JOIN은 왼쪽(사원)의 모든 행을 보존하므로, 오른쪽(부서)에 (+)를 붙인다.
문제 7. 다음 SQL의 결과 건수는?
사원 테이블: 10건, 부서 테이블: 5건
SELECT * FROM 사원 CROSS JOIN 부서;
① 10건 ② 15건 ③ 50건 ④ 5건
정답: ③ 해설: CROSS JOIN은 카테시안 곱이므로 10 × 5 = 50건이다.
문제 8. 다음 계층형 쿼리에서 PRIOR 키워드의 위치가 의미하는 것은?
CONNECT BY PRIOR 사원번호 = 관리자번호
① 역방향 전개 (자식→부모) ② 순방향 전개 (부모→자식) ③ 같은 레벨 전개 ④ 전체 전개
정답: ② 해설: PRIOR가 사원번호(PK) 쪽에 있으므로 이전 행의 사원번호가 현재 행의 관리자번호와 같은 행을 찾는다. 이는 부모에서 자식으로 가는 순방향 전개이다.
문제 9. 다음 중 서브쿼리의 종류와 위치가 올바르게 매칭된 것은?
① 스칼라 서브쿼리 - FROM 절 ② 인라인 뷰 - SELECT 절 ③ 연관 서브쿼리 - WHERE 절 ④ 중첩 서브쿼리 - HAVING 절만
정답: ③ 해설: 스칼라 서브쿼리는 SELECT 절, 인라인 뷰는 FROM 절, 연관 서브쿼리는 주로 WHERE 절에서 메인 쿼리의 칼럼을 참조하여 사용된다.
문제 10. 트랜잭션의 특성 중 "트랜잭션이 전부 실행되거나 전부 취소되어야 한다"는 특성은?
① 일관성 (Consistency) ② 원자성 (Atomicity) ③ 고립성 (Isolation) ④ 지속성 (Durability)
정답: ② 해설: 원자성(Atomicity)은 All or Nothing 원칙으로, 트랜잭션의 모든 연산이 완전히 수행되거나 전혀 수행되지 않아야 함을 의미한다.
심화 문제
문제 11. 다음 결과를 예측하시오:
SELECT 1 FROM DUAL WHERE NULL = NULL;
① 1 ② NULL ③ 결과 없음 (0건) ④ 에러
정답: ③ 해설: NULL = NULL은 Unknown이므로 WHERE 조건이 TRUE가 아니어서 결과가 반환되지 않는다. NULL 비교는 IS NULL / IS NOT NULL을 사용해야 한다.
문제 12. 다음 SQL에서 ROLLUP의 결과로 생성되는 소계 행의 수는?
SELECT 부서코드, 직급, 성별, SUM(급여) FROM 사원 GROUP BY ROLLUP(부서코드, 직급, 성별);
부서코드 3개, 직급 2개, 성별 2개인 경우:
① 3개 ② 6개 ③ 총 그룹 수에 따라 다름 ④ 인자 수 + 1 = 4단계 소계
정답: ④ (개념적) 해설: ROLLUP(A, B, C)는 4단계의 소계를 생성한다: (A, B, C) → (A, B) → (A) → () 전체 총계. 즉, 일반 그룹 + A,B별 소계 + A별 소계 + 전체 총계.
문제 13. 다음 두 SQL의 결과가 같은가 다른가?
-- SQL1 SELECT * FROM 사원 WHERE 부서코드 NOT IN ('D01', 'D02', NULL); -- SQL2 SELECT * FROM 사원 WHERE 부서코드 NOT IN ('D01', 'D02');
① 같다 ② 다르다 (SQL1이 더 적다) ③ 다르다 (SQL1이 0건) ④ 에러 발생
정답: ③ 해설: NOT IN에 NULL이 포함되면 모든 비교 결과가 Unknown이 되어 결과가 0건이 된다. 이는 NULL의 비교 연산 특성 때문이다. NOT IN (A, B, NULL) → 부서코드<>A AND 부서코드<>B AND 부서코드<>NULL 마지막 조건이 항상 Unknown이므로 전체가 Unknown → 결과 없음.