2009/04/29

DBeXpress 的 RecordCount

DBX中的 SQLTable / SQLQuery / SQLDataSet 都有支援「RecordCount」函式(統計資料集合現有多少筆數)

實際在用的時候發現並不是這麼好用,常常 run 到該處就發生例外錯誤(Exception)

不少人認為這是DBX的BUG...

最近閒來沒事在查這方面的資訊,發現原來BCB的HELP中有解釋不能應用的場合

原來DBX的元件是由「TCustomSQLDataSet」繼承「TDataSet」而來

然「TCustomSQLDataSet」有改寫 RecordCount 的些許內容,一般簡易查詢依然可以利用
但如果遇到以下情況則會跳出例外錯誤:

1. The dataset represents stored procedure.
1. 資料集為 Stored Procedure 表示式

2. The dataset represents a query that contains parameters.
2. 資料集為帶有參數查詢的表示式

3. The dataset represents a multi-table join.
3. 資料集為多表合併的表示式

不過HELP中並沒有提到這樣的限制的理由 (汗)

解決的方式有以下兩種:
1. 利用ClientDataSet來處理RecordCount
2.使用SQL的COUNT(*)來統計

以上,做個小參考吧~~~(笑)

2009/04/28

DBeXpress的一些觀念

話說DBeXpress是B社強力主推的軟技術之一
但鮮少有資料可供參考

討論它的專書繁中+簡中的…大約4本吧
而有提到的書籍,無一不說DBX的效能好,但…

說效能不錯,那是有條件的!

就先拿BDE來說好了,B社資料連結技術的開山元祖
速度快不快,說真的,在單機架構中,應該是沒有任何的引擎比它快
(就連最新的DBX4也才只比BDE快0.X秒)

缺點:
封裝麻煩

說真的,BDE除了這點外,大概沒其它問題(…是我沒用到過)

而DBX呢?
速度嘛…M$SQL輸ADO(大),單機輸BDE(超大),C/S則還需看BDE的SQL LINK及ODBC效能才有機會勝出…
DBX的效能…或許在5~6版之後能真正超越BDE吧
那,DBX的優勢在哪邊?
1.封裝簡單
  只需要把 MIDAS 及 DBX+DB Client所需的DLL包在一起,直接封裝即可,快速簡單
2.架構明確
  以往習慣BDE的B社愛好者(被逼?),一但必須把原1~2-TIER的架構晉升為N-TIER時,往往會因架構落差太大導致轉換不易
  而DBX採行的則是「準n-tier」架構,讀取資料時採單向取出,寫入資料時則交由DATASNAP方式寫入
各司其職,取「單向取出」及「MIDAS寫入」二者優點,得到最大效能
(慢就是慢在MIDAS,所以效能慢也不能全怪DBX)

提了這麼多,那DBX的架構是什麼東西?

DBX元件 + DataSetProvider + ClientDataSet

這就是完整的DBX架構

某書曾寫DBX + StringGrid的方式簡直是大誤......

N-TIER所用到的元件則是…

(BDE / ADO / DBX) + DataSetProvider |<-Server 切 Client->| (DCOM / Socket) + ClientDataset

如何?有沒有像一點?如此切開就成了n-tier架構

接下來要討論的課題是
這樣的DBX要怎麼在專案中設計?
以下的內容不一定是正規寫法,也歡迎提出討論

1.視時機使用SQLTable/SQLQuery及ClientDataSet操作資料存取元件
  這種方式看起來沒啥不好,在c/s設計上也沒有問題,但在未來移轉到n-tier時,需要變更的地方可不少
(也就是說,仍採用與BDE相同的設計模式)

2.將ClientDataSet直接取代成TTable / TQuery來使用
  把SQLTable及DataSetProvider藏在DataModule內,完全使用ClientDataSet操作
這樣的方式更能貼近N-TIER的程式寫法,若未來有升級的空間,可考慮這樣的寫法
但在1~2-TIER上設計時往往會有脫褲子放屁的感覺
再者,由於大部份的情況都在DataSetProvider做in/out動作,效率上會顯得更為慢些
(在單機的情況來說,這並不明顯)

在設計性與未來延伸性來說,會是設計師的一門課題
不過也因為這個原因,讓資料庫程式設計變得富有趣味性及變化性。

2009/04/11

在史瓦濟蘭九天的回憶錄(一)

話說去史瓦濟蘭,到底是好,還是不好?
說真的,沒一天好,沒有一天是好的!
(這就是愛台灣啦!哈哈!)

因為敝公司的業務平日都有在做面子,所以在那邊真的很能感受客戶對我們的善意
也看到了當地的好人,當然,壞人也不少啦,但與南非比起來,史國民情算是相當優良的了
出發第一天從台灣出發,飛到香港後,等了3小時才搭上預計的班機,當時是晚上,而南非當地則是白天。坐上飛機,發現座位旁沒人,心想真幸運,但沒想到之後坐在我旁邊的居然是位高胖的黑人(多胖?185cm / 120公斤有吧),就是坐著還會卡到我,真是大隻……
抵達南非時的機上照片
飛機飛得很快,平均是900km / hr,我覺得就像上演現代版夸父追日一般,當然,太陽沒被我們追到,連月亮都跑得比我們還要快,到最後,我們又被太陽給追上了,呵!
「這邊是德本機場!」不遠處的香港旅行團團員盼盼在討論這件事情
呃!?這跟我們預計的不太一樣,我們不是應該到約翰尼斯堡嗎?
之後的廣播告知:因南非天候不佳,必須先到德本機場等候,等天氣好一點,再飛到目的地。
之後我就開始觀查旁邊的大隻佬,呼吸聲真大…下飛機時還會把垃圾丟到我位置前的置物袋……是我太大驚小怪嗎?但…好像是當地的民族性…或許吧

