반응형

🧠 SQL 정규화(Normalization) 완전 정복: 제1정규형 ~ 제3정규형까지!

안녕하세요! 오늘은 SQLD 시험과 실무에서 모두 중요한 데이터베이스 정규화(Normalization) 개념에 대해 다뤄보겠습니다.

정규화란? 👉 데이터 중복을 제거하고 이상(anomaly)을 방지하여 데이터베이스 구조를 논리적으로 정리하는 과정입니다.

이번 글에서는 제1정규형부터 제3정규형까지 예제와 함께 아주 자세히 설명드릴게요. 📚✨


✅ 정규화가 필요한 이유

정규화가 되지 않은 테이블은 아래와 같은 문제를 일으킬 수 있습니다:

  • 삽입 이상: 일부 속성만 입력할 수 없음
  • 삭제 이상: 일부 데이터를 삭제하면 관련 정보도 같이 사라짐
  • 갱신 이상: 데이터를 일부 수정할 때 여러 군데 수정을 해야 함 → 무결성 깨질 수 있음

🧩 예제 테이블 (비정규형)

학번 이름 연락처 수강과목
1001 홍길동 010-1234-5678 DB, 운영체제
1002 김영희 010-2345-6789 자료구조
1003 이철수 010-3456-7890 DB, 자료구조, 통계학

위 테이블은 비정규형(unnormalized form)입니다. 왜냐하면 하나의 셀(수강과목)에 여러 개의 값이 들어있기 때문입니다. 그럼 정규화를 단계별로 진행해볼게요!


🔹 제1정규형 (1NF: First Normal Form)

정의: 모든 속성의 값이 원자값(Atomic Value)을 가져야 한다.

하나의 셀에는 하나의 값만 존재해야 함

📌 변환 후 테이블

학번 이름 연락처 수강과목
1001 홍길동 010-1234-5678 DB
1001 홍길동 010-1234-5678 운영체제
1002 김영희 010-2345-6789 자료구조
1003 이철수 010-3456-7890 DB
1003 이철수 010-3456-7890 자료구조
1003 이철수 010-3456-7890 통계학

✅ 효과

  • 원자값만 남기므로 검색이 쉬워짐
  • 하지만 여전히 중복된 정보(이름, 연락처 등)가 존재함

🔹 제2정규형 (2NF: Second Normal Form)

정의: 제1정규형을 만족하면서, 기본키의 부분집합이 결정자가 되는 종속성이 없어야 함 (부분함수 종속 제거)

즉, 복합키인 경우 부분키에만 종속된 속성 제거

📌 예시 분석

  • 기본키: (학번, 수강과목)
  • 이름, 연락처는 학번에만 종속되어 있음 → 부분함수 종속 → 2NF 위반!

📌 2NF로 분해된 테이블

학생 테이블

학번 이름 연락처
1001 홍길동 010-1234-5678
1002 김영희 010-2345-6789
1003 이철수 010-3456-7890

수강 테이블

학번 수강과목
1001 DB
1001 운영체제
1002 자료구조
1003 DB
1003 자료구조
1003 통계학

✅ 효과

  • 부분함수 종속 제거
  • 중복 데이터가 줄어듦

🔹 제3정규형 (3NF: Third Normal Form)

정의: 제2정규형을 만족하면서, 이행적 함수 종속이 없어야 함

A → B, B → C 이면 A → C는 이행적 종속. 이걸 제거해야 3NF!

📌 예시 테이블 (추가된 속성)

학번 수강과목 교수명
1001 DB 김교수
1002 자료구조 이교수
1003 통계학 박교수
  • 교수명은 수강과목에 따라 결정됨 → 학번 → 수강과목 → 교수명 → 이행적 종속!

📌 3NF로 분해된 테이블

수강 테이블

학번 수강과목
1001 DB
1002 자료구조
1003 통계학

과목 테이블

수강과목 교수명
DB 김교수
자료구조 이교수
통계학 박교수

✅ 효과

  • 이행적 종속 제거 → 유지보수 편해짐
  • 데이터 무결성 보장

📝 마무리 정리

정규형 정의 목적 제거대상
1NF 모든 속성이 원자값 셀마다 하나의 값 반복 속성 (다중값)
2NF 부분함수 종속 제거 키에 완전 종속된 속성만 유지 부분 종속
3NF 이행적 함수 종속 제거 비속성 간 종속 제거 이행 종속

정규화는 단순히 암기하는 게 아니라, 데이터를 얼마나 구조적으로 잘 관리할 수 있느냐를 보여주는 지표입니다.

반응형
반응형

🔍 정규표현식(Regular Expression) 완벽 가이드

안녕하세요,  오늘은 SQLD 과목 중에서도 헷갈릴 수 있는 정규표현식(Regex)에 대해 정리해보겠습니다. 


✅ 정규표현식이란?

  • 정규표현식(Regular Expression)은 문자열에서 특정한 규칙(패턴)을 가진 문자열을 찾거나 비교할 때 사용하는 표현식입니다.
  • 문자열 검색 / 추출 / 유효성 검증 등에 활용
  • DB에서 REGEXP, REGEXP_LIKE와 함께 출제될 수 있음

🧱 기본 기호 및 의미 정리

