VCL How To - DataAcess 元件篇 |
|
bruce0211
版主 發表:157 回覆:668 積分:279 註冊:2002-06-13 發送簡訊給我 |
元件名稱:Table
文件版本:for BCB 5.0
文件作者:BCB 源碼任務 (http://home.kimo.com.tw/bruce0829) 2002/08/11 ●前言
Table 是資料庫應用程式裡最常用的物件(尤其是單機版的資料庫應用程式) , 用來連接實體 Table ,在今天的主題中我想好好的以單機版資料庫程式架構來介紹一下 Table 元件的應用 ●基本觀念
資料庫(DataBase)是一群資料表(Table)的集合 , 而資料表(Table)又是一群記錄(Record)的集合 , 而記錄(Record)再細分下去則是欄位(Field)的集合 ; 換個方式來講 , 一個進銷存管理系統的資料庫 , 是由廠商 Table , 客戶Table , 商品 Table , 銷售資料 Table 等等組成 , 而廠商 Table 又由多個廠商的記錄(Record)組成 , 每筆記錄又由廠商代碼 , 廠商名稱 , 廠商地址 , 電話等等欄位組成 ....
在單機版應用程式中 , 嚴格來說是沒有所謂的資料庫 (DataBase) 這一層 , 每一個 Table 都為一實體檔案 (如 *.DBF 或 *.DB 檔) , 若把進銷存管理系統所會用到的 Table 檔都放在某一目錄中 , 這個目錄我們就把它當作單機版的"資料庫"這一層... 要將程式連到一個實體的 Table , 需用 Table 這個元件來連接 , 有了以上的觀念 , 我們將 Table 的元件之 DatabaseName 屬性設為該實體 Table 檔的目錄位置 , 再將 Table 的元件之 TableName 屬性設為該實體 Table 檔檔名 , 然後執行 Table 元件的 Open() 方法 (或是將 Table 元件的 Active 屬性設為 true ) , 這個 Table 就被我們的程式連進來了 所有的新增刪除修改幾乎都是在 Table 這個元件上做動作 , 而 Table 元件是個執行時看不到的元件 ,所以其他如 DBEdit , DBGrid 等 [DataControl] 頁的元件則扮演"監視"的立場 , 將新增刪除修改的結果呈現到 GUI 上 ●基本 Table 狀態判斷
if (Table1->State == dsEdit) //如果是在編輯狀態就 ....
{
//............ Table1->Post(); //存檔並回到瀏覽狀態
} 補充 : 其它狀態有哪些請查 On Line Help 找關鍵字 "TDataSetState type"
常用的有 dsEdit (編輯狀態) , dsBrowse (瀏覽狀態) 另外 , 要判斷 Table 是否為空記錄狀態 :
if (Table1->Bof && Table1->Eof)
{
ShowMessage("Table1 內目前無記錄 !!");
} ●清空 Table 內所有記錄(record)
Table1->Active = false;
Table1->Exclusive = true; //以獨占模式開啟
Table1->Active = true; Table1->EmptyTable(); //清空資料 ●基本 Table 記錄指標控制
第一筆 : Table1->First();
前一筆 : Table1->Prior();
下一筆 : Table1->Next();
最後筆 : Table1->Last(); ●基本的新增,修改,刪除,查詢
□新增 Table1->Append(); 在原有記錄之最後一筆插入一筆空記錄 (較常用)
或
Table1->Insert(); 在目前記錄位置插入一筆空記錄 □刪除 if (!Table1->Bof && !Table1->Eof) // 若要由程式執行刪除動作 , 先判斷 Table 是否為空記錄再刪 ...(經驗)
{
Table1->Delete(); //執行刪除動作
} □修改 Table1->Edit(); //進入修改狀態
Table1->FieldByName("canceled")->AsInteger=0;
Table1->FieldByName("tran_serial")->AsString=tran_serial;
Table1->FieldByName("record_type")->AsString=temp.SubString(1,2);
.....
.....
Table->Post(); //存檔並回到瀏覽狀態 □查詢 常用的搜尋方法有兩種,一種是 FindKey方法,另一種是 Locate方法;FindKey 的方法在早期就已使用,現在只是為了相容性而存在,而且要使用FindKey方法來搜尋某鍵值,必須存在該鍵值的索引;而 Locate方法不一定需要索引(有則會自動引用),建議讀者盡量使用Locate方法,兩者方法都會在搜尋到鍵值時會將資料庫記錄指標停在找到的記錄上,並傳回一個 true 值 (1) FindKey() (要先設好 Table1 之索引鍵) 只搜尋"一個"鍵位值時
Table1->FindKey(new TVarRec(搜尋值), 0); 搜尋"複合鍵位"(兩個以上)值時
Table1->FindKey(OPENARRAY(TvarRec, (搜尋值1, 搜尋值2, 搜尋值3))) (2) Locate() 只搜尋"一個鍵位"值時 :
Table1->Locate(“欲搜尋之欄位名稱”, 搜尋值, TLocateOptions() << loPartialKey); 搜尋"複合鍵位"(兩個以上)值時 :
Variant locvalues[]={搜尋值1, 搜尋值2, 搜尋值3};
Table1->Locate("欄位名稱1; 欄位名稱2; 欄位名稱3", VarArrayOf(locvalues,(sizeof(locvalues)/16-1)),TLocateOptions() <<
loPartialKey); ●欄位資料的讀出處理
讀出 "字串型態" 欄位 : String Temp = Table1->FieldByName("Name")->AsString;
讀出 "邏輯型態" 欄位 : bool Sex = Table1->FieldByName("Sex")->AsBool;
讀出 "整數型態" 欄位 : int Age = Table1->FieldByName("Age")->AsInteger;
讀出 "浮點數型態" 欄位 : Float Weight = Table1->FieldByName("Weight")->AsFloat; 其它讀出資料值的方法參考 :
ShowMessage(Table1->FieldValues["ecr_no"]);
ShowMessage(Table1->FieldByName("ecr_no")->AsVariant);
ShowMessage(Table1->FieldByName("ecr_no")->Value); 或由 index 來找
ShowMessage(Table1->Fields->Fields[0]->Value); 另外 , 若要將某一欄位加 5 再回存
Table1->FieldByName("pc_cnt")->AsInteger = Table1->FieldByName("pc_cnt")->AsInteger + 5 ; 可寫成
Table1->FieldByName("pc_cnt")->AsInteger += 5; 但不可寫成
Table1->FieldValues["pc_cnt"] += 5 ●完整 Table 資料的讀出處理 □基本讀出 , 將 Table 從頭到尾掃過一遍 , 例如 : 將 "Name" 欄位的資料全部讀出來放到 Memo 中 String Temp;
Table1->First();
while (! Table1->Eof)
{
Temp = Table1->FieldByName("Name")->AsString;
Memo1->Lines->Add(Temp);
Table1->Next();
} □過濾資料讀出 , 例如 : 將符合條件 (當 "id" 欄位值等於 "1234567" ) 的 "Name" 欄位資料全部讀出來放到 Memo 中 String Temp;
Table1->Filter=" id ='"+ "1234567" +"'";
Table1->Filtered=true; if (Table1->FindFirst())
{
do {
Temp = Table1->FieldByName("Name")->AsString;
Memo1->Lines->Add(Temp); } while (Table1->FindNext()); } 說明 : 在經過過濾的資料中 , 要用 Table->FindFirst() 取代 Table1->First() 來找出第一筆記錄資料 , 另外也要用 Table1->FindNext() 取代 Table1->Next() 來改變記錄指標至下一筆記錄資料 ●增加資料讀出效率
在上一節中'完整 Table 資料的讀出處理',若要處理的資料記錄非常多,如數千或數萬筆以上時,在做迴圈處理時,由於 DBEdit , DBGrid 等 [DataControl] 元件會同時監測並顯示 Table 狀態 , 這會花費系統資源在更新 GUI 的動作上 ; 使用 Table 的 DisableConstraints() & EnableConstraints() 兩個方法 , 可避免掉這個情況 , 在做 Table 迴圈處理時 , 會暫時關掉 DataControl 的 GUI 連結 , 上節程式改寫如下 : String Temp; Table1->DisableConstraints(); //暫時關掉 GUI 連結
Table1->First();
while (! Table1->Eof)
{
Temp = Table1->FieldByName("Name")->AsString;
Memo1->Lines->Add(Temp);
Table1->Next();
}
Table1->EnableConstraints(); //重新打開 GUI 連結 ●以程式碼建立 Paradox Table
一般我們都是用程式去連一個現成存在的 Table 實體檔案 (*.DBF *.DB) , 然後對這個 Table 檔做維護的動作 (新增,刪除,修改,查詢) , 但我們是否可以反過來由程式建立實體 Table 檔呢 ? 當然可以 , 某些程式如 POS 系統 , 要將每天的業績資料獨立存放在一個獨立 Table 檔中 , 而 Table 的檔名就以日期為檔名 下面的程式碼會在您事先設好的 "My_Alias" BDE 別名路徑下,自動建立一個名叫 Plu.db 的資料表實體(若名稱中加入日期流水號 , 每執行一次就會產生一個 Table), 欄位有字串及整數型態 , 並以 PLU_NO 欄位當 Primery Key void __fastcall TForm1::Button1Click(TObject *Sender)
{ TTable *NewTable = new TTable(Form1);
NewTable->Active = false;
NewTable->TableType = ttParadox; //可自行更改 , 但一般都用 Paradox Table
NewTable->DatabaseName = "My_Alias"; // Table 實體檔案存放目錄 , 可自行更改
NewTable->TableName = "Plu.db"; //Table 實體檔案名稱 , 可自行更改 NewTable->FieldDefs->Clear();
NewTable->FieldDefs->Add("PLU_NO",ftString,16,true);
NewTable->FieldDefs->Add("TEN_CODE",ftString,4,false);
NewTable->FieldDefs->Add("DEPT_NO",ftString,8,true);
NewTable->FieldDefs->Add("PACK_AMT",ftInteger,0,true);
NewTable->FieldDefs->Add("PACK_QTY",ftInteger,0,true);
NewTable->FieldDefs->Add("UNIT_PRICE",ftInteger,0,true);
NewTable->FieldDefs->Add("SALE1",ftInteger,0,true);
NewTable->FieldDefs->Add("SALE2",ftInteger,0,true);
NewTable->FieldDefs->Add("SALE3",ftInteger,0,true); //設定以 PLU_NO 欄位當 Primery Key
NewTable->IndexDefs->Clear();
NewTable->IndexDefs->Add("","PLU_NO", TIndexOptions() << ixPrimary << ixUnique); NewTable->CreateTable();
NewTable->Free();
} ●其他相關參考 □PACK 一個 Paradox Table
BCB On Line Help 中的範例為 Delphi 版本 , 下由敝人改寫為 BCB 版本 //---------------------------------------------------------------------------
// <<使用方法>> if (ParadoxPack("AbcPSV","TOT")) ShowMessage("資料庫 TOT ...PACK 完成");
// <<注意事項>>
// 1. 在呼叫 ParadoxPack 前 , 連到 TOT 的 TTable 元件 Active 必須是 false
// 2. 第一個傳入參數一定要用資料庫別名 , 不能用路徑
// (如 ParadoxPack("C:\\AbcPSV\\DB","TOT")) 是錯誤的 )
//---------------------------------------------------------------------------
bool _ParadoxPack(char *Alias, char *TblName)
{ hDBIDb hDb ;
DBIResult dbResult ;
CRTblDesc TblDesc ;
bool r=false; //Initialize the BDE
DbiInit(null); //Open a Database
DbiOpenDatabase(Alias,null,dbiREADONLY,dbiOPENSHARED,null,0,null,null,hDb); memset((void *) &TblDesc, 0, sizeof(CRTblDesc));
lstrcpy(TblDesc.szTblName, TblName);
lstrcpy(TblDesc.szTblType, szPARADOX);
TblDesc.bPack = true;
dbResult = DbiDoRestructure(hDb, 1, &TblDesc, null, null,null, false);
if(dbResult == DBIERR_NONE) r=true; //Close Database
DbiCloseDatabase(hDb); return r; } □以程式設定資料庫別名(Alias) void __fastcall TForm1::FormCreate(TObject *Sender)
{
shortString MyAlias="MyAlias";
if (!Session->IsAlias(MyAlias))
{
Session->AddStandardAlias(MyAlias, ExtractFilePath(Application->ExeName)+"DB\\LocalDB", "PARADOX");
//Session->SaveConfigFile(); //不存也可,但程式結束,此臨時別名也會消失
ShowMessage("自動加入資料庫別名:"+MyAlias);
} □Paradox 資料庫的極限 在 BDE 之 Configuration 中設定 Native 驅動器 Paradox
之 BLOCK SIZE 可決定 paradox 資料表的容量
內定值是 2048 也就是 128MB
根據敝人親自測試
在一百萬筆商品主檔中
(約佔 350MB 磁碟空間 , 故 BLOCK SIZE
要設為 16384) , 搜尋一筆資料約只費 0.1 秒 ...
故適用在收銀機系統或圖書資料查詢等情況
在不需做複雜關聯的情況下
用其他大型資料庫根本是殺機用牛刀 ... BlockSize 對 Max FileSize 參考 :
當 BlockSize 設為 1024 時 Paradox 最大實體檔案 Size 為 64M
當 BlockSize 設為 2048 時 Paradox 最大實體檔案 Size 為 128M (default)
當 BlockSize 設為 4096 時 Paradox 最大實體檔案 Size 為 256M
當 BlockSize 設為 16384 時 Paradox 最大實體檔案 Size 為 1G
當 BlockSize 設為 32768 時 Paradox 最大實體檔案 Size 為 4G ●以上為筆者使用 Table 元件的經驗筆記 , 雖然 Table 元件還有很多方法未介紹 , 但上面的資料已足夠大部份資料庫軟體開發應用 ●完整之文字檔資料轉入 Table 範例
//宣告變數 char buff[65535]; AnsiString temp; //開啟一文字檔 以便檔做轉檔來源 ifstream infile; infile.open("c:\\test.txt"); if (!infile) return ; // 若不加這行 , 則當檔案不存在時會當機 //開啟一 Table 以便檔做轉檔目標 Table1->Close(); Table1->TableName="TOT.DB"; Table1->Open(); while (!infile.eof()) // 標準用法 { infile.getline(buff,sizeof(buff)); //將文字檔資料一行一行讀進來 //將讀入資料放到 temp 變數中 temp = buff; //新增一筆空紀錄以便編輯 Table1->Append(); //拆解 temp 字段以填入各個欄位 Table1->FieldByName("ten_code")->AsString=temp.SubString(1,6); Table1->FieldByName("ecr_no")->AsString=temp.SubString(8,6); Table1->FieldByName("sys_dttm")->AsString=temp.SubString(15,12); Table1->FieldByName("tran_cnt")->AsInteger=_StrToInt(temp.SubString(28,6)); Table1->FieldByName("kind_no")->AsString=temp.SubString(35,2); Table1->FieldByName("prj_no")->AsString=temp.SubString(38,8); Table1->FieldByName("sale_no")->AsString=temp.SubString(47,12); Table1->FieldByName("type")->AsString=temp.SubString(60,1); Table1->FieldByName("invoice_char")->AsString=temp.SubString(62,2); Table1->FieldByName("invoice_begin")->AsInteger=_StrToInt(temp.SubString(65,8)); Table1->FieldByName("invoice_end")->AsInteger=_StrToInt(temp.SubString(74,8)); Table1->FieldByName("unit_no")->AsString=temp.SubString(83,8); Table1->FieldByName("vip_no")->AsString=temp.SubString(92,19); Table1->FieldByName("pay_cash_amt")->AsInteger=_StrToInt(temp.SubString(112,9)); Table1->FieldByName("pay_eband_amt")->AsInteger=_StrToInt(temp.SubString(122,9)); Table1->FieldByName("pay_credit_amt")->AsInteger=_StrToInt(temp.SubString(132,9)); Table1->FieldByName("pay_present_amt")->AsInteger=_StrToInt(temp.SubString(142,9)); Table1->FieldByName("pay_cpn_amt")->AsInteger=_StrToInt(temp.SubString(152,9)); Table1->FieldByName("pay_bill_amt")->AsInteger=_StrToInt(temp.SubString(162,9)); Table1->FieldByName("pay_eat_amt")->AsInteger=_StrToInt(temp.SubString(172,9)); Table1->FieldByName("pay_group_amt")->AsInteger=_StrToInt(temp.SubString(182,9)); Table1->FieldByName("pay_ent_amt")->AsInteger=_StrToInt(temp.SubString(192,9)); Table1->FieldByName("pay_hu_amt")->AsInteger=_StrToInt(temp.SubString(202,9)); Table1->FieldByName("pay_other1_amt")->AsInteger=_StrToInt(temp.SubString(212,9)); Table1->FieldByName("pay_other2_amt")->AsInteger=_StrToInt(temp.SubString(222,9)); Table1->FieldByName("pc_cnt")->AsInteger=_StrToInt(temp.SubString(232,5)); Table1->FieldByName("tot_amt")->AsInteger=_StrToInt(temp.SubString(238,9)); Table1->FieldByName("invo_amt")->AsInteger=_StrToInt(temp.SubString(248,9)); Table1->FieldByName("pay_sub_per")->AsInteger=_StrToInt(temp.SubString(258,8)); Table1->FieldByName("pay_over_amt")->AsInteger=_StrToInt(temp.SubString(267,8)); Table1->FieldByName("pay_chg_amt")->AsInteger=_StrToInt(temp.SubString(276,8)); Table1->FieldByName("pay_add_amt")->AsInteger=_StrToInt(temp.SubString(285,8)); Table1->FieldByName("pay_sub_amt")->AsInteger=_StrToInt(temp.SubString(294,8)); Table1->FieldByName("pay_add_per")->AsInteger=_StrToInt(temp.SubString(303,8)); Table1->FieldByName("card_no")->AsString=temp.SubString(312,19); Table1->FieldByName("approval_no")->AsString=temp.SubString(332,9); Table1->FieldByName("card_type")->AsString=temp.SubString(342,2); Table1->FieldByName("del_flag")->AsString=temp.SubString(345,1); Table1->FieldByName("keep_amt")->AsInteger=_StrToInt(temp.SubString(347,8)); Table1->FieldByName("tax_amt")->AsInteger=_StrToInt(temp.SubString(356,8)); Table1->FieldByName("ftax_amt")->AsInteger=_StrToInt(temp.SubString(365,8)); Table1->FieldByName("rtax_amt")->AsInteger=_StrToInt(temp.SubString(374,8)); Table1->FieldByName("ytax_amt")->AsInteger=_StrToInt(temp.SubString(383,8)); //拆解完後存檔 Table1->Post(); } //關閉來源文字檔 infile.close(); //關閉 Table Table1->Close();發表人 - bruce0211 於 2002/08/11 21:50:23 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |