線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:2082
推到 Plurk!
推到 Facebook!

请问如何在Query中filter calculated field?

答題得分者是:Chance36
ebx
一般會員


發表:1
回覆:20
積分:9
註冊:2003-10-09

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-05-24 18:42:26 IP:203.106.xxx.xxx 未訂閱
我試了以下兩種方法,均行不通,望各位賜教 1. Filter := 'ABS = 1'; 會出現 Field 'ABS' cannot be used in a filter expression. 2. qryStaffOnFilterRecord: Accept := qryStaff.FieldByName('ABS').AsFloat >= 1; Main Program: qryStaff.Filtered := TRUE; 這時會出現qryStaff完全沒有資料 但是如果改成Accept := qryStaff.FieldByName('ABS').AsFloat > 0; 則會出現資料(不對的) 經檢查,發現是 onFilterRecord 的執行次序高於 onCalculateFields 也就是說,當執行到Accept := qryStaff.FieldByName('ABS').AsFloat 這行時,ABS 還未取得正確的值,這也解釋了為什麼改成 Accept := qryStaff.FieldByName('ABS').AsFloat > 0; 則會出現資料(因為ABS 這時的值是亂七八糟的且大於0) 我的環境是 WinXP Home SP1, Delphi6, Foxpro table.
Chance36
版主


發表:31
回覆:1033
積分:792
註冊:2002-12-31

發送簡訊給我
#2 引用回覆 回覆 發表時間:2004-05-24 19:08:23 IP:211.20.xxx.xxx 未訂閱
ebx 你好
  即然發現是 onFilterRecord 的執行次序高於 onCalculateFields,
那麼可以用第二種方法,自行觸發onCalcFields事件
2.
   qryStaffOnFilterRecord:
     qryStaff.OnCalcFields(Sender); // 自行觸發OnCalcFields 
     Accept := qryStaff.FieldByName('ABS').AsFloat >= 1;       Main Program:
     qryStaff.Filtered := TRUE;
ebx
一般會員


發表:1
回覆:20
積分:9
註冊:2003-10-09

發送簡訊給我
#3 引用回覆 回覆 發表時間:2004-05-26 16:02:20 IP:219.95.xxx.xxx 未訂閱
這也試過了,很奇怪,自行觸發onCalcFields事件所得到的值是錯的!!
Chance36
版主


發表:31
回覆:1033
積分:792
註冊:2002-12-31

發送簡訊給我
#4 引用回覆 回覆 發表時間:2004-05-26 17:18:05 IP:211.20.xxx.xxx 未訂閱
引言: 這也試過了,很奇怪,自行觸發onCalcFields事件所得到的值是錯的!! < face="Verdana, Arial, Helvetica"> ebx 你好 是否可以貼出你的程式碼呢?我這邊測試沒問題唷!
ko
資深會員


發表:28
回覆:785
積分:444
註冊:2002-08-14

發送簡訊給我
#5 引用回覆 回覆 發表時間:2004-05-26 18:07:56 IP:61.221.xxx.xxx 未訂閱
ebx 你好: Filter :='ABS =' Quotedstr('1');
------
======================
昏睡~
不昏睡~
不由昏睡~
ebx
一般會員


發表:1
回覆:20
積分:9
註冊:2003-10-09

發送簡訊給我
#6 引用回覆 回覆 發表時間:2004-05-27 10:02:23 IP:219.95.xxx.xxx 未訂閱
Chance36 你好,    1. qryStaff 與 tblDaily 關係是Master-Detail 2. qryStaffABS 的值是即時從tblDaily計算得來的 3. 麻煩你了! < class="code"> procedure TForm1.SetDailyRange; begin with tblDaily do begin CancelRange; SetRangeStart; FieldByName('Emp_Code').AsString := qryStaffEmp_Code.Value; FieldByName('Date').AsDateTime := EncodeDate(2004, 2, 1); SetRangeEnd; FieldByName('Emp_Code').AsString := qryStaffEmp_Code.Value; FieldByName('Date').AsDateTime := EncodeDate(2004, 2, 24); ApplyRange; end; end; procedure TForm1.tblDailyFilterRecord(DataSet: TDataSet; var Accept: Boolean); begin Accept := tblDailyREASON.Value = 'ABS'; end; procedure TForm1.qryStaffCalcFields(DataSet: TDataSet); begin if tblDailyEMP_CODE.Value <> qryStaffEMP_CODE.Value then SetDailyRange; tblDaily.First; while not tblDaily.Eof do begin if tblDailyREASON.Value = 'ABS' then qryStaffABS.Value := qryStaffABS.Value 1; tblDaily.Next; end; // ShowMessage('OnCalcFields ' qryStaffEmp_Code.Value ' ' FloatToStr(qryStaffABS.Value)); end; procedure TForm1.qryStaffFilterRecord(DataSet: TDataSet; var Accept: Boolean); begin qryStaffCalcFields(DataSet); // ShowMessage('OnFilterRecord ' qryStaffEmp_Code.Value ' ' FloatToStr(qryStaffABS.Value)); Accept := qryStaffABS.Value > 0; end; procedure TForm1.qryStaffAfterScroll(DataSet: TDataSet); begin // if tblDailyEMP_CODE.Value <> qryStaffEMP_CODE.Value then // SetDailyRange; end; procedure TForm1.Button1Click(Sender: TObject); begin // qryStaff.AutoCalcFields := FALSE; qryStaff.Filtered := TRUE; end;
ebx
一般會員