기호 의미 상세설명 예시
. 임의의 한 문자 어떤 문자든 상관없이 1개 a.b → “acb”, “a1b” 등
* 0개 이상 반복 앞 문자가 0번 이상 나올 수 있음 bo* → “b”, “bo”, “boo” 등
+ 1개 이상 반복 앞 문자가 1번 이상 반복 bo+ → “bo”, “boo” 등 (“b”는 불일치)
? 0개 또는 1개 앞 문자가 0번 또는 1번 나타남 colou?r → “color”, “colour” 모두 매칭
[] 문자 집합 대괄호 안의 문자 중 하나를 의미 [abc] → “a”, “b”, “c” 중 하나
[^] 부정 문자 집합 대괄호 안에 있는 문자를 제외한 문자 [^abc] → a, b, c 제외한 문자
` ` OR 조건 두 패턴 중 하나
() 그룹핑 괄호 안 패턴을 하나의 단위로 묶음 (ab)+ → “ab”, “abab” 등 반복 가능

📍 위치 지정자 (Anchors)

기호 의미 상세 설명 예시
^ 문자열 시작 해당 문자/패턴이 문자열의 시작에 있어야 함 ^abc → “abc123”은 매칭, “123abc”는 불일치
$ 문자열 끝 해당 문자/패턴이 문자열의 끝에 있어야 함 xyz$ → “endxyz”는 매칭, “xyzend”는 불일치

🔢 반복 수량자 (Quantifiers)

표현식 의미 상세 설명
{n} n번 반복 앞 문자가 정확히 n번 반복됨
{n,} n번 이상 반복 앞 문자가 최소 n번 이상 반복됨
{n,m} n ~ m번 반복 앞 문자가 n번 이상 m번 이하 반복됨

예시: a{2,4} → “aa”, “aaa”, “aaaa”는 매칭, “a”나 “aaaaa”는 불일치


🧠 실무에서는 어떻게 쓰일까?

1. 다음 중 이메일 형식에 가장 적절한 정규표현식

^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$
    •   1개 이상 반복, \. : 점(.) 문자, ^, $는 시작과 끝 의미

2. 010 또는 011로 시작하는 핸드폰 번호 정규식

^01[0-9]-?[0-9]{3,4}-?[0-9]{4}$
  • ?는 하이픈이 있어도 되고 없어도 되는 선택 옵션

📚 SQL에서 정규표현식 관련 함수 (REGEXP / REGEXP_LIKE / REGEXP_SUBSTR)

📌 1. REGEXP

  • MySQL 등에서 사용하며, 문자열이 정규표현식과 일치하면 TRUE 반환
SELECT *
FROM users
WHERE email REGEXP '^[a-z0-9]+@[a-z]+\\\\.[a-z]{2,3}$';

📌 2. REGEXP_LIKE

  • Oracle에서 주로 사용, LIKE보다 정교한 문자열 매칭 가능
SELECT *
FROM users
WHERE REGEXP_LIKE(email, '^[a-z0-9._%+-]+@[a-z0-9.-]+\\\\.[a-z]{2,}$');

📌 3. REGEXP_SUBSTR

  • 문자열 중 정규표현식에 일치하는 부분 문자열만 추출할 때 사용

📘 문법

REGEXP_SUBSTR(문자열, 정규표현식 [, 시작위치 [, 발생순서 [, 일치방식 [, 플래그]]]])

✔ 예시

SELECT REGEXP_SUBSTR('My phone: 010-1234-5678', '\\d{3}-\\d{4}-\\d{4}') AS phone
FROM dual;
  • 결과: 010-1234-5678
  • 숫자와 하이픈으로 구성된 전화번호를 추출

또는 이메일 주소 중 도메인만 추출할 때:

SELECT REGEXP_SUBSTR('user@example.com', '@[a-z.]+') AS domain
FROM dual;
  • 결과: @example.com

📝 SQLD 암기 포인트 정리표

포인트 설명
. 아무 문자 1개 (도트 = 점 하나!)
* 0번 이상 반복 (없어도 되고 많아도 됨)
+ 1번 이상 반복 (하나는 꼭 있어야 함)
? 0개 또는 1개 (있거나 없거나)
^, $ 문자열의 시작과 끝 위치 지정
[] 괄호 안 문자 중 하나 선택 (선택박스 느낌)
() 그룹핑 (묶어서 하나의 단위로 처리)
{} 반복 횟수 제어용 수량자
REGEXP_LIKE 오라클에서 정규표현식 조건 검사할 때 사용
REGEXP_SUBSTR 일치하는 문자열을 추출할 때 사용

🎯 마무리 정리

  • 정규표현식은 기호의 의미, 패턴의 구성 방식, 간단한 검증 정규식 관련 문제로 출제됩니다.
  • REGEXP, REGEXP_LIKE, REGEXP_SUBSTR 함수는 문자열 패턴을 조건으로 주거나 추출할 때 핵심입니다.
  • 복잡한 정규식보다는 각 기호의 기능을 정확히 알고 적용하는 것이 핵심입니다.

 

반응형
반응형

📊 SQL PIVOT / UNPIVOT 완전 정복! 실무 & 시험에 모두 나오는 핵심 개념

안녕하세요, 오늘은 데이터 집계와 변환에서 자주 활용되는 PIVOTUNPIVOT 절에 대해 알아보겠습니다. 실무에서 리포트를 만들거나, 시험에서 형태 변환 관련 문제가 나올 때 정말 유용한 기능이니 꼭 숙지해두세요!


🔄 PIVOT 절이란?

PIVOT은 행(row) 데이터를 열(column)로 바꾸는 기능입니다. 즉, 세로 데이터를 가로로 펼쳐서 보기 쉽게 만들어 줍니다.

✅ 기본 문법 (Oracle 기준)

SELECT *
FROM (
  SELECT 부서, 직급, 급여 FROM employee
)
PIVOT (
  SUM(급여) FOR 직급 IN ('사원' AS staff, '대리' AS assistant, '과장' AS manager)
);

💡 해석:

  • 내부 서브쿼리에서 부서, 직급, 급여를 뽑고
  • 직급을 기준으로 열로 변환하며,
  • 각 직급의 급여 합계를 컬럼별로 보여줍니다.

📌 결과 예시:

부서 staff assistant manager
총무부 3000 4000 5000
영업부 2800 NULL 5200

🔃 UNPIVOT 절이란?

UNPIVOT은 열(column) 데이터를 행(row)으로 바꾸는 기능입니다. 즉, 가로로 되어 있는 데이터를 세로로 펼쳐서 상세하게 분석할 수 있게 합니다.

✅ 기본 문법

SELECT *
FROM (
  SELECT 부서, staff, assistant, manager FROM pivoted_data
)
UNPIVOT (
  급여 FOR 직급 IN (staff AS '사원', assistant AS '대리', manager AS '과장')
);

💡 해석:

  • staff, assistant, manager라는 열을
  • 직급과 급여라는 두 개의 열로 변환합니다.

📌 결과 예시:

부서 직급 급여
총무부 사원 3000
총무부 대리 4000
총무부 과장 5000
영업부 사원 2800
영업부 과장 5200

💡 NULL 값은 UNPIVOT시 자동으로 제거되는 경우가 많습니다. 필요 시 INCLUDE NULLS 옵션도 사용 가능합니다.


📝 실무 사용 팁

  • PIVOT은 집계 함수(SUM, AVG 등)와 함께 사용하면 요약표 만들기에 좋습니다.
  • UNPIVOT은 다양한 지표를 한 컬럼에 모아 비교 분석할 때 유용합니다.

📚 시험 대비 암기 팁

기능 암기법
PIVOT Pivot → “열로 회전!” (P자로 Pivot 회전 상상)
UNPIVOT Unpivot → “행으로 다시 풀어!”
FOR 절 무엇을 기준으로 바꿀지!
IN 절 어떤 항목들로 열/행을 만들지!

✅ 마무리 정리

  • PIVOT: 행 데이터를 열로 변환 → 요약표, 리포트에 딱!
  • UNPIVOT: 열 데이터를 행으로 변환 → 정규화, 비교 분석에 유리!

 

반응형
반응형

🌳 SQL 계층형 질의 완벽 정리!  🌳

안녕하세요, 오늘은 데이터베이스에서 데이터를 계층적으로 표현할 때 자주 사용하는 계층형 질의(Hierarchical Query)에 대해서 확실하게 배워보겠습니다. 특히 오라클에서 많이 활용되므로 시험 대비로도 필수입니다!


📌 계층형 질의란 무엇인가요?

계층형 질의는 데이터가 부모-자식 관계로 구성될 때, 데이터의 계층 구조를 쉽게 조회할 수 있도록 도와주는 특별한 쿼리입니다. 조직도, 메뉴 구조, 카테고리 분류 등에서 자주 사용됩니다.

🛠️ 기본 문법부터 알아볼까요?

기본 구조는 다음과 같습니다:

SELECT 컬럼명
FROM 테이블명
START WITH 시작조건
CONNECT BY PRIOR 부모컬럼 = 자식컬럼;
  • START WITH: 계층의 최상위 데이터를 지정합니다.
  • CONNECT BY PRIOR: 부모와 자식 데이터를 연결하는 조건을 설정합니다.
  • PRIOR 부모컬럼 = 자식컬럼 => 역방향 전개
  • PRIOR 자식컬럼 = 부모컬럼 => 순방향 전개

🔎 이해하기 쉬운 예시!

직원과 상사의 관계를 나타내는 employee 테이블을 예로 들어보겠습니다:

emp_id emp_name mgr_id (상사 id)
1 홍길동 NULL
2 김철수 1
3 이영희 1
4 박준호 2
5 최지연 2

🔥 계층형 질의 예시 쿼리

SELECT emp_id, emp_name, mgr_id, LEVEL
FROM employee
START WITH mgr_id IS NULL
CONNECT BY PRIOR emp_id = mgr_id;

📚 결과 분석

emp_id emp_name mgr_id LEVEL
1 홍길동 NULL 1
2 김철수 1 2
4 박준호 2 3
5 최지연 2 3
3 이영희 1 2
  • LEVEL: 계층의 깊이를 나타냅니다. (1은 최상위이며 숫자가 클수록 하위 단계입니다.)

🌟 자주 쓰이는 옵션 정리

1️⃣ LEVEL

계층의 깊이를 확인할 수 있습니다. 보고서나 메뉴에서 들여쓰기를 할 때 유용합니다.

2️⃣ CONNECT_BY_ROOT

최상위 부모를 확인할 수 있습니다.

SELECT emp_name, CONNECT_BY_ROOT emp_name AS root
FROM employee
START WITH mgr_id IS NULL
CONNECT BY PRIOR emp_id = mgr_id;

3️⃣ SYS_CONNECT_BY_PATH

경로를 보기 쉽게 표시해줍니다.

SELECT emp_name, SYS_CONNECT_BY_PATH(emp_name, '/') AS path
FROM employee
START WITH mgr_id IS NULL
CONNECT BY PRIOR emp_id = mgr_id;

