How It Works

방법론 — 어떻게 분류하는가

5천만 건 트랜잭션에서 부진재고 원인 6가지를 어떻게 자동 분류하는지, 왜 이게 어려운 문제인지, 그리고 dhub 의 ontology + agent 가 이 문제를 어떻게 푸는지.
왜 이게 어려운 문제인가
부진재고 원인 분류는 3가지 어려움이 동시에 걸린 문제입니다.
(1) 다중 테이블 인과 결합 — 단일 SKU 분류에 평균 5개 테이블, 1.2M 행 조인 필요.
(2) 시간/인과 추론 — "ECO 발생 후에 R3 입고가 있었나" 같은 시간 순서 조건이 인덱스에 안 잡힘.
(3) 의미적 분류 — 임계치/비즈니스 룰이 SQL 안에 박히면 매번 코드 수정 필요.

SAP BI 만으로 안 되고, Tableau 만으로 안 되고, 자체 ML 만으로도 안 되는 이유입니다. dhub 는 Ontology + Agent 분리로 이 셋을 한 번에 해결합니다.
01

Ontology — 데이터의 의미적 그래프

dim 8개 + fact 6개 = 14개 entity 와 그 사이 관계. 한 번 정의하면 6개 cause agent 모두 그 위에서 그래프 traversal.
Dimension (8) Fact (6)
노드 크기 = log(rows) · 클릭 → schema 보기 · 드래그/휠 = 회전/줌
🖱️ 12 entities · 17 relationships
왼쪽 노드를 클릭하면
스키마와 관계가 표시됩니다
02

6개 Cause Detector Agents

각 agent 는 Ontology 위에서 정해진 entity 들을 traverse 하고, 룰과 임계치로 분류 결정. 슬라이더로 임계치를 조정해보세요 — live detection 수가 변합니다.
FORECAST_BIAS
forecast-bias-detector
수요예측 과대
live detections (24h)
23
Input Entities (4)
MaterialDemandForecastSalesOrderProductionOrder
Rule
24개월 평균 forecast 가 실수요 대비 임계치 이상일 때 → 그 결과로 PO/PROD 가 부풀려진 SKU
SQL-like Logic
WITH bias AS (
  SELECT product_code, AVG(forecast_qty) / NULLIF(AVG(actual_demand_qty),0) AS r
  FROM fact_demand_forecast
  GROUP BY product_code
)
SELECT product_code FROM bias WHERE r > {bias_threshold}
  AND EXISTS (... over-production check ...)
Thresholds — 슬라이더로 조정해보세요
bias_threshold1.5 x
1.15
min_months6 개월
324
ECO_LEFTOVER
eco-leftover-detector
ECO 미반영
live detections (24h)
14
Input Entities (4)
MaterialECOProductionOrderInventoryMovement
Rule
ECO 발생 이후 시점에 구형 revision 으로 GR(입고) 또는 생산이 발생했는지 시간 순서로 판정
SQL-like Logic
SELECT m.product_code FROM fact_inventory_movement m
JOIN dim_eco e ON m.product_code = e.product_code
WHERE m.movement_date > e.eco_date
  AND m.product_revision = e.old_revision
  AND m.movement_type = 'GR'
Thresholds — 슬라이더로 조정해보세요
min_leftover_qty100 단위
05000
days_after_eco30
0365
MOQ_OVERORDER
moq-overorder-detector
MOQ 과다발주
live detections (24h)
11
Input Entities (4)
MaterialPurchaseOrderSupplierSalesOrder
Rule
공급사 MOQ 가 비정상적으로 크고 (high_moq_flag) 실제 발주가 그 MOQ 그대로 들어가 실수요 대비 과다
SQL-like Logic
SELECT po.product_code FROM fact_purchase_order po
JOIN dim_supplier s ON po.supplier_id = s.supplier_id
WHERE s.high_moq_flag = 1
  AND po.order_qty >= s.min_order_qty
  AND po.order_qty > {ratio_threshold} * monthly_demand
