Database 매뉴얼 · Chapter 6
SELECT 패턴
읽기 SQL 은 두 가지 흐름으로 정리됩니다.
| 패턴 | API | 용도 |
|---|---|---|
| A) 단일 값 | RunSqlScalarInt(sql) 외 | COUNT(*) 같은 한 셀짜리 결과 |
| B) 일괄 SELECT | RunSqlSelect(sql) + RowCount + GetRowArray(i) / GetValue(i, col) | DataGrid 채우기, 다건 처리 |
샘플 프로젝트는 두 패턴을 모두 사용합니다.
패턴 A — 단일 값 조회
DB_Open() 의 빈 테이블 검사가 대표 예입니다.
int rowCnt = DB["local"].RunSqlScalarInt("SELECT COUNT(*) FROM order_history");
if( rowCnt == 0 )
{
DB_InsertInitialSamples();
}타입별 함수:
| 함수 | 반환 타입 |
|---|---|
RunSqlScalarInt(sql) | int |
RunSqlScalarDouble(sql) | double |
RunSqlScalar(sql) | string (만능) |
쿼리 결과가 비어 있으면 0 / 0.0 / "" 가 반환됩니다. 실패 여부는 LastError 로 판별합니다.
패턴 B — 일괄 SELECT (DB_Refresh)
샘플의 DB_Refresh() 함수가 표준 흐름을 보여줍니다.
FUNCTION DB_Refresh()
{
if( DB["local"].IsOpen == false )
{
LogError($"DB_Refresh : DB is not open");
return false;
}
string sql = "SELECT id, order_no, menu_name, start_time, end_time, weight_g, result, is_error FROM order_history ORDER BY id ASC";
if( DB["local"].RunSqlSelect(sql) == false )
{
LogError($"DB_Refresh select failed : {DB["local"].LastError}");
return false;
}
DispData.Clear();
int rows = DB["local"].RowCount;
for( i, 0, rows-1 )
{
// GetRowArray : 한 행을 컬럼 순서대로 XArray 로 반환
// CSV 한 줄(콤마 결합)이 DataGrid 한 행
array row = DB["local"].GetRowArray(/*row*/i);
string line = $"{row[0]},{row[1]},{row[2]},{row[3]},{row[4]},{row[5]},{row[6]},{row[7]}";
DispData.Add(line);
}
Log($"DB_Refresh : {rows} rows loaded");
return true;
}흐름 4 단계
RunSqlSelect(sql)— 결과를 메모리에 캐싱.false면 실패.RowCount— 캐시된 결과 행 수.GetRowArray(i)—i번째 행을 컬럼 순서대로array로.- 결과 변환 — 여기서는 CSV 한 줄로 묶어
DispData배열에 추가.
결과 캐시는 다음 SELECT 가 실행되거나 연결이 닫힐 때까지 유효합니다.
컬럼 단위 접근
행 단위가 아니라 셀 단위로 읽고 싶을 때:
DB["local"].RunSqlSelect("SELECT id, menu_name, weight_g FROM order_history WHERE id=?", p);
int id = DB["local"].GetValueInt(/*row*/0, /*colName*/"id");
string menu = DB["local"].GetValue(/*row*/0, /*colName*/"menu_name");
double w = DB["local"].GetValueDouble(/*row*/0, /*colName*/"weight_g");샘플의 DB_OpenModifyDlg (7 장에서 다룸) 가 이 패턴으로 선택 행의 모든 컬럼을
편집 필드(Edit*) 에 복사합니다.
| 함수 | 의미 |
|---|---|
GetValue(row, col) | 문자열로 (만능) |
GetValueInt(row, col) | 정수 |
GetValueDouble(row, col) | 실수 |
GetValueBool(row, col) | 불리언 |
row 는 0 부터 시작하는 인덱스, col 은 컬럼 이름 또는 인덱스 둘 다 허용됩니다
(이름 권장 — 컬럼 순서가 바뀌어도 안전).
자주 쓰는 SELECT 형태
샘플 도메인 기준 — 그대로 복붙해 시험해 보면 좋습니다.
// 1) 최근 50건 (DataGrid 표시용)
"SELECT id, order_no, menu_name, end_time, result " +
"FROM order_history ORDER BY id DESC LIMIT 50"
// 2) 결과별 집계
"SELECT result, COUNT(*) FROM order_history GROUP BY result"
// 3) 에러만
"SELECT id, order_no, menu_name FROM order_history WHERE is_error = 1"
// 4) 시간 범위
"SELECT id, order_no FROM order_history " +
"WHERE start_time >= ? AND start_time < ?"
// → RunSqlSelect 의 두 번째 인자로 [from, to] 배열 전달파라미터가 있는 SELECT
RunSqlSelect 도 두 번째 인자로 파라미터 배열을 받습니다 — INSERT/UPDATE 와 동일한 패턴.
array p[] = {""};
p.Clear();
p.Add(targetId);
DB["local"].RunSqlSelect("SELECT * FROM order_history WHERE id=?", p);
if( DB["local"].RowCount == 1 )
{
string menu = DB["local"].GetValue(0, "menu_name");
// ...
}자주 빠지는 함정
RowCount는 SELECT 직후에만 의미 있다 — INSERT / UPDATE 후에 다시 SELECT 를 돌려야 정확한 행 수를 얻습니다. 샘플은 모든 변경 함수가 끝날 때DB_Refresh()를 호출해 이 문제를 자동으로 해결합니다.- 컬럼명을 SELECT 에서 명시적으로 적자 —
SELECT *는 컬럼 순서가 환경마다 달라질 수 있어GetRowArray로 인덱스 접근할 때 위험합니다. - 빈 결과 —
RowCount == 0일 때GetValue(0, ...)는 빈 문자열을 반환하지만, 논리상 의미 없는 호출이므로 항상 가드해 주세요.
다음 챕터로
읽기/쓰기 코드가 모두 갖춰졌으면, 이 데이터를 화면에 띄울 차례입니다 — DispData 배열
하나만 변경해도 XDataGrid 가 자동으로 갱신되는 바인딩이 다음 챕터의 주제입니다.