💡 시험 대비 외우기 쉬운 꿀팁!

  • START WITH: 어디서부터 시작할지 “시작점”을 기억하세요!
  • CONNECT BY PRIOR: 부모(PRIOR)가 앞에 오고 자식이 뒤에 온다고 기억하면 쉽습니다.
  • LEVEL은 항상 “1부터 시작하는 깊이의 계단”이라고 외우세요!
  • CONNECT_BY_ROOT는 “뿌리(ROOT)는 항상 최상위!“입니다.
  • SYS_CONNECT_BY_PATH는 경로(PATH)를 보여준다고 생각하세요!
  • PRIOR 부모컬럼 = 자식컬럼 => 역방향 전개 (프부자)
  • PRIOR 자식컬럼 = 부모컬럼 => 순방향 전개 (프자부)

🚩 마무리 정리!

계층형 질의는 데이터의 구조를 명확하게 표현할 때 매우 유용합니다. 특히 오라클에서는 실무에서도, 시험에서도 자주 등장하니 이번 기회에 확실히 이해하고 시험 준비도 완벽하게 하시기 바랍니다!

반응형
반응형

SQL 행 관련 순서함수(오프셋 함수) 완벽 정리 🚀

안녕하세요! 오늘은 SQL 윈도우 함수 중에서도 행의 순서를 기반으로 데이터를 처리하는 오프셋 함수(OFFSET Functions)에 대해 알아보겠습니다.

오프셋 함수는 특정 행을 기준으로 앞뒤에 위치한 행의 값을 쉽게 참조할 수 있도록 도와줍니다. 주로 데이터를 시계열적으로 분석하거나 비교할 때 매우 유용합니다.


🔍 오프셋 함수의 종류와 특징

SQL에서 가장 자주 사용하는 오프셋 함수는 크게 세 가지입니다:

1️⃣ LAG()

LAG() 함수는 현재 행에서 지정한 수만큼 앞에 있는 행의 값을 반환합니다.

사용법

LAG(컬럼명, offset, 기본값) OVER (ORDER BY 컬럼명)
  • offset(생략 가능): 몇 번째 앞의 행을 참조할지 지정 (기본값 1)
  • 기본값(생략 가능): 참조할 행이 없을 경우 반환할 값 (기본값 NULL)

예시

SELECT emp_name, salary,
       LAG(salary, 1, 0) OVER (ORDER BY hire_date) AS prev_salary
FROM employee;
  • 각 직원의 이전 직원의 급여를 보여줍니다. 첫 행의 경우 이전 행이 없으므로 0을 반환합니다.

2️⃣ LEAD()

LEAD() 함수는 현재 행에서 지정한 수만큼 뒤에 있는 행의 값을 반환합니다.

사용법

LEAD(컬럼명, offset, 기본값) OVER (ORDER BY 컬럼명)
  • offset(생략 가능): 몇 번째 뒤의 행을 참조할지 지정 (기본값 1)
  • 기본값(생략 가능): 참조할 행이 없을 경우 반환할 값 (기본값 NULL)

예시

SELECT emp_name, salary,
       LEAD(salary, 1, 0) OVER (ORDER BY hire_date) AS next_salary
FROM employee;
  • 각 직원의 다음 직원의 급여를 표시합니다. 마지막 행은 다음 행이 없으므로 0을 반환합니다.

3️⃣ FIRST_VALUE(), LAST_VALUE()

  • FIRST_VALUE(): 지정된 윈도우 범위 내의 첫 번째 행 값을 반환합니다.
  • LAST_VALUE(): 지정된 윈도우 범위 내의 마지막 행 값을 반환합니다.

사용법

FIRST_VALUE(컬럼명) OVER (ORDER BY 컬럼명 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)

예시

