Post

Oracle SQL 실습 (1) - 부품 교체 이력 및 정비 이력 조회

Oracle SQL 실습 (1) - 부품 교체 이력 및 정비 이력 조회

🧾 시나리오 개요

차량에 장착된 부품의 교체 이력과, 해당 시점 전후의 정비 작업을 함께 조회하는 실습입니다.
아래 조건을 만족하는 SQL을 Oracle 스타일 전통 조인 방식으로 작성합니다.


🧩 주요 테이블 구성

테이블명역할
CAR차량 기준 정보
PART부품 기준 정보
CAR_PART_CHANGE_HIST부품 교체 이력 (메인)
CAR_PART_CHANGE_DETAIL부품 탈거/장착 시점
CAR_MAINTAIN_HIST차량 정비 작업 이력

🎯 조회 조건

  • CAR_PART_CHANGE_HIST.CREATED_TIME을 기준으로
    • 교체 직전 점검 작업 종료 시각
    • 교체 직후 점검 작업 시작 시각 을 함께 조회
  • 이전/현재 부품의 SN 및 탈거/장착 시각도 표시
  • Oracle 전통 조인 문법 사용

✅ 조회 SQL - 스칼라 서브쿼리 방식

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
SELECT 
  A.ID AS "부품교체이력ID",
  A.CAR_ID AS "차ID",
  C.NAME AS "차명",
  A.PART_ID AS "부품ID", 
  P.NAME AS "부품명",
  A.UNMOUNT_PART_SN AS "탈거부품SN", 
  A.MOUNT_PART_SN AS "장착부품SN", 
  A.REMARK AS "비고",
  TO_CHAR(A.CREATED_TIME, 'YYYY-MM-DD HH24:MI:SS') AS "(교체완료에 따른)이력생성시각",
  TO_CHAR(UNMOUNT.UNMOUNT_TIME, 'YYYY-MM-DD HH24:MI:SS') AS "(기존부품)탈거시각",
  TO_CHAR(MOUNT.MOUNT_TIME, 'YYYY-MM-DD HH24:MI:SS') AS "(새부품으로 교체)장착시각",

  -- 교체 직전 점검 종료 시각
  (SELECT TO_CHAR(MAX(MH.END_TIME), 'YYYY-MM-DD HH24:MI:SS')
     FROM CAR_MAINTAIN_HIST MH
    WHERE MH.CAR_ID = A.CAR_ID
      AND MH.END_TIME < A.CREATED_TIME
  ) AS "(부품 교체 전) 차량작업 완료시각",

  -- 교체 직후 점검 시작 시각
  (SELECT TO_CHAR(MIN(MH.START_TIME), 'YYYY-MM-DD HH24:MI:SS')
     FROM CAR_MAINTAIN_HIST MH
    WHERE MH.CAR_ID = A.CAR_ID
      AND MH.START_TIME > A.CREATED_TIME
  ) AS "(부품 교체 후) 차량작업 시작시각"

FROM 
  CAR_PART_CHANGE_HIST A,
  CAR C,
  PART P,
  CAR_PART_CHANGE_DETAIL UNMOUNT,
  CAR_PART_CHANGE_DETAIL MOUNT

WHERE 
  A.CAR_ID = C.ID
  AND A.PART_ID = P.ID
  AND A.UNMOUNT_PART_SN = UNMOUNT.PART_SN
  AND A.MOUNT_PART_SN = MOUNT.PART_SN

ORDER BY 
  A.CREATED_TIME;

🛠️ (보완) 인라인뷰 + 윈도우 함수 방식

스칼라 서브쿼리는 행마다 반복적으로 정비 이력 테이블을 조회하기 때문에, 정비 이력의 데이터 양이 많아질수록 성능 저하가 발생할 수 있습니다.

이를 개선하기 위해 ROW_NUMBER() 윈도우 함수와 인라인뷰를 활용해 각 교체이력 건당 정확히 1건의 직전/직후 정비 이력을 사전에 추출하고 조인하는 방식으로 변경하여 대량 데이터 조회시의 성능을 고려하여 보완하였습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
SELECT 
  A.ID AS "부품교체이력ID",
  A.CAR_ID AS "차ID",
  C.NAME AS "차명",
  A.PART_ID AS "부품ID", 
  P.NAME AS "부품명",
  A.UNMOUNT_PART_SN AS "탈거부품SN", 
  A.MOUNT_PART_SN AS "장착부품SN", 
  A.REMARK AS "비고",
  TO_CHAR(A.CREATED_TIME, 'YYYY-MM-DD HH24:MI:SS') AS "(부품교체 완료되어) 이력생성시각",
  TO_CHAR(UNMOUNT.UNMOUNT_TIME, 'YYYY-MM-DD HH24:MI:SS') AS "(기존부품)탈거시각" ,
  TO_CHAR(MOUNT.MOUNT_TIME, 'YYYY-MM-DD HH24:MI:SS') AS "(새부품으로 교체)장착시각",
  TO_CHAR(PREV.END_TIME, 'YYYY-MM-DD HH24:MI:SS') AS "(부품 교체 전) 차량작업 완료시각",
  TO_CHAR(NEXT.START_TIME, 'YYYY-MM-DD HH24:MI:SS') AS "(부품 교체 후) 차량작업 시작시각"
