2014年12月26日 星期五

ClientDataSet在EOF後可以取值嗎?

當我們在一個ClientDataSet取值時,如果該ClientDataSet已經EOF了,那ClientDataSet.FieldByName('KEY').AsString可以取值嗎?
答案是可以,它會是最後一筆的值。所以在做二個ClientDataSet比較時,別忘了在EOF時要做特別判斷,例如給'zzz'這種最大值,否則常會造成誤判,變成另一ClientDataSet的值被忽略了。

2014年12月20日 星期六

合久必分,分久必合

很久以前用DBASE資料庫的時候,當時一個檔案有2M的限制,所以考量到資料量的關係,於是把筆數多的資料,區分為不同類型的檔案。例如料品基本檔就依型態,原料叫ITEM1,一般物料叫ITEM2,半成品叫ITEM3,成品叫ITEM4等等。這樣就可以減少單一TABLE筆數過大的問題。

不過到了SQL,資料庫的容量大大提昇了,用WHERE條件來區分料品型態其實是一件很容易的事。於是為了減少程式碼,就把先前的檔案合併成一個,用一個分類碼來做區分,這樣原先要個別寫四隻程式維護,變成只要寫一隻就行了。

這種方式行之有年,大家也覺得很正常,資料庫大真的很方便,程式不用再像以前一樣考慮TABLE合併的問題。不過筆者最近碰到一個案例,就是前面說的料品檔一分為四的情形。可能是從舊程式資料轉上來,因為資料的關係,沒辦法合併到相同的TABLE,這下要查詢挑選全部的資料就麻煩了。

事實上在很多情況也必須把不同TABLE的資料合併讓操作人員挑選,例如付款時,付款的對象可能是廠商,但如果客戶也會有付款的情形呢?當然有人會在廠商檔建立客戶編號,當作付款對象,不過如果員工也會有付款情形呢?好像這樣加也不是好方法。

這是用SQL資料庫提供的VIEW就很方便了,利用UNION把先前分開的TABLE整合在一起,就可以讓操作人員挑選了
以下就是把客戶檔 G_CUST、廠商檔 P_VEND 和員工檔 PUB_EMP 建立成 v_PUB_ID_HQ

Create View vPUB_ID_HQ AS
Select  '1' AS FG_OBJECT,ID_CUST AS ID_HQ,NM_CS,NM_CONTACT,NN_TEL,NN_FAX,AR_MAIL FROM G_CUST
UNION
Select  '2' AS FG_OBJECT,ID_VEND AS ID_HQ,NM_CS,NM_CONTACT,NN_TEL,NN_FAX,AR_MAIL FROM P_VEND
UNION
Select  '3' AS FG_OBJECT,ID_EMP AS ID_HQ,NM_EMP AS NM_CS,NM_CONTACT,NN_TEL,NULL AS NN_FAX,AR_EMAIL AS AR_MAIL  FROM PUB_EMP

這樣以後 SLECT * FROM v_PUB_ID_HQ,就會出現合併的資料,讓使用者挑選了

用WHERE條件把大資料區分成需要的小資料,用UNION把不同TABLE的資料合併成大資料。二種方式讓你設計的資料庫結構更有彈性。

2014年12月13日 星期六

欄位資料取得的速度

如果要取得G_CUST的檔案中的AR_DLV欄位,用G_CUST.FieldByName('AR_DLV').AsString和G_CUSTAR_DLV.AsString(直接用欄位取值)那一個比較好?
以前筆者都採用G_CUST.FieldByName('AR_DLV').AsString,主要的原因是1.看起來很直覺2.不用抓欄位設定3.如果欄位或來源有更改不用重抓。
不過最近在做程式大量計算時,發現當資料量一多,檔案的欄位在多年功能變動加大的努力上,也成長了不少,結果速度就有點拖下去了。改用第二種方式後,速度明顯的提升了,原因是用欄位取值是直接取,FieldByName則要跑一下迴圈,判斷是AR_DLV欄位後,才給值,所以雖然FieldByName的彈性大,但是速度就被犧牲了。
所以在設計程式時,也許要考量一下這二種不同的方式,依程式特性來做取捨。提供給Delphi同好做參考。

2014年12月4日 星期四

文字自動序號的寫法

先前看過林壽山先生寫的 DBGrid顯示流水號方式 內容是用Integer的方式來做序號的產生。不過在我接觸的專案中,一般都是用文字來做序號,例如0001。主要是因為在排序時,會比較方便做處理,在顯示上也比較美觀。所以在此提供一下文字自動序號的解法。
首先在要文字自動序號的明細檔的DataSet建一個AggregateField欄位,如下圖
最重要的是在 Expression 加上MAX(NO_SEQ)。NO_SEQ就是我們要自動序號的文字欄位。

接下來在DataSet(本例為jdsG_SOS) 的 AfterInsert Event中加上 jdsG_SOSNO_SEQ.AsString := AutoSEQ(jdsG_SOSMAXSEQ.AsString);

而AutoSEQ這個Function的程式碼如下

function AutoSEQ(MaxSEQ : String):String;
const
  NOSize =4;
var
  I : Integer;
  NO : String;
begin
  NO := '';
  for I := 1 to NOSize-1 do NO := NO+'0';
  if Length(MaxSEQ)>0 then
  begin
    I := StrToInt(MaxSEQ)+1;
    NO := NO+IntToStr(I);
    NO := Copy(NO,Length(NO)-NOSize+1,NOSize);
  end
  else
  begin
    NO := NO+'1';
  end;

這樣在執行時,就可以看到NO_SEQ會出現0001這樣的序號了,如果修改為0003,接下來的序號也會變成0004。
希望提供的方式對DELPHI愛用者有幫助。