Thresholds — 슬라이더로 조정해보세요
ratio_threshold6 개월치
224
min_moq5000 단위
10050000
EOL_DELAY
eol-delay-detector
EOL 처리 지연
live detections (24h)
7
Input Entities (3)
MaterialPurchaseOrderProductionOrder
Rule
lifecycle_stage = EOL 인데도 그 이후 PO 또는 생산이 계속 발생
SQL-like Logic
SELECT p.product_code FROM dim_product p
WHERE p.lifecycle_stage = 'EOL'
  AND EXISTS (
    SELECT 1 FROM fact_purchase_order po
    WHERE po.product_code = p.product_code
      AND po.po_date > p.eol_planned_date - {grace_days}
  )
Thresholds — 슬라이더로 조정해보세요
grace_days30
0180
min_event_count1
120
CROSS_LE_IMBAL
cross-le-imbalance-detector
법인간 불균형
live detections (24h)
19
Input Entities (4)
MaterialInventorySnapshotCompanySalesOrder
Rule
한 법인에 재고 70% 이상 집중 vs 다른 법인은 부족. 같은 SKU 가 LE 간 미이체로 묶임
SQL-like Logic
WITH dist AS (
  SELECT product_code, company_code,
         SUM(on_hand_qty) AS qty,
         SUM(SUM(on_hand_qty)) OVER (PARTITION BY product_code) AS total
  FROM fact_inventory_snapshot
  GROUP BY product_code, company_code
)
SELECT product_code FROM dist
WHERE qty / total > {concentration_threshold}
Thresholds — 슬라이더로 조정해보세요
concentration_threshold0.6 비중
0.40.95
min_total_qty1000 단위
10050000
ORDER_CANCEL
order-cancel-detector
주문 취소
live detections (24h)
9
Input Entities (3)
MaterialSalesOrderCustomer
Rule
특정 SKU 의 cancelled_flag = 1 비율이 임계치 초과. 대량 발주 후 취소 패턴
SQL-like Logic
SELECT product_code,
       AVG(CAST(cancelled_flag AS FLOAT)) AS cancel_rate
FROM fact_sales_order
GROUP BY product_code
HAVING cancel_rate > {cancel_rate_threshold}
Thresholds — 슬라이더로 조정해보세요
cancel_rate_threshold0.15 비율
0.050.5
min_orders20
5200
03

Performance — Naive SQL vs dhub Ontology

동일 분석을 두 방식으로 돌렸을 때의 차이. 50K SKU 전체 분류 기준.
Naive SQL 접근
36분 6초
73.2M rows scanned · 다중 풀 테이블 스캔
dhub Ontology 접근
447 ms
294 rows · 인덱스 그래프 traversal
차이
4,847x faster
248,930x less data scanned
6개 cause 분류 시간 (ms · 로그 스케일)
한 SKU 분류 1회 기준. 50K SKU 전체로 곱하면 차이가 더 커짐
케이스별 상세 비교
CauseNaive RowsNaive TimeNaive Joinsdhub Rowsdhub TimeJoinsSpeedup
수요예측 과대16.1M412s42487ms14736x
ECO 미반영28.0M678s432124ms25468x
MOQ 과다발주5.0M188s31862ms13032x
EOL 처리 지연8.1M245s31241ms15976x
법인간 불균형6.0M287s3895ms13021x
주문 취소10.0M156s120038ms14105x
합계 (50K SKU 전체 분류 추정)73.2M36분 6초294447 ms4,847x faster
왜 이렇게 차이가 나는가?
Naive SQL 의 문제
  • • 매 cause 마다 풀 테이블 스캔 (5M~25M 행)
  • • 시간 윈도우 조건이 인덱스에 안 잡힘 (range query)
  • • 6개 cause = 6개 별도 쿼리 → 같은 데이터 6번 읽음
  • • 임계치 변경 시 모든 쿼리 다시 작성/배포
  • • 결과 lineage 추적 안 됨 (재현 불가)
dhub Ontology 의 우위
  • • Material 노드 기준 그래프 traversal (1~2 hop)
  • • 관계가 사전 인덱싱됨 → O(log n) 접근
  • • 6개 agent 가 같은 데이터 한 번 읽고 분기
  • • 임계치는 agent metadata 변경만 (배포 X)
  • • 모든 분류 결정에 자동 lineage trace 생성
Powered by dhub Ontology + Agent runtime14 entities · 17 relationships · 6 active agents