☰ 분류

데이터 파이프라인 결과가 틀렸을 때 추적하는 프롬프트

최종 숫자가 이상할 때 어느 단계에서 틀어졌는지 거슬러 올라갑니다.

분류개발 › 데이터·DB
태그분석검토개발자
프롬프트 (영어 본문 · 답은 한국어로 옵니다)
Trace this pipeline failure.

Approach — bisect by stage, not by reading code:
1. For each stage, the two numbers to capture: **row count in versus out, and the sum of the key measure in versus out.** A stage where either changes unexpectedly is your stage.
2. What each stage is *allowed* to change. Filters reduce rows legitimately; joins may multiply them illegitimately.
3. Order to check in, starting where a discrepancy is cheapest to detect.
4. Given my symptom, which stages it already rules out.

Then, by symptom shape:
- **Too high** — a join fanning out, a re-run that appended instead of replacing, double-counted late-arriving data
- **Too low** — an inner join dropping unmatched rows, a filter on a nullable column, a partition not yet loaded
- **Off by a constant** — timezone boundary, currency, unit
- **Off for some rows only** — type coercion, encoding, a code not present in a lookup table

For each candidate: the query that confirms or rules it out.

Rules:
- *Do not guess the cause from the description.* Give the check.
- Note where reprocessing would destroy the evidence, and what to snapshot first.
- Say whether the pipeline is idempotent, since if it is not, re-running to investigate makes it worse.
붙여 넣으면 맨 아래에 채울 칸(파이프라인 단계 · 어떻게 틀렸나 · 확인한 것)이 나옵니다

어떤 프롬프트인가

파이프라인은 에러 없이 틀린 값을 낸다. 이 프롬프트는 각 단계의 행 수와 합계를 대조해 어디서 값이 바뀌었는지 이분 탐색하듯 좁힌다.

모델 비교

ChatGPT가 가장 정확하고 완결적이다. Gemini는 구체적이지만 과도한 단정과 임의 날짜가 문제이며, [C]는 답이 없다.

ChatGPT종합 1위
43/ 50

+ 단계별 측정·허용 변화·증거 보존을 충실히 다뤘다.

- 전체 후보군별 쿼리까지 다루지 않아 다소 불완전하다.

Gemini
34/ 50

+ 유력 후보별 판정 기준과 확인 쿼리가 구체적이다.

- 근거 없이 원인·날짜를 단정하고 일부 가능성을 배제했다.

기준ChatGPTGemini앞선 모델
지시 준수97ChatGPT +29%
정확성95ChatGPT +80%
구체성98ChatGPT +13%
구성·가독성98ChatGPT +13%
분량 적정성76ChatGPT +17%

gpt-5.6-sol가 모델 이름을 가린 채 1~10점으로 채점했습니다 (2026-09-25). 측정값이 아니라 AI 심사 결과입니다.

답 전문 보기

같은 입력을 세 모델에 넣어 받은 답을 그대로 옮겼습니다. 각 CLI(에이전트 하네스)에서 실행했습니다. ChatGPT·Claude 앱이나 웹에서는 답이 다를 수 있습니다.

넣은 입력
파이프라인 단계: raw_orders(원본 수집 로그) → stg_orders(정제·중복 제거) → fct_orders(일별 배치 팩트 테이블) → BI 대시보드 집계

어떻게 틀렸나: 어제부터 fct_orders의 일별 매출 합계가 실제보다 약 1.8배 높게 찍힘. 어제 새벽 프로모션 이벤트 로그가 늦게 도착해서 재처리 스크립트를 한 번 더 돌렸음

확인한 것: raw_orders 행 수는 평소와 비슷한 수준인 것 확인함, stg_orders 중복 제거 로직 코드는 최근 변경 없음, 재처리 스크립트가 append인지 overwrite인지는 아직 확인 안 함
gpt-5.6-luna (codex CLI · 에이전트 하네스) · 실행일 2026-09-24

현재 증상만으로 원인을 단정할 수는 없습니다. 다만 재처리 후 fct 매출이 약 1.8배이므로, 가장 먼저 재처리의 append/overwrite와 fact 적재의 중복 여부를 확인해야 합니다.

1. 단계별로 먼저 캡처할 값

대상 날짜를 동일하게 맞춰 <D>로 표시합니다. 금액 컬럼은 <amount>, 주문 키는 <order_id>로 대체하세요.

단계 행 수 매출 합계
raw_orders COUNT(*) SUM(<amount>)
stg_orders COUNT(*) SUM(<amount>)
fct_orders COUNT(*) SUM(<amount>)
BI 원천 집계 COUNT(*) 또는 주문 수 SUM(<amount>)
BI 대시보드 가능하면 동일 필터의 매출 동일
SELECT
    COUNT(*) AS row_count,
    SUM(<amount>) AS amount_sum
FROM raw_orders
WHERE <order_date> = DATE '<D>';
SELECT
    COUNT(*) AS row_count,
    SUM(<amount>) AS amount_sum
FROM stg_orders
WHERE <order_date> = DATE '<D>';
SELECT
    COUNT(*) AS row_count,
    SUM(<amount>) AS amount_sum
FROM fct_orders
WHERE <order_date> = DATE '<D>';

각 단계의 값을 같은 실행 시점에 별도 저장하세요. 특히 재처리 전에 존재했던 fct 상태가 필요합니다.

2. 각 단계가 바꿔도 되는 것

  • raw_orders → stg_orders

    • 중복 제거로 행 수 감소: 가능
    • 유효하지 않은 행 필터링으로 행 수 감소: 가능
    • 정상적인 중복 제거라면 금액 합계도 줄 수 있지만, 제거된 주문 목록으로 설명 가능해야 함
    • 원본 한 건의 금액을 임의로 증가시키면 안 됨
  • stg_orders → fct_orders

    • 일별 집계라면 행 수 감소: 가능
    • 합계는 해당 일자의 유효한 주문 금액 합계와 같아야 함
    • 차원/lookup 조인으로 행 수가 늘면 위험
    • 재처리 시 기존 날짜 데이터를 교체해야 하며, 기존 데이터에 다시 더하면 안 됨
  • fct_orders → BI

    • 필터, 날짜 범위, 환율·통화 변환 등으로 값이 달라질 수 있음
    • 대시보드가 fact를 다시 조인해 행을 증식시키면 안 됨

3. 확인 순서

가장 저렴하게 확인할 순서는 다음과 같습니다.

  1. fct_orders에 재처리 배치가 두 번 들어갔는지 확인
  2. stg_orders → fct_orders 적재 쿼리의 append/overwrite 동작 확인
  3. fct_orders의 동일 주문 중복 확인
  4. fact 생성 쿼리의 조인별 행 수·금액 변화 확인
  5. stg_orders의 dedup 결과와 raw 대비 비교
  6. BI 집계 쿼리에서 fact가 다시 증식되는지 확인

raw 행 수가 평소와 비슷하다는 사실만으로는 raw 단계가 정상이라고 결론낼 수 없습니다. 행 수뿐 아니라 금액 합계, 주문 키 중복, 해당 날짜의 실제 입력 범위를 함께 확인해야 합니다.

4. 현재 증상이 이미 배제하는 것

현재 확인된 사실로 확실히 배제되는 것은 거의 없습니다.

  • raw 행 수가 평소와 비슷함
    → raw의 행 수 이상 가능성은 낮아 보이지만, 금액 중복·중복 이벤트·늦게 도착한 로그는 아직 배제하지 못합니다.
  • stg 중복 제거 코드가 변경되지 않음
    → 코드 변경에 의한 회귀 가능성은 낮아 보이지만, 입력 데이터 변화와 재실행 방식은 여전히 확인해야 합니다.
  • 1.8배 높음
    → 누락, nullable filter, 미로드 partition보다는 중복 적재나 조인 fan-out 계열을 우선 점검할 근거는 되지만, 이것도 원인 확정은 아닙니다.

5. 후보별 확인 쿼리

A. 재처리가 append되어 기존 fact를 다시 더했는지

배치 실행 이력이나 적재 메타데이터가 있다면:

SELECT
    <business_date>,
    <batch_id>,
    <run_id>,
    <load_mode>,
    COUNT(*) AS rows_loaded,
    SUM(<amount>) AS amount_loaded,
    MIN(<loaded_at>) AS first_loaded_at,
    MAX(<loaded_at>) AS last_loaded_at
FROM fct_orders
WHERE <business_date> = DATE '<D>'
GROUP BY <business_date>, <batch_id>, <run_id>, <load_mode>
ORDER BY last_loaded_at;

같은 날짜에 두 개의 적재 실행이 있고 둘 다 금액을 포함하면 append 가능성이 있습니다.

재처리 스크립트의 핵심 동작도 확인해야 합니다.

-- 위험한 형태
INSERT INTO fct_orders
SELECT ...
FROM stg_orders
WHERE <business_date> = DATE '<D>';

-- 날짜 파티션을 교체하는 형태인지 확인
DELETE FROM fct_orders
WHERE <business_date> = DATE '<D>';

INSERT INTO fct_orders
SELECT ...
FROM stg_orders
WHERE <business_date> = DATE '<D>';

파티션 교체, MERGE, overwrite 옵션 없이 단순 INSERT라면 재실행에 안전하지 않을 수 있습니다.

B. fact에 같은 주문이 두 번 들어갔는지

SELECT
    <order_id>,
    COUNT(*) AS cnt,
    SUM(<amount>) AS amount_sum
FROM fct_orders
WHERE <business_date> = DATE '<D>'
GROUP BY <order_id>
HAVING COUNT(*) > 1
ORDER BY cnt DESC;

재처리 실행별로 중복이 나뉘어 있다면:

SELECT
    <order_id>,
    COUNT(DISTINCT <run_id>) AS run_count,
    COUNT(*) AS row_count,
    SUM(<amount>) AS amount_sum
FROM fct_orders
WHERE <business_date> = DATE '<D>'
GROUP BY <order_id>
HAVING COUNT(DISTINCT <run_id>) > 1;

C. fact 생성 조인의 fan-out