FROM 
  CAR_PART_CHANGE_HIST A,
  CAR C,
  PART P,
  CAR_PART_CHANGE_DETAIL UNMOUNT,
  CAR_PART_CHANGE_DETAIL MOUNT,
  (
    SELECT CHANGE_ID, CAR_ID, END_TIME, CREATED_TIME
    FROM (
      SELECT PH.ID AS CHANGE_ID,
             MH.CAR_ID,
             MH.END_TIME,
             PH.CREATED_TIME,
             ROW_NUMBER() OVER(PARTITION BY PH.ID ORDER BY MH.END_TIME DESC) AS RN
      FROM CAR_MAINTAIN_HIST MH,
           CAR_PART_CHANGE_HIST PH
      WHERE MH.CAR_ID = PH.CAR_ID
        AND MH.END_TIME < PH.CREATED_TIME
    )
    WHERE RN = 1
  ) PREV,
  (
    SELECT CHANGE_ID, CAR_ID, START_TIME, CREATED_TIME
    FROM (
      SELECT PH.ID AS CHANGE_ID,
             MH.CAR_ID,
             MH.START_TIME,
             PH.CREATED_TIME,
             ROW_NUMBER() OVER(PARTITION BY PH.ID ORDER BY MH.START_TIME ASC) AS RN
      FROM CAR_MAINTAIN_HIST MH,
           CAR_PART_CHANGE_HIST PH
      WHERE MH.CAR_ID = PH.CAR_ID
        AND MH.START_TIME > PH.CREATED_TIME
    )
    WHERE RN = 1
  ) NEXT
WHERE 
  A.CAR_ID = C.ID
  AND A.PART_ID = P.ID
  AND A.UNMOUNT_PART_SN = UNMOUNT.PART_SN
  AND A.MOUNT_PART_SN = MOUNT.PART_SN
  AND A.ID = PREV.CHANGE_ID(+)
  AND A.ID = NEXT.CHANGE_ID(+)
ORDER BY 
  A.CREATED_TIME;

📊 결과 예시

부품교체이력ID차명부품명탈거SN장착SN이력생성시각(부품 교체 전) 차량작업 완료시각(부품 교체 후) 차량작업 시작시각
CH01아반떼브레이크패드SN001SN0022025-01-15 10:00:002025-01-10 09:00:002025-01-20 14:00:00
CH02그랜저엔진오일SN003SN0042025-02-20 14:00:002025-02-18 11:00:002025-02-21 09:00:00

📂 테이블별 더미 데이터

🚗 CAR (차량 기준 정보)

IDNAME
CAR01아반떼
CAR02그랜저

🔩 PART (부품 기준 정보)

IDNAME
P01브레이크패드
P02엔진오일

📝 CAR_PART_CHANGE_HIST (부품 교체 이력)

IDPART_IDCAR_IDUNMOUNT_PART_SNMOUNT_PART_SNREMARKCREATED_TIME
CH01P01CAR01SN001SN002정기교체2025-01-15 10:00:00
CH02P02CAR02SN003SN004점검 중 교체2025-02-20 14:00:00

📦 CAR_PART_CHANGE_DETAIL (부품 탈거/장착 이력)

IDPART_IDPART_SNUNMOUNT_TIMEMOUNT_TIME
D01P01SN0012024-12-01 09:00:00 
D02P01SN002 2025-01-15 10:00:00
D03P02SN0032025-02-10 08:00:00 
D04P02SN004 2025-02-20 14:00:00

🛠️ CAR_MAINTAIN_HIST (차량 정비 이력)

IDCAR_IDWORK_TYPESTART_TIMEEND_TIMEWORKER
M01CAR01일상점검2025-01-10 08:00:002025-01-10 09:00:00김정비
M02CAR01부품검사2025-01-20 14:00:002025-01-20 15:00:00박정비
M03CAR02상시점검2025-02-18 10:00:002025-02-18 11:00:00이정비
M04CAR02부품교환 후 점검2025-02-21 09:00:002025-02-21 10:00:00김정비
This post is licensed under CC BY 4.0 by the author.