SELECT emp_name, salary,
       FIRST_VALUE(salary) OVER (ORDER BY hire_date
                                 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS first_salary,
       LAST_VALUE(salary) OVER (ORDER BY hire_date
                                ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS last_salary
FROM employee;
  • 입사일 기준으로 처음 입사한 직원의 급여와 현재까지 마지막 직원의 급여를 각각 반환합니다.

⚠️ 주의사항

  • 오프셋 함수를 사용할 때는 반드시 ORDER BY 절을 명시해야 합니다.
  • FIRST_VALUE(), LAST_VALUE() 함수는 프레임절(Frame Clause)을 함께 사용할 때 정확한 결과를 얻을 수 있습니다.

🎯 요약 정리

함수명 역할 주요사용예시
LAG() 현재 행 이전의 행 값을 참조 이전 시점의 데이터와 비교 분석
LEAD() 현재 행 이후의 행 값을 참조 다음 시점의 데이터와 비교 분석
FIRST_VALUE() 윈도우 내 첫 번째 행의 값 반환 누적 비교나 최초값 기준 비교 시 사용
LAST_VALUE() 윈도우 내 마지막 행의 값 반환 누적 비교나 최신값 기준 비교 시 사용

 

반응형
반응형

SQL 윈도우 함수의 프레임절(Frame Clause) 완벽 이해하기 📈

안녕하세요, 오늘은 SQL의 강력한 기능 중 하나인 윈도우 함수(Window Functions)에서 자주 사용되는 프레임절(Frame Clause)에 대해 자세히 알아보겠습니다.

📌 프레임절(Frame Clause)이란?

프레임절은 윈도우 함수를 사용할 때 분석할 데이터의 범위를 지정하는 역할을 합니다. 윈도우 함수는 OVER 절을 이용해 특정 범위를 정의하는데, 이때 프레임절로 범위를 구체적으로 설정합니다.

기본 형태는 다음과 같습니다

윈도우함수() OVER (
    PARTITION BY 컬럼명
    ORDER BY 컬럼명
    [ROWS 또는 RANGE 프레임시작 AND 프레임종료]
);

🔍 ROWS와 RANGE의 차이점

1️⃣ ROWS

ROWS는 행의 물리적 위치를 기준으로 프레임을 정의합니다.

  • ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING: 현재 행을 기준으로 이전 행과 다음 행을 포함
  • ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: 첫 행부터 현재 행까지 포함

예시:

SELECT emp_name, salary,
       AVG(salary) OVER (
           ORDER BY salary
           ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
       ) AS avg_salary
FROM employee;
  • 각 직원의 급여를 기준으로 앞뒤 한 명씩(총 3명)의 급여 평균을 계산합니다.

2️⃣ RANGE

RANGE는 논리적인 값을 기준으로 프레임을 정의합니다. ORDER BY로 정렬된 값에 따라 프레임 범위가 결정됩니다.

  • RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: ORDER BY의 값이 시작부터 현재 행까지 같은 모든 행을 포함합니다.

예시:

SELECT salary,
       SUM(bonus) OVER (
           ORDER BY salary
           RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS cumulative_bonus
FROM employee;
  • 급여를 기준으로 누적된 보너스를 합산하여 나타냅니다. 급여가 동일한 모든 직원은 같은 프레임에 포함됩니다.

📝 프레임절 사용 시 주의사항

  • ROWS를 사용할 때는 행 위치가 명확히 구분되지만, RANGE를 사용할 때는 동일 값이 여러 개일 경우 같은 프레임으로 묶입니다.
  • 성능적인 측면에서 ROWS가 RANGE보다 일반적으로 더 빠르게 처리됩니다.

🚩 프레임절 자주 쓰는 표현

표현식 의미

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 처음 행부터 현재 행까지 포함
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING 현재 행부터 마지막 행까지 포함
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 현재 행의 이전 행과 다음 행 포함
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 논리적 값 기준 처음부터 현재 값까지 포함

💡 외우기 쉬운 꿀팁!

  • ROWS는 “Row by Row(행 단위로)“를 생각하면 물리적 행 위치로 기억하기 쉬워요!
  • RANGE는 “Logical RANGE(논리적 범위)“로 논리적 값을 기준으로 기억하세요.
  • “PRECEDING(이전), CURRENT(현재), FOLLOWING(이후)” 단어 뜻 그대로 외우면 이해하기 편합니다.

🎯 요약 및 결론

  • 프레임절은 윈도우 함수에서 분석 범위를 명확히 정의합니다.
  • 물리적 행 위치를 명확히 다룰 때는 ROWS를, 논리적 값의 범위를 다룰 때는 RANGE를 사용합니다.
  • 적절한 프레임 설정을 통해 원하는 데이터를 정확히 분석할 수 있습니다.

 

반응형
반응형

SQL NULL 관련 함수 완벽 정리 🌟

오늘은 SQL에서 자주 사용되면서도 초보자들이 헷갈려 하는 NULL 처리 함수들에 대해 알아보겠습니다.

SQL에서 NULL이란 ‘값이 없다’ 또는 ‘알 수 없는 값’을 의미합니다. 따라서 일반적인 값과는 다르게 처리해야 하는데요, 이때 유용하게 사용할 수 있는 함수들이 있습니다. 대표적인 NULL 처리 함수들을 하나씩 살펴볼까요?


🔍 1. NVL(컬럼명, 대체값)

NVL 함수는 Oracle에서 제공하는 함수로, 특정 컬럼이 NULL일 경우 지정한 다른 값으로 대체해줍니다.

사용법

SELECT NVL(COMM, 0)
FROM EMP;

의미: EMP 테이블의 COMM 컬럼 값이 NULL이라면 0으로 출력합니다.


🔍 2. IFNULL(컬럼명, 대체값)

MySQL/MariaDB에서 NVL 함수와 유사한 역할을 합니다.

사용법

SELECT IFNULL(COMM, 0)
FROM EMP;

의미: COMM 컬럼 값이 NULL이라면 0으로 표시합니다.


🔍 3. ISNULL(컬럼명, 대체값)

SQL Server에서 사용하는 NULL 대체 함수입니다.

사용법

SELECT ISNULL(COMM, 0)
FROM EMP;

의미: COMM 컬럼 값이 NULL일 때 0으로 변경하여 출력합니다.


🔍 4. COALESCE(컬럼1, 컬럼2, …, 컬럼n)

COALESCE 함수는 표준 SQL 함수로 여러 개의 컬럼 또는 표현식을 입력받아, NULL이 아닌 첫 번째 값을 반환합니다.

사용법

SELECT COALESCE(COMM, BONUS, 0)
FROM EMP;

의미: COMM 값이 NULL이라면 BONUS를 확인하고, BONUS도 NULL이라면 마지막 값 0을 출력합니다.

  • COALESCE는 여러 개의 컬럼을 사용할 수 있어 NVL이나 IFNULL보다 확장성이 높습니다.

🔍 5. NULLIF(표현식1, 표현식2)

두 표현식을 비교하여 값이 같다면 NULL을 반환하고, 다르면 표현식1의 값을 반환합니다.

사용법

SELECT NULLIF(SAL, 0)
FROM EMP;

의미: SAL이 0일 경우 NULL로 처리하여 출력하고, 0이 아니면 SAL의 원래 값을 출력합니다.


🌟 각 함수별 DB 지원 정리

함수명 Oracle MySQL/MariaDB SQL Server 표준 SQL
NVL ✔️
IFNULL ✔️
ISNULL ✔️
COALESCE ✔️ ✔️ ✔️ ✔️
NULLIF ✔️ ✔️ ✔️ ✔️

🎯 언제, 무엇을 쓰면 좋을까?

  • Oracle 환경이라면 NVL을, MySQL이라면 IFNULL을 선택하세요.
  • SQL Server에서는 ISNULL이 최적입니다.
  • 여러 컬럼 중 첫 번째로 나타나는 NULL이 아닌 값을 원할 땐 COALESCE를 쓰세요!
  • 특정 값이 나오면 강제로 NULL로 변환하고 싶다면 NULLIF가 딱입니다.

 

반응형
반응형

데이터베이스 설계 과정에서 테이블의 행(row)을 고유하게 식별하는 방법으로는 크게 본질식별자인조식별자가 있습니다. 각각의 식별자는 특징과 용도에 따라 적절히 활용됩니다. 오늘은 이 두 가지 식별자에 대해 알아보겠습니다.


1️⃣ 본질식별자

본질식별자는 테이블에 저장되는 데이터의 본질적인 특성을 기반으로 하여 정의되는 식별자입니다. 쉽게 말해, 업무적으로 자연스럽게 만들어지는 식별자입니다.

📌 특징

  • 업무 프로세스에서 자연스럽게 생성됩니다.
  • 테이블의 데이터 자체가 본질식별자를 포함하고 있습니다.
  • 데이터의 의미를 직접적으로 나타내는 경우가 많습니다.

📖 예시

  • 학번: 학생이 학교에 입학할 때 부여되는 번호로, 각 학생을 명확히 구별할 수 있습니다. 학번 자체만으로도 학생이라는 데이터의 본질적 특성을 반영하고 있습니다.
  • 고객번호: 고객을 고유하게 구분하기 위해 업무적으로 생성되는 번호입니다.

2️⃣ 인조식별자

인조식별자는 데이터베이스 내에서 데이터를 쉽게 관리하기 위해 인위적으로 만들어진 식별자입니다. 업무 프로세스와 직접적으로 관련되지 않으며, 주로 데이터의 본질식별자가 복잡할 때 사용됩니다.

📌 특징

  • 데이터베이스 시스템에서 자동으로 생성됩니다.
  • 본질적인 데이터 특성과는 무관하게 운영됩니다.
  • 일반적으로 순차적인 숫자나 UUID 등의 형태로 만들어집니다.

📖 예시

  • 등록ID: 수강 기록 데이터를 관리할 때 시스템이 자동으로 1, 2, 3...과 같은 순차적인 번호를 부여합니다.
  • 주문번호: 주문 엔티티가 고객번호+주문날짜+순번과 같이 복잡한 구성을 가지고 있을 때, 이를 간략화하고 쉽게 관리하기 위해 인조식별자로 단순화된 번호를 사용합니다.

인조식별자만으로는 데이터의 구체적인 내용을 알기 어렵습니다. 예를 들어, 등록ID만으로 특정 학생이 언제 어떤 과목을 수강했는지를 알 수 없으며, 반드시 학번, 학기, 과목코드 등의 다른 정보를 함께 사용해야 정확한 의미를 알 수 있습니다.


🔍 요약 정리

구분 본질식별자(Natural Key) 인조식별자(Surrogate Key)
정의 업무에서 자연스럽게 생성되는 키 시스템에서 인위적으로 생성하는 키
예시 학번, 고객번호 등록ID, 주문번호
특징 데이터의 본질적 의미 반영 데이터 관리 편의를 위한 임의적 생성
반응형
반응형

문제 설명

다음 SQL의 결과는 무엇일까요?

SELECT COUNT(*)
FROM T1
WHERE NOT EXISTS (
  SELECT 'X'
  FROM T2
  WHERE T1.COL1 = T2.COL1
);

T1 테이블

COL1

10
20

T2 테이블

COL1

10
NULL

① 2  ② 0  ③ 1  ④ 오류 발생


쿼리 분석 및 핵심 포인트

이 문제는 단순한 NOT EXISTS 서브쿼리 문제가 아니에요. NULL과의 비교 연산이 핵심 포인트입니다.

1.  NOT EXISTS란?

  • NOT EXISTS (SELECT ... FROM T2 WHERE 조건)은 T2의 조건절을 만족하는 값이 존재하지 않을 때 참이 됩니다.
  • 즉, T2에서 T1.COL1 = T2.COL1절대 참이 되지 않는 경우에만 해당 T1 행이 결과에 포함돼요.

 

2.  비교 연산자 =  NULL

여기서 주의할 점! SQL에서 NULL = 어떤 값은 무조건 UNKNOWN이 되고, 이는 WHERE 절에서 거짓(FALSE)으로 처리돼요.

예시:

  • 10 = 10 → TRUE
  • 10 = NULL → UNKNOWN → WHERE에서 걸러짐
  • NULL = NULL → UNKNOWN

실제 동작 흐름 분석

T1의 각 행에 대해 서브쿼리를 돌려보자!

T1의 첫 번째 행: COL1 = 10

서브쿼리:

SELECT 'X' FROM T2 WHERE T1.COL1 = T2.COL1;
-- 즉, WHERE 10 = T2.COL1;
  • T2에 10이 있음 → 조건 만족하는 행 있음 → EXISTS는 TRUE → NOT EXISTS는 FALSE
  • → T1의 이 행은 제외됨

 

T1의 두 번째 행: COL1 = 20

서브쿼리:

SELECT 'X' FROM T2 WHERE 20 = T2.COL1;
  • T2에는 20이 없음
  • 20 = NULL → 비교 불가(UNKNOWN)→ 이 행은 결과에 포함됨
  • → 조건을 만족하는 행이 하나도 없음EXISTS는 FALSE → NOT EXISTS는 TRUE

 

정답

→ 조건을 만족하는 행은 딱 T1의 두 번째 행 1개

③ 1


마무리 요약 정리

포인트 설명
NOT EXISTS 조건을 만족하는 값이 없을 때 참
NULL = 값 항상 UNKNOWN으로 평가됨
조건절 내 NULL 처리 조심해야 하며, 존재 유무 판단 시 혼란을 유발함
NULL이 있는 컬럼과 비교하는 서브쿼리에서는, 절대 = 조건이 항상 원하는 결과를 주지 않음! EXISTS/NOT EXISTS에서 더더욱 주의!
반응형
반응형

💡 문제

다음 SQL문 중에서 다른 결과를 반환하는 것은 무엇일까요?

-- 1번
SELECT * FROM TBL WHERE V1 = 'A' AND V2 IN ('T1', 'T2', 'T3');

-- 2번
SELECT * FROM TBL WHERE V1 = 'A' AND V2 = 'T1' OR V2 = 'T2' OR V2 = 'T3';

-- 3번
SELECT * FROM TBL WHERE (V1, V2) IN (('A', 'T1'), ('A', 'T2'), ('A', 'T3'));

-- 4번
SELECT * FROM TBL WHERE V1 = 'A' AND (V2 = 'T1' OR V2 = 'T2' OR V2 = 'T3');

📌 핵심 개념 요약

1. SQL 논리 연산자 우선순위

  • NOT > AND > OR
  • 괄호가 없으면 AND가 먼저 계산됨!

2. 튜플 비교 (row value constructor)

  • (컬럼1, 컬럼2) IN ((값1, 값2), ...) 형식
  • 컬럼 쌍이 정확히 매칭될 때만 TRUE

 

📊 각 쿼리 분석

 

✅ 1번

SELECT * FROM TBL WHERE V1 = 'A' AND V2 IN ('T1', 'T2', 'T3');
  • V1 = 'A'이면서, V2T1, T2, T3 중 하나일 때만 조회
  • ✅ 조건이 명확함
  • 정상적으로 필터링된 결과

 

❌ 2번 (정답)

SELECT * FROM TBL WHERE V1 = 'A' AND V2 = 'T1' OR V2 = 'T2' OR V2 = 'T3';
  • 괄호 없음! 그래서 아래처럼 해석됨👇
(V1 = 'A' AND V2 = 'T1') OR V2 = 'T2' OR V2 = 'T3'

 

👉 이렇게 되면?

  • V1='A'이고 V2='T1' → ✅
  • 하지만 V2='T2' 또는 V2='T3'인 행은 V1이 무엇이든 조회됨! → ❌
  • 즉 결과가 달라짐

 

✅ 3번

SELECT * FROM TBL WHERE (V1, V2) IN (('A', 'T1'), ('A', 'T2'), ('A', 'T3'));
  • (V1, V2) 쌍이 (‘A’, ‘T1’), (‘A’, ‘T2’), (‘A’, ‘T3’) 중 하나일 때만 조회
  • 1번 쿼리와 논리적으로 완전 동일한 결과
  • 튜플 비교 방식의 간결한 표현

 

✅ 4번

SELECT * FROM TBL WHERE V1 = 'A' AND (V2 = 'T1' OR V2 = 'T2' OR V2 = 'T3');
  • 괄호로 OR 조건을 묶었기 때문에
V1 = 'A' AND V2 IN ('T1', 'T2', 'T3')

와 동일한 의미입니다.

 

SQL에서는 AND가 OR보다 우선순위가 높기 때문에, 괄호가 없으면 해석이 달라질 수 있습니다.
위의 2번 쿼리는 V1='A' 조건이 일부 OR 조건에만 적용되므로 불필요한 레코드까지 조회됩니다.
정확한 조건식 표현을 위해 괄호로 논리 구조를 명확히 하는 것이 중요합니다!

 

🧠 꿀팁 암기

🎯 논리 연산자 우선순위: NOT > AND > OR
🎯 괄호 없이 쓰면 예기치 않은 결과 가능성 있음
🎯 튜플 비교는 깔끔하고 안전한 대안!

반응형
반응형

SQLD 문제

다음 중애서 도메인(Domain)에 대한 특징으로 옳지 않은 것은?

1) 릴레이션의 속성에 대한 데이터 타입과 크기이다.

2) 속성에 대하여 NOT NULL 제약사항을 설정하여 NULL 값을 허용하지 않는다.

3) 속성에 값을 입력할 때 CHECK 기능을 사용해서 입력값을 검사한다.