2009/04/08

[轉貼]心得分享 : 參數檔 (INI 檔) 處理

來源:心得分享 : 參數檔 (INI 檔) 處理


INI 檔應用的歷史從 Windows 3.1就已存在,雖然它也是文字檔的一種,但它利用「節區(Section)」及「識別字(KeyWord)」觀念,類似資料庫的索引,所以用來當作少量 變數資料的儲存器非常適合,除變數管理容易外尚可做程式流程控制,市售套裝軟體有用 ini 檔來控制其軟體版本例子,其主執行檔可能只有一個,但依 INI 檔設定可開放其全功能版(豪華版)與非全功能版;INI檔案 Size 雖然有 64k的限制,但已足夠存放上百個變數資料;在 32 位元系統上,正統取代 INI 機能的方法為使用 Windows Regist,但為了儲存Windows 作業系統本身的資訊,Regist已夠龐大,如果我們每個應用程式又去 Regist內挖一塊空間當作自己儲藏變數或參數的地方,會讓 Regist變得更龐大更複雜,所以筆者還是建議每個程式都使用自己的INI檔來儲存自己的變數,況且 INI檔不會被淘汰,連 Linux Kylix 下都看得到其蹤影

■ 應用實例

假設程式中需要紀錄目前發票號碼(invo_no)、交易序號(tran_no),而每次交易完都要將這兩個變數值加1,若程式從頭到尾都不關機,那沒有 問題,程式會記錄目前的發票號碼及交易序號已排到幾號;但若程式中途有關機後,下次開機,程式怎知發票號碼及交易序號已排到幾號?所以除了將變數資料存於 資料庫中外,更簡單的方法就是使用INI檔來記錄變數值,在程式重新啟動時再將變數值取回,所以該INI檔內容可能長得如下:

[Varible]
invo_no=00000005
tran_no=3

其中,用中括號刮起來的Variable就是「節區(Section)」名稱(可自訂),而其內的 INVO_NO、TRAN_NO則稱為「識別字(KeyWord)」(可自訂);不可有相同名稱的兩個節區(Section),但不同的節區中可有相同名 稱的識別字(KeyWord),取用時按節區(Section)索引分開取用,如下圖:

[Varible]
invo_no=00000005
tran_no=3

[Varible_2]
invo_no=00000007
tran_no=5

■ 程式實例

接下來我們介紹使用 INI 檔來儲存或取回變數的方法,在程式中要使用 INI 檔有幾個步驟:

1.程式開頭必須 #include "inifiles.hpp"
2.程式中首先需建立一個 INI 物件以對應實際的 INI 檔,不用時再釋放這個 INI 物件,通常 INI 檔實 體與主執行檔存在同一路徑,甚至檔名也和主執行檔相同,但副檔名則為 *.ini
3.使用 INI 物件的某些方法(Method)來存取變數,取用的方法依變數型態有下列幾種方法(假設這個 INI 物件名為 MyIni)
MyIni->ReadInteger(節區名,識別字,整數Default值);
MyIni->ReadString(節區名,識別字,字串Default值);
MyIni->ReadBool(節區名,識別字,布林Default值)
4.寫入的方法依變數型態也有下列幾種方法
MyIni->WriteInteger(節區名,識別字,欲寫入之整數變數值);
MyIni->WriteString(節區名,識別字,欲寫入之字串變數值);
MyIni->WriteBool(節區名,識別字,欲寫入之布林變數值)

以下為一完整使用INI程式碼的範例
//-------------------------------------------------------
#include
#pragma hdrstop

#include "Unit1.h"
#include "inifiles.hpp" //要 include 這個東西才有 TIniFile 類別
//-------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
TIniFile *MyIni;
//-------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//-------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  //建立 ini 物件及實體存放路徑
  MyIni = new TIniFile(ChangeFileExt( Application->ExeName, ".ini" ) );

  //讀取變數(若變數值不存在則用自動使用 Default 值)
  Left=MyIni->ReadInteger( "Form","Left",100);
  Caption=MyIni->ReadString( "Form","Caption","Default Caption");
}
//-------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
  //將變數寫入 ini 物件
  MyIni->WriteInteger("Form","Left",Left);
  MyIni->WriteString("Form","Caption",Caption);

  // 釋放 ini 物件
  delete MyIni;
}
//-------------------------------------------------------

■ INI 物件的生命週期

在上面的程式碼中,可以看見我們把 INI 物件的建立寫在 FormCreate 事件中,而釋放 INI 物件則寫在 FormClose 事件中;事實上,變數真正寫到實體檔案中,是在INI 物件釋放的時候(也就是類似關閉檔案動作);平常則只是暫存於記憶體中,如此可減少磁碟 I/O 動作,但若程式不正常關閉,則INI 物件來不及將暫存於記憶體中的變數存檔,會導致欲存檔的變數遺失;所以為保險起見,我們可在準備寫入變數的時候才去建立 INI 物件,存入後馬上釋放 INI 物件以確保寫入實體檔案中,但這樣卻會增加磁碟I/O讀寫頻繁,其中的斟酌端看應用方向的特性。

//-------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  TIniFile *MyIni;
  MyIni = new TIniFile(ChangeFileExt( Application->ExeName, ".ini" ) );

  MyIni->WriteInteger("Form","Left",Left);
  MyIni->WriteString("Form","Caption",Caption);

  delete MyIni;
}
//-------------------------------------------------------