각 조인을 한 번에 포함하지 말고 단계별로 측정합니다.

WITH base AS (
    SELECT <order_id>, <amount>
    FROM stg_orders
    WHERE <business_date> = DATE '<D>'
),
joined AS (
    SELECT
        b.<order_id>,
        b.<amount>,
        l.<lookup_key>
    FROM base b
    LEFT JOIN <lookup_table> l
      ON b.<join_key> = l.<join_key>
)
SELECT
    COUNT(*) AS row_count,
    COUNT(DISTINCT <order_id>) AS distinct_orders,
    SUM(<amount>) AS amount_sum
FROM joined;

COUNT(*)와 SUM(<amount>)가 조인 전보다 증가하고 COUNT(DISTINCT <order_id>)는 그대로라면 해당 조인이 fan-out입니다.

lookup 키 자체도 확인합니다.

SELECT
    <join_key>,
    COUNT(*) AS lookup_rows
FROM <lookup_table>
GROUP BY <join_key>
HAVING COUNT(*) > 1;

D. stg가 재처리 입력을 중복으로 보존했는지

SELECT
    <order_id>,
    COUNT(*) AS cnt,
    SUM(<amount>) AS amount_sum
FROM stg_orders
WHERE <business_date> = DATE '<D>'
GROUP BY <order_id>
HAVING COUNT(*) > 1;

raw와 stg의 금액을 비교합니다.

SELECT
    'raw' AS layer,
    COUNT(*) AS row_count,
    COUNT(DISTINCT <order_id>) AS distinct_orders,
    SUM(<amount>) AS amount_sum
FROM raw_orders
WHERE <business_date> = DATE '<D>'

UNION ALL

SELECT
    'stg',
    COUNT(*),
    COUNT(DISTINCT <order_id>),
    SUM(<amount>)
FROM stg_orders
WHERE <business_date> = DATE '<D>';

E. raw에 늦게 도착한 이벤트가 정상적으로 중복 포함됐는지

SELECT
    <order_id>,
    COUNT(*) AS raw_rows,
    MIN(<event_time>) AS first_event_time,
    MAX(<ingested_at>) AS last_ingested_at,
    SUM(<amount>) AS amount_sum
FROM raw_orders
WHERE <business_date> = DATE '<D>'
GROUP BY <order_id>
HAVING COUNT(*) > 1;

동일 주문의 재전송 이벤트인지, 실제 추가 주문 이벤트인지 이벤트 식별자와 상태 컬럼까지 비교해야 합니다.

F. BI에서 fact를 다시 증식시키는지

대시보드 쿼리를 그대로 떼어내어 fact 단독 집계와 비교합니다.

SELECT SUM(<amount>)
FROM fct_orders
WHERE <business_date> = DATE '<D>';
SELECT SUM(f.<amount>)
FROM fct_orders f
JOIN <bi_dimension_or_lookup> d
  ON f.<join_key> = d.<join_key>
WHERE f.<business_date> = DATE '<D>';

두 번째 값만 커지면 BI 조인의 fan-out입니다.

6. 재처리 전에 보존할 증거

다시 재처리하기 전에 다음을 별도 스냅샷 테이블이나 파일로 보존하세요.

CREATE TABLE investigation_fct_orders_<D> AS
SELECT *
FROM fct_orders
WHERE <business_date> = DATE '<D>';

가능하면 다음도 저장합니다.

  • 현재 fct_orders의 행 수, 주문 수, 금액 합계
  • 해당 날짜의 stg_orders 스냅샷
  • raw/stg/fct의 중복 주문 목록
  • 재처리 실행 ID, 시작·종료 시각, 실행 파라미터
  • 재처리 스크립트 버전과 실제 SQL
  • 해당 파티션의 생성·수정 시각

현재 상태를 보존하지 않고 overwrite나 delete 후 재처리하면, “재처리 전부터 잘못됐는지”와 “재처리 때문에 잘못됐는지”를 구분할 수 없게 됩니다.

7. 멱등성 판단

현재 정보만으로 이 파이프라인이 멱등적이라고 말할 수 없습니다. 다음 중 하나가 보장되어야 멱등적입니다.

  • 날짜 파티션을 매번 overwrite
  • MERGE가 안정적인 business key로 기존 행을 갱신
  • batch/run key에 unique 제약 또는 중복 방지 로직 존재
  • 재처리 시 동일 입력에 대해 동일한 최종 상태 생성

재처리 스크립트가 단순 INSERT INTO fct_orders SELECT ...라면 비멱등적이며, 조사를 위해 다시 실행할수록 매출이 더 증가할 수 있습니다. 따라서 먼저 fct_orders와 실행 메타데이터를 스냅샷한 뒤, append인지 overwrite인지 확인하는 것이 첫 번째 점검입니다.

같은 분류의 프롬프트

대용량 텍스트 변환기 프롬프트
AI 데이터 추출 도구 기획 프롬프트
AI2sql 자연어 SQL 생성 프롬프트
백엔드 아키텍트 역할을 맡기는 프롬프트
Base R 프로그래밍 프롬프트