AI가 작성한 정규표현식을 일치·불일치 예제로 검증하기
AI가 만든 정규표현식을 완성된 규칙이 아니라 초안으로 취급합니다. 작은 합성 테스트 표를 만들고 예상 결과와 실제 매칭 결과를 비교한 뒤, 수정된 패턴과 검토 보고서를 확인합니다.
AI가 만든 SQL을 완성된 답이 아니라 초안으로 취급하고, 정답을 알고 있는 작은 SQLite 데이터베이스에서 시험합니다. 실제 데이터에 적용하기 전에 AI 결과를 손으로 계산한 예상 표와 비교합니다.
이런 분께 맞아요AI로 SQL 초안을 만든 뒤 중요한 데이터에 적용하기 전에 작은 재현 가능한 테스트로 검증하려는 사람을 위한 안내입니다.
요구사항이 다음과 같다고 가정합니다. 모든 고객에 대해 2026년 8월에 발생한 PAID 주문의 총금액을 보고합니다. 조건에 맞는 주문이 하나도 없는 고객도 합계 0으로 반드시 포함해야 합니다.
AI가 customers와 orders를 INNER JOIN하고 WHERE에서 상태와 날짜를 필터링하는 SQL을 제안했다고 가정합니다. 문법적으로는 그럴듯하지만 INNER JOIN은 조건에 맞는 주문이 없는 고객을 제거합니다. 따라서 0원 고객도 포함해야 한다는 요구사항을 만족하지 못합니다.
아래 데이터베이스는 이 글을 위해 작성한 합성 데이터입니다. 고객 3명과 주문 6개로 구성되며, 결제 여부, 월 경계, 8월의 결제 완료 주문이 없는 고객을 모두 시험할 수 있도록 구성했습니다.
import sqlite3
from pathlib import Path
SOURCE_DIR = Path("outputs") / "ai_sql_demo"
DATABASE = SOURCE_DIR / "orders.sqlite"
CUSTOMERS = [
(1, "Ana"),
(2, "Ben"),
(3, "Cara"),
]
ORDERS = [
(101, 1, "2026-08-03", "PAID", 40),
(102, 1, "2026-08-20", "PAID", 60),
(103, 1, "2026-08-25", "CANCELLED", 30),
(104, 2, "2026-08-10", "PAID", 25),
(105, 2, "2026-09-01", "PAID", 50),
(106, 3, "2026-08-12", "CANCELLED", 80),
]
SOURCE_DIR.parent.mkdir(parents=True, exist_ok=True)
SOURCE_DIR.mkdir() # Stop if the synthetic source already exists.
connection = sqlite3.connect(DATABASE)
try:
connection.executescript("""
CREATE TABLE customers (
customer_id INTEGER PRIMARY KEY,
customer_name TEXT NOT NULL
);
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
order_date TEXT NOT NULL,
status TEXT NOT NULL,
amount INTEGER NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
""")
connection.executemany(
"INSERT INTO customers VALUES (?, ?)", CUSTOMERS
)
connection.executemany(
"INSERT INTO orders VALUES (?, ?, ?, ?, ?)", ORDERS
)
connection.commit()
finally:
connection.close()
python create_ai_sql_demo.pyAna는 8월 PAID 주문이 40과 60 두 건이므로 합계는 100입니다. CANCELLED 주문 30은 제외합니다. Ben은 8월 PAID 주문 25 한 건만 해당하며 9월 주문 50은 제외합니다. Cara는 8월 주문이 있지만 CANCELLED 상태뿐이므로 반드시 0으로 표시되어야 합니다.
| customer_id | customer_name | 예상 8월 PAID 합계 |
|---|---|---|
| 1 | Ana | 100 |
| 2 | Ben | 25 |
| 3 | Cara | 0 |
예상 전체 합계는 125입니다. 요구사항에 모든 고객을 포함한다고 했으므로 출력 행은 정확히 3개여야 합니다.
아래 AI SQL은 INNER JOIN 뒤 WHERE에서 주문 조건을 필터링합니다. Cara에게는 조건을 만족하는 8월 PAID 주문이 없으므로 결과에서 완전히 사라집니다.
SELECT
c.customer_id,
c.customer_name,
SUM(o.amount) AS august_paid_total
FROM customers AS c
JOIN orders AS o
ON o.customer_id = c.customer_id
WHERE o.status = 'PAID'
AND o.order_date >= '2026-08-01'
AND o.order_date < '2026-09-01'
GROUP BY c.customer_id, c.customer_name
ORDER BY c.customer_id;수정 SQL은 모든 고객에서 시작해 LEFT JOIN을 사용합니다. 주문 필터를 ON 조건 안에 두므로 조건에 맞는 주문이 없는 고객도 남습니다. 집계 결과가 NULL이면 COALESCE로 0으로 바꿉니다.
SELECT
c.customer_id,
c.customer_name,
COALESCE(SUM(o.amount), 0) AS august_paid_total
FROM customers AS c
LEFT JOIN orders AS o
ON o.customer_id = c.customer_id
AND o.status = 'PAID'
AND o.order_date >= '2026-08-01'
AND o.order_date < '2026-09-01'
GROUP BY c.customer_id, c.customer_name
ORDER BY c.customer_id;다음 스크립트를 ai_sql_check.py로 저장하세요. 두 SQL을 실행하고 결과를 손으로 계산한 예상 표와 비교한 뒤 검토용 CSV를 작성합니다. 합성 데이터베이스는 수정하지 않습니다.
import csv
import sqlite3
from pathlib import Path
DATABASE = Path("outputs") / "ai_sql_demo" / "orders.sqlite"
OUTPUT_DIR = Path("outputs") / "ai_sql_check_result"
REPORT = OUTPUT_DIR / "sql_check.csv"
AI_SQL = """
SELECT c.customer_id, c.customer_name, SUM(o.amount)
FROM customers AS c
JOIN orders AS o ON o.customer_id = c.customer_id
WHERE o.status = 'PAID'
AND o.order_date >= '2026-08-01'
AND o.order_date < '2026-09-01'
GROUP BY c.customer_id, c.customer_name
ORDER BY c.customer_id;
"""
REVISED_SQL = """
SELECT c.customer_id, c.customer_name, COALESCE(SUM(o.amount), 0)
FROM customers AS c
LEFT JOIN orders AS o
ON o.customer_id = c.customer_id
AND o.status = 'PAID'
AND o.order_date >= '2026-08-01'
AND o.order_date < '2026-09-01'
GROUP BY c.customer_id, c.customer_name
ORDER BY c.customer_id;
"""
EXPECTED = [
(1, "Ana", 100),
(2, "Ben", 25),
(3, "Cara", 0),
]
def run_query(connection, sql):
return connection.execute(sql).fetchall()
def main() -> None:
if not DATABASE.is_file():
raise FileNotFoundError(f"Database not found: {DATABASE}")
if OUTPUT_DIR.exists():
raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")
connection = sqlite3.connect(f"file:{DATABASE.resolve()}?mode=ro", uri=True)
try:
ai_rows = run_query(connection, AI_SQL)
revised_rows = run_query(connection, REVISED_SQL)
finally:
connection.close()
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir()
with REPORT.open("x", encoding="utf-8", newline="") as stream:
writer = csv.writer(stream)
writer.writerow(["query", "rows", "matches_expected"])
writer.writerow(["AI", repr(ai_rows), ai_rows == EXPECTED])
writer.writerow(["REVISED", repr(revised_rows), revised_rows == EXPECTED])
print(f"Expected rows: {len(EXPECTED)}.")
print(f"AI rows: {len(ai_rows)}; matches expected: {ai_rows == EXPECTED}.")
print(
f"Revised rows: {len(revised_rows)}; "
f"matches expected: {revised_rows == EXPECTED}."
)
print(f"Report: {REPORT.as_posix()}")
if revised_rows != EXPECTED:
raise RuntimeError("Revised SQL does not match the expected result.")
if __name__ == "__main__":
main()
python ai_sql_check.pyAI SQL은 Ana와 Ben만 반환하므로 행이 2개이며 예상 표와 일치하지 않아야 합니다. 수정 SQL은 고객 3명을 모두 반환하고 손으로 계산한 표와 정확히 일치해야 합니다.
| SQL | 반환 행 | 예상 전체 합계 | 예상 표와 일치 |
|---|---|---|---|
| AI | 2 | 125 | 아니오 |
| 수정 | 3 | 125 | 예 |
AI 결과는 Cara의 필수 0 행이 빠졌는데도 전체 합계는 125로 맞을 수 있습니다. 따라서 전체 합계만 확인하는 것으로는 충분하지 않습니다.
Expected rows: 3.
AI rows: 2; matches expected: False.
Revised rows: 3; matches expected: True.
Report: outputs/ai_sql_check_result/sql_check.csv| 자주 발생하는 문제 | 확인할 사항 |
|---|---|
| 예상보다 행이 적음 | JOIN 종류와 WHERE 조건이 LEFT JOIN의 일치하지 않는 행을 제거하는지 확인하세요. |
| 합계가 너무 큼 | 집계 전에 일대다 JOIN으로 행이 중복되는지 확인하세요. |
| 날짜 범위 오류 | 저장된 날짜 형식에 맞는 명시적인 시작 경계와 배타적 종료 경계를 사용하세요. |
| 0 대신 NULL | 일치 항목이 없을 때 NULL을 유지할지 COALESCE로 0으로 바꿀지 결정하세요. |
| 전체 합계는 맞지만 상세가 틀림 | 전체 합계뿐 아니라 예상 행 전체를 비교하세요. |
이 테스트는 수정 SQL이 이 작은 합성 예제와 일치한다는 것만 보여줍니다. 실제 스키마에는 중복 관계, NULL, 타임스탬프, 시간대, 환불, 추가 업무 규칙이 있을 수 있습니다. 그런 조건이 중요하다면 합성 테스트 데이터에도 추가해야 합니다.
2026-09-20 · 예제 수동 검토 · 대상: Python 3.12 · 표준 라이브러리: csv, pathlib, sqlite3 · 실행하지 않음
설명과 예제는 직접 작성했습니다. 관련 동작과 개념은 아래 공식 자료에서 확인할 수 있습니다.