發表:1
回覆:20
積分:9
註冊:2003-10-09

發送簡訊給我
#7 引用回覆 回覆 發表時間:2004-05-27 10:13:37 IP:219.95.xxx.xxx 未訂閱
引言: ebx 你好: Filter :='ABS =' Quotedstr('1');
Field 'ABS' cannot be used in a filter expression.
Chance36
版主


發表:31
回覆:1033
積分:792
註冊:2002-12-31

發送簡訊給我
#8 引用回覆 回覆 發表時間:2004-05-27 12:43:08 IP:203.204.xxx.xxx 未訂閱
procedure TForm1.qryStaffCalcFields(DataSet: TDataSet);
begin
  if tblDailyEMP_CODE.Value <> qryStaffEMP_CODE.Value then
    SetDailyRange;      tblDaily.First;
  qryStaffABS.Value := 0; // 要先歸零
  while not tblDaily.Eof do
  begin
    if tblDailyREASON.Value = 'ABS' then
      qryStaffABS.Value := qryStaffABS.Value   1;
    tblDaily.Next;
  end;    //  ShowMessage('OnCalcFields '   qryStaffEmp_Code.Value   ' '   FloatToStr(qryStaffABS.Value));
end;    procedure TForm1.qryStaffFilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
begin
  qryStaffCalcFields(DataSet);
//  ShowMessage('OnFilterRecord '   qryStaffEmp_Code.Value   ' '   FloatToStr(qryStaffABS.Value));
  Accept := qryStaffABS.Value > 0;
end;    procedure TForm1.Button1Click(Sender: TObject);
begin
//  qryStaff.AutoCalcFields := FALSE;
  qryStaff.Filtered := TRUE;
end;
ebx
一般會員


發表:1
回覆:20
積分:9
註冊:2003-10-09

發送簡訊給我
#9 引用回覆 回覆 發表時間:2004-05-28 18:03:52 IP:219.95.xxx.xxx 未訂閱
不行    
引言:
procedure TForm1.qryStaffCalcFields(DataSet: TDataSet);
begin
  if tblDailyEMP_CODE.Value <> qryStaffEMP_CODE.Value then
    SetDailyRange;      tblDaily.First;
  qryStaffABS.Value := 0; // 要先歸零
  while not tblDaily.Eof do
  begin
    if tblDailyREASON.Value = 'ABS' then
      qryStaffABS.Value := qryStaffABS.Value   1;
    tblDaily.Next;
  end;    //  ShowMessage('OnCalcFields '   qryStaffEmp_Code.Value   ' '   FloatToStr(qryStaffABS.Value));
end;    procedure TForm1.qryStaffFilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
begin
  qryStaffCalcFields(DataSet);
//  ShowMessage('OnFilterRecord '   qryStaffEmp_Code.Value   ' '   FloatToStr(qryStaffABS.Value));
  Accept := qryStaffABS.Value > 0;
end;    procedure TForm1.Button1Click(Sender: TObject);
begin
//  qryStaff.AutoCalcFields := FALSE;
  qryStaff.Filtered := TRUE;
end;
Chance36
版主


發表:31
回覆:1033
積分:792
註冊:2002-12-31

發送簡訊給我
#10 引用回覆 回覆 發表時間:2004-05-28 22:42:15 IP:211.20.xxx.xxx 未訂閱
 再試試以下程式