4) 하나의 릴레이션과 관계된 다른 릴레이션의 FK(Foreign Key) 제약조건이다.


도메인(Domain) 이란 릴레이션(=테이블)의 속성(컬럼)이 가질 수 있는 값들의 집합(타입, 범위, 형식)을 말합니다.

예를 들어,

emp_age NUMBER(3)

➡️ 여기에서 emp_age는 속성, NUMBER(3)은 도메인(정수형 숫자 3자리까지) 이라고 보면됩니다.


✍️ 보기 분석

✔ 1) 릴레이션의 속성에 대한 데이터 타입과 크기이다.

정답!

도메인은 컬럼의 데이터 타입과 크기를 포함하는 개념입니다.

  • 예) VARCHAR2(20) → 문자형 20자 = 도메인 OK

 

✔ 2) 속성에 대하여 NOT NULL 제약사항을 설정하여 NULL 값을 허용하지 않는다.

정답!

도메인은 NOT NULL 같은 제약조건도 포함할 수 있습니다.

  • 제약조건도 도메인의 일부로 간주함.
  • 도메인을 정의할 때, “이 컬럼은 반드시 값이 있어야 한다”는 것도 포함 가능.

 

✔ 3) 속성에 값을 입력할 때 CHECK 기능을 사용해서 입력값을 검사한다.

