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 분리로 이 셋을 한 번에 해결합니다.
(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 전체로 곱하면 차이가 더 커짐
케이스별 상세 비교
| Cause | Naive Rows | Naive Time | Naive Joins | dhub Rows | dhub Time | Joins | Speedup |
|---|---|---|---|---|---|---|---|
| 수요예측 과대 | 16.1M | 412s | 4 | 24 | 87ms | 1 | 4736x |
| ECO 미반영 | 28.0M | 678s | 4 | 32 | 124ms | 2 | 5468x |
| MOQ 과다발주 | 5.0M | 188s | 3 | 18 | 62ms | 1 | 3032x |
| EOL 처리 지연 | 8.1M | 245s | 3 | 12 | 41ms | 1 | 5976x |
| 법인간 불균형 | 6.0M | 287s | 3 | 8 | 95ms | 1 | 3021x |
| 주문 취소 | 10.0M | 156s | 1 | 200 | 38ms | 1 | 4105x |
| 합계 (50K SKU 전체 분류 추정) | 73.2M | 36분 6초 | 294 | 447 ms | 4,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