//SetDailyRange移動AfterScroll事件中
procedure TForm1.qryStaffAfterScroll(DataSet: TDataSet);
begin
//  if tblDailyEMP_CODE.Value <> qryStaffEMP_CODE.Value then
    SetDailyRange;  // <----- 放到AfterScroll事件中
End;    procedure TForm1.qryStaffCalcFields(DataSet: TDataSet);
Var
  Cnt : Integer ;
begin
{  // 移到AfterScroll事件中 
  if tblDailyEMP_CODE.Value <> qryStaffEMP_CODE.Value then
    SetDailyRange;
}
  tblDaily.First;
  Cnt := 0 ; // 要先歸零
  while not tblDaily.Eof do
  begin
    if tblDailyREASON.Value = 'ABS' then
      Inc(Cnt);
    tblDaily.Next;
  end;
  qryStaffABS.Value := Cnt;
  
//  ShowMessage('OnCalcFields '   qryStaffEmp_Code.Value   ' '   FloatToStr(qryStaffABS.Value));
end;    procedure TForm1.qryStaffFilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
begin
  qryStaff.Edit;
  qryStaffCalcFields(DataSet);
  qryStaff.Post;
//  ShowMessage('OnFilterRecord '   qryStaffEmp_Code.Value   ' '   FloatToStr(qryStaffABS.Value));
  Accept := qryStaffABS.Value > 0;
end;    procedure TForm1.Button1Click(Sender: TObject);
begin
//  qryStaff.AutoCalcFields := FALSE;
  qryStaff.Filtered := TRUE;
end;
PS:一句[不行]會讓人傻眼的,而且也達不到互動討論的效果,對於問題的真象分析更是沒有幫助。
ebx
一般會員


發表:1
回覆:20
積分:9
註冊:2003-10-09

發送簡訊給我
#11 引用回覆 回覆 發表時間:2004-05-29 17:11:31 IP:219.95.xxx.xxx 未訂閱
引言:
 再試試以下程式
//SetDailyRange移動AfterScroll事件中
procedure TForm1.qryStaffAfterScroll(DataSet: TDataSet);
begin
//  if tblDailyEMP_CODE.Value <> qryStaffEMP_CODE.Value then
    SetDailyRange;  // <----- 放到AfterScroll事件中
End;
..... PS:一句[不行]會讓人傻眼的,而且也達不到互動討論的效果,對於問題的真象分析更是沒有幫助。
不好意思,因為「不行」的原因與之前的一樣就是 「很奇怪,自行觸發onCalcFields事件所得到的值是錯的!!」 所以就沒有略加說明。 1. 對於這次的修改,我想你是基於有使用一個TUpdateSQL,以及設定CacheUpdate/ReqeustLive 為TRUE 的情況下測試的吧,要不然會出現'qryStaff: Cannot modify a readonly dataset.' 2. 至於把SetDailyRange 移至AfterScroll, 會導致在完成了整個qryStaff.Filtered := TRUE 的事件後才會觸發一次SetDailyRange,而不是我要的每一筆記錄一次觸發SetDailyRange。 3. 再來是關鍵的地方,(使用一個TUpdateSQL,以及設定CacheUpdate/ReqeustLive 為TRUE 的情況下)
 procedure TForm1.qryStaffFilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
begin
  qryStaff.Edit;
  qryStaffCalcFields(DataSet);
  qryStaff.Post;
//  ShowMessage('OnFilterRecord '   qryStaffEmp_Code.Value   ' '   FloatToStr(qryStaffABS.Value));
  Accept := qryStaffABS.Value > 0;
end;
有三種情況 1. 不作任何修改,會出現'Record already locked by thi session' > >
Chance36
版主


發表:31
回覆:1033
積分:792
註冊:2002-12-31

發送簡訊給我
#12 引用回覆 回覆 發表時間:2004-05-30 00:04:23 IP:203.204.xxx.xxx 未訂閱
1. 對於這次的修改,我想你是基於有使用一個TUpdateSQL,以及設定CacheUpdate/ReqeustLive 為TRUE 的情況下測試的吧,要不然會出現'qryStaff: Cannot modify a readonly dataset.'    2. 至於把SetDailyRange 移至AfterScroll, 會導致在完成了整個qryStaff.Filtered := TRUE 的事件後才會觸發一次SetDailyRange,而不是我要的每一筆記錄一次觸發SetDailyRange。 [/quote] ebx 你好     我想我大概了解狀況了, 1.qryStaff是個唯讀的資料集,應該是SQL中有關聯或用到Order By等指令。故在OnFilterRecord不能Edit後再Post。 2.OnFilterRecord 好像會停止其他事件的觸發,所以AfterScroll及OnCalcFields事件都沒反應。 因此之故,在OnFilterRecord事件中,凡事都要自已來,且要注意不要動到自身(DataSet)的記錄指標,免得造成循環觸發OnFilterRecord事件。
procedure TForm1.qryStaffFilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
Var
  ABS : Integer ;