정답!

도메인 정의 시 CHECK 제약조건으로 값의 범위나 조건을 설정할 수 있습니다.

age NUMBER(3) CHECK (age BETWEEN 0 AND 120)

➡️ 나이는 0~120 사이 값만 허용 → 도메인의 일부임.

 

❌ 4) 하나의 릴레이션과 관계된 다른 릴레이션의 FK(Foreign Key) 제약조건이다.

오답! ❌❌❌

요게 바로 “도메인의 개념이 아닌 것”, 즉 정답입니다.

🔻왜냐면:

  • FK(외래키)는 두 릴레이션 간의 관계를 정의하는 것이고
  • 도메인은 단일 속성의 데이터 형식이나 제약조건에 대한 것입니다.
  • 즉, 도메인은 “속성”의 내부적 제약조건, FK는 “릴레이션 간” 외부적 제약조건입니다.
보기 옳음 여부 이유
1 O 도메인은 속성의 타입과 크기 포함
2 O NOT NULL도 도메인 제약조건 가능
3 O CHECK 조건도 도메인에 포함됨
4 FK는 릴레이션 간 관계 → 도메인 아님

 

반응형
반응형

SQL에서 데이터를 순위 매기듯 나열할 수 있다면 얼마나 유용할까요?

오늘은 그중에서도 가장 자주 쓰이는 순위 함수 ROW_NUMBER()에 대해 배우고,

