Database 매뉴얼 · Chapter 6

SELECT 패턴

읽기 SQL 은 두 가지 흐름으로 정리됩니다.

패턴API용도
A) 단일 값RunSqlScalarInt(sql)COUNT(*) 같은 한 셀짜리 결과
B) 일괄 SELECTRunSqlSelect(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 단계

  1. RunSqlSelect(sql) — 결과를 메모리에 캐싱. false 면 실패.
  2. RowCount — 캐시된 결과 행 수.
  3. GetRowArray(i)i 번째 행을 컬럼 순서대로 array 로.
  4. 결과 변환 — 여기서는 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 가 자동으로 갱신되는 바인딩이 다음 챕터의 주제입니다.