begin
  SetDailyRange; // 
  ABS := 0 ; // 要先歸零
  while not tblDaily.Eof do begin
    if tblDailyREASON.Value = 'ABS' then
      Inc(ABS );
    tblDaily.Next;
  end;
  Accept := ABS > 0; //使用變數來判斷
end;    procedure TForm1.qryStaffCalcFields(DataSet: TDataSet);
Var
  Cnt : Integer ;
begin
  SetDailyRange;
  tblDaily.First;
  Cnt := 0 ; // 要先歸零
  while not tblDaily.Eof do
  begin
    if tblDailyREASON.Value = 'ABS' then
      Inc(Cnt);
    tblDaily.Next;
  end;
  qryStaffABS.Value := Cnt;
  
//  ShowMessage('OnCalcFields '   qryStaffEmp_Code.Value   ' '   FloatToStr(qryStaffABS.Value));
end;    
peipei36
一般會員


發表:8
回覆:51
積分:16
註冊:2002-03-13

發送簡訊給我
#13 引用回覆 回覆 發表時間:2004-05-30 04:51:28 IP:220.137.xxx.xxx 未訂閱
看了Chance36版主 的結論才大概了解這是在做什麼事.. 您看這樣是不是您要的.. 但我沒用 Range..(用了可能也要跑迴圈處理..) 雖然感覺動作是正常了 但還是覺得 CalcFields 拿來這樣用..整個運作怪怪的.. 每一個CalcFields做一次detail的filter.. 資料量大些時 會不會不好跑..    
var cntAbs:integer;    procedure TForm1.qryStaffAfterScroll(DataSet: TDataSet);
begin
  SetDailyRange;
end;    procedure TForm1.SetDailyRange;
begin      with tblDaily do
  begin
    Filter := 'emp_code=''' qryStaff.Fieldbyname('emp_code').AsString ''' and date > ''2004/02/01'' and date < ''2004/02/24''';
    Filtered:=true;
  end;
end;    procedure TForm1.Button1Click(Sender: TObject);
begin
  tblDaily.Open;
  qryStaff.Open;
end;    procedure TForm1.qryStaffCalcFields(DataSet: TDataSet);
begin
  cntAbs:=0;
  //if tblDaily.State=dsInactive then tblDaily.Open;
  SetDailyRange;
  dataset.FieldByName('abs').AsInteger:=cntAbs;
end;    procedure TForm1.tblDailyFilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
begin
  if (dataset.FieldByName('reason').AsString='ABS') then inc(cntAbs);
end;
ebx
一般會員


發表:1
回覆:20
積分:9
註冊:2003-10-09

發送簡訊給我
#14 引用回覆 回覆 發表時間:2004-06-17 21:27:18 IP:218.111.xxx.xxx 未訂閱
我的解決方法與Chance36, 一樣,只不過這樣會影響執行效率,以及會有重複的代碼,在此提前一下,如果仍然沒有更好的方法,就結帖了,感謝Chance36的熱心幫助
Mickey
版主


發表:77
回覆:1882
積分:1390
註冊:2002-12-11

發送簡訊給我
#15 引用回覆 回覆 發表時間:2004-06-17 22:57:56 IP:218.32.xxx.xxx 未訂閱
Eebx 你好: 只是插花一下, 是否考慮過, 將 Data 透過 TDataSetProvider 到 TClientDataSet 之後, 再來過濾 ClientDataSet 呢 ?
ebx
一般會員


發表:1
回覆:20
積分:9
註冊:2003-10-09

發送簡訊給我
#16 引用回覆 回覆 發表時間:2004-06-18 08:49:41 IP:219.95.xxx.xxx 未訂閱
有,你是指用Internal Calculated Field嗎?由於以前從來沒有用過ClientDataSet,正在學習中。
系統時間:2024-06-29 19:47:50
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!