title: "스크립트로 INSERT · UPDATE — 조리 시작/종료 기록" chapter: 5 images:
- script-insert-update.png
스크립트로 INSERT · UPDATE
이 장부터 .xms 스크립트에서 데이터베이스를 다룹니다.
시나리오는 빌트인 order_history 테이블에 조리 시작 시 1행 INSERT,
조리 종료 시 같은 행 UPDATE 입니다.
public/manual/gui/script-insert-update.png키 함수 두 가지
| 함수 | 용도 | 설명 |
|---|---|---|
RunSqlQuery(sql) | INSERT · UPDATE · DELETE · DDL | 단순 실행. 성공 여부 bool |
RunSqlQueryParam(sql, values) | 동일 + 파라미터 바인딩 | ? 자리표시자에 XArray 값 매핑. SQL Injection 방지 |
운영 코드는 항상 RunSqlQueryParam 을 사용합니다. SQL 안에 변수 값을 직접 문자열로 합치는 방식은
따옴표 처리 실수와 인젝션 공격에 취약합니다.
1) BeginOrder — 조리 시작 시 INSERT
order_history 의 start_time 컬럼은 default 가 CURRENT_TIMESTAMP 이므로 INSERT 시점에 자동으로 채워집니다.
스크립트는 order_no, menu_name 만 넘기면 됩니다.
FUNCTION BeginOrder(string orderNo, string menuName)
{
// ==========================================================================
// Module : 'OrderHistory'
// Caption : 조리 시작 시 order_history 행 추가
// start_time 은 default 로 자동 채워짐
// Return : bool (true = 성공, false = 실패)
// ==========================================================================
if( STR.IsNullOrWhiteSpace(/*text*/orderNo) )
{
Log($"!!!---BeginOrder : orderNo is empty");
return false;
}
XArray vals = XArray.Create1D(2);
vals[0] = orderNo;
vals[1] = menuName;
string sql =
"INSERT INTO order_history(order_no, menu_name)" +
" VALUES(?, ?)";
if( X.DB["local"].RunSqlQueryParam(sql, vals) == false )
{
LogError($"BeginOrder Error : {X.DB[\"local\"].LastError}");
return false;
}
Log($"BeginOrder OK : orderNo={orderNo}, menu={menuName}");
return true;
}호출 예
BeginOrder(/*orderNo*/$"{SYS.DateString}_{seq:D03}", /*menuName*/"떡볶이");seq 는 운영 측에서 발번해 주는 일련번호입니다. 날짜 + 일련번호 조합이면 식별이 쉽습니다.
2) EndOrder — 조리 종료 시 UPDATE
종료 시각, 계량 무게, 결과(OK/NG), 에러 플래그를 같은 행에 갱신합니다.
FUNCTION EndOrder(string orderNo, double weightG, string result, bool isError)
{
// ==========================================================================
// Module : 'OrderHistory'
// Caption : 조리 종료 시 동일 order_no 행을 갱신
// Return : bool
// ==========================================================================
string endTime = SYS.GetDateTimeStringFormat("yyyy-MM-dd HH:mm:ss");
XArray vals = XArray.Create1D(5);
vals[0] = endTime;
vals[1] = weightG;
vals[2] = result;
vals[3] = isError ? 1 : 0;
vals[4] = orderNo;
string sql =
"UPDATE order_history" +
" SET end_time=?, weight_g=?, result=?, is_error=?" +
" WHERE order_no=?";
if( X.DB["local"].RunSqlQueryParam(sql, vals) == false )
{
LogError($"EndOrder Error : {X.DB[\"local\"].LastError}");
return false;
}
Log($"EndOrder OK : orderNo={orderNo}, g={weightG}, r={result}");
return true;
}호출 예
EndOrder(/*orderNo*/orderNo,
/*weightG*/UnitData::UnitResultWeight[unitIndex],
/*result*/"OK",
/*isError*/false);파라미터 바인딩 규칙
RunSqlQueryParam 은 SQL 안의 ? 를 왼쪽부터 순서대로 XArray 의 [0], [1], … 와 매핑합니다.
XArray vals = XArray.Create1D(3);
vals[0] = 42; // INTEGER
vals[1] = 12.34; // REAL
vals[2] = "hello"; // TEXT
X.DB["local"].RunSqlQueryParam(
"INSERT INTO t(a, b, c) VALUES(?, ?, ?)", vals);| 스크립트 타입 | DB 매핑 |
|---|---|
int | INTEGER |
double | REAL |
bool | INTEGER (0/1) |
string | TEXT |
null | NULL |
날짜는 항상 문자열 로 전달합니다. ISO 8601 (yyyy-MM-dd HH:mm:ss) 권장.
자주 묻는 질문
Q. INSERT 후 자동 부여된 id 값을 받고 싶습니다.
다음 한 줄을 이어서 호출하면 됩니다.
int newId = X.DB["local"].RunSqlScalarInt("SELECT last_insert_rowid()");
Log($"newId={newId}");같은 트랜잭션 / 같은 연결에서 호출해야 정확한 값입니다.
Q. UPDATE 가 0건만 영향 줘도 성공으로 처리되나요?
RunSqlQueryParam 은 SQL 실행 자체가 정상이면 true 를 반환합니다.
영향받은 행 수까지 확인하려면 RunSqlQueryNonQuery 를 쓰십시오 — 행 수를 int 로 반환하며 실패 시 -1.
int affected = X.DB["local"].RunSqlQueryNonQueryParam(sql, vals);
if( affected <= 0 )
{
LogError($"EndOrder : no row matched - orderNo={orderNo}");
return false;
}Q. NULL 을 명시적으로 넣으려면?
XArray 요소에 null 을 대입하거나 (스크립트 측에서 가능한 경우), SQL 자체에서 NULL 리터럴을 사용합니다.
weight_g 처럼 default 가 0 인 컬럼은 “미입력” 과 “0” 을 구분하기 어려우므로,
구분이 필요한 컬럼은 default 를 두지 말고 NULL 허용으로 두는 편이 좋습니다.
학습 포인트
?자리표시자 +XArray값 → 파라미터 바인딩 이 운영 코드의 표준 형태입니다.- 함수 헤더 표준 주석 을 빼먹지 마십시오. 운영 중 누가 언제 만든 코드인지 추적할 수 있어야 합니다.
- 로그 두 줄을 항상 남깁니다 — 실패 시
LogError($"... : {LastError}"), 성공 시Log($"... OK"). 이 둘이 있으면 8장의OnDbError이벤트와 함께 운영 중 원인 추적이 쉬워집니다.
체크포인트
BeginOrder("test_001", "떡볶이")호출 → Data 탭에서 행이 보인다.EndOrder("test_001", 105.4, "OK", false)호출 → 같은 행의end_time/weight_g/result가 채워진다.- 일부러 잘못된 컬럼명으로 INSERT 해보면
LastError메시지에 SQLite 의 원인이 그대로 들어온다.