직접 실습 테이블을 만들고 쿼리도 돌려보는 실습 포스트를 준비했어요!


🎯 ROW_NUMBER() 함수란?

ROW_NUMBER()는 SQL의 윈도우 함수(Window Function) 중 하나로, 지정한 정렬 기준에 따라 각 행에 고유한 순위를 부여합니다.

 

  • 같은 값이 있어도 중복 없이 1, 2, 3… 식으로 순서가 매겨져요.
  • 그룹화(PARTITION BY)와 정렬 기준(ORDER BY)을 통해 다양한 방식으로 순위를 나눌 수 있어요.

 

📌 문법

ROW_NUMBER() OVER (PARTITION BY 컬럼명 ORDER BY 정렬기준)
구성요소 설명
PARTITION BY 그룹을 나누는 기준 (선택사항)
ORDER BY 순서를 매길 기준 (필수)

🧪 실습 테이블 만들기 (오라클 기준)

 

📂 1. employees 테이블 생성

CREATE TABLE employees (
    emp_id     NUMBER PRIMARY KEY,
    emp_name   VARCHAR2(100),
    dept_name  VARCHAR2(50),
    salary     NUMBER
);

 

💾 2. 샘플 데이터 삽입

INSERT INTO employees VALUES (1, '홍길동', '영업부', 5000);
INSERT INTO employees VALUES (2, '김철수', '영업부', 5500);
INSERT INTO employees VALUES (3, '이영희', '영업부', 5000);
INSERT INTO employees VALUES (4, '박민수', '개발부', 6000);
INSERT INTO employees VALUES (5, '최지우', '개발부', 6500);
INSERT INTO employees VALUES (6, '정해인', '개발부', 6000);
INSERT INTO employees VALUES (7, '장원영', '총무부', 4500);
INSERT INTO employees VALUES (8, '안유진', '총무부', 4600);

📊 실습 1: 부서별 급여순 1등만 출력

SELECT *
FROM (
    SELECT
        emp_id,
        emp_name,
        dept_name,
        salary,
        ROW_NUMBER() OVER (
            PARTITION BY dept_name
            ORDER BY salary DESC
        ) AS rn
    FROM employees
)
WHERE rn = 1;

부서별 급여가 가장 높은 직원만 뽑아낸 결과입니다!
ROW_NUMBER()가 부서별로 급여 내림차순 정렬 후 순위를 부여하고,
rn = 1 조건으로 1등만 골라냈습니다.

 

📊 실습 2: 부서별 전체 순위 출력

SELECT
    emp_id,
    emp_name,
    dept_name,
    salary,
    ROW_NUMBER() OVER (
        PARTITION BY dept_name
        ORDER BY salary DESC
    ) AS rn
FROM employees
ORDER BY dept_name, rn;


🎁 RANK() vs DENSE_RANK() vs ROW_NUMBER()

함수 동일한 값 처리 예시 결과
ROW_NUMBER() 무조건 1, 2, 3… 1, 2, 3
RANK() 동일값은 같은 순위, 다음 순위 건너뜀 1, 1, 3
DENSE_RANK() 동일값은 같은 순위, 다음 순위 건너뛰지 않음 1, 1, 2
반응형

+ Recent posts