---
description: TradeDB/MariaDB 임시 조회 시 스키마 확인·PyMySQL % 이스케이프 필수 (실패 재시도 금지)
alwaysApply: true
---

# DB 임시 조회(adhoc) 안전 규칙

TradeDB/`database.py`로 SQL을 날릴 때 **추측 쿼리 금지**. 실패하면 같은 가정을 반복하지 말 것(토큰 낭비).

## 1. 쿼리 전에 스키마 확인 (필수)

모르는 테이블/컬럼이면 **먼저** `SHOW COLUMNS FROM <table>` 또는 `DESCRIBE <table>`.

대표 함정:

| 테이블 | 주의 |
|--------|------|
| `target_candidates` | **전략 컬럼 없음** (code/name/score/price/scan_time/updated_at + market/sector/theme). `strategy_id` SELECT 금지 |
| `target_candidates_history` | 컬럼은 `SHOW COLUMNS`로 확인 후 사용. CREATE 기본 DDL과 실제 DB가 다를 수 있음 |
| 전략별 후보 | history의 `strategy_id` 또는 별도 테이블/코드 경로를 문서·스키마로 확인 |

## 2. PyMySQL `%` 포맷 충돌 (필수)

`TradeDB.conn.execute()`는 pymysql이라 SQL 문자열의 `%`가 포맷으로 해석된다.

- ❌ `LIKE '20260712%'` / `LIKE '%BREAK%'` (단독 문자열에 `%`)
- ✅ 바인딩: `LIKE %s` + params `('20260712%',)`
- ✅ 또는 `%%` 이스케이프: `LIKE '20260712%%'`

`not enough arguments for format string` = 이 문제. 스키마 문제가 아님.

## 3. 실패 시 재시도 규칙

1. 에러 읽기 → 원인 분류(포맷 vs 컬럼없음 vs 테이블없음)
2. **스키마/바인딩 고친 뒤 1회만** 재실행
3. 같은 실패를 다른 날짜·다른 strategy 문자열로 반복 금지
4. 불확실하면 `database.py`의 CREATE/migrate/`get_*` 헬퍼를 읽고 그걸 쓰거나, 헬퍼에 없는 조회면 스키마 확인 후 작성

## 4. 최소 템플릿

```python
from database import TradeDB
db = TradeDB()
cols = [r["Field"] for r in db.conn.execute("SHOW COLUMNS FROM target_candidates_history").fetchall()]
# cols 확인 후 SELECT. LIKE는 반드시 %s 바인딩
rows = db.conn.execute(
    "SELECT slot_key, COUNT(*) n FROM target_candidates_history WHERE slot_key LIKE %s GROUP BY slot_key",
    ("20260712%",),
).fetchall()
```
