如何使用TClient数据集的日期过滤功能? [英] How to use the Filter functions of TClientdatasets for dates?

查看:289
本文介绍了如何使用TClient数据集的日期过滤功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Delphi 7中我有一个TClientDataSet,我想将一个筛选器应用到一个简单的TEdit中,所以它看起来像这样:

  CDS.Filter:= Edit1.Text; 
CDS.Filtered:= True;

现在我查看Helpfile来过滤记录
,根据它我应该可以筛选日期时间字段以及。
但是每当我写这样的东西到我的编辑:

$ $ p $ code DAY(EDATUM)= 17

并应用过滤器我得到了表达式中的类型不匹配-Exception



我已经尝试了上面例子的许多不同的格式。

  DATE(DAY(EDATUM))= DATE DAY(17))//不工作
DAY(EDATUM)='17'//不工作
DAY(EDATUM)= DAY(17)//不工作
DAY(EDATUM)= DAY(DATE('17 .09.2016'))
...
...

唯一有效的是

pre $ EDATUM = '17 .09.2016'// Works

但是我想单独过滤几个月和几年,而不是把它们放在一起。



我在其他地方找到的东西无论如何也没有。



任何想法我做错了什么?



Edatum是Firebird 1.5 Dat中的TimeStamp如果你想使用 Filter 表达式,而不是使用表达式,一个 OnFilterRecord 处理程序,值得看看TclientDataSet使用的 TExprParser 类的来源文本过滤器。它包含在Delphi源代码中的DBCommon.Pas单元文件中。 D7 TExprParser 支持以下功能:

 函数TExprParser.TokenSymbolIsFunc const S:string):Boolean; 
begin
结果:=(CompareText(S,'UPPER')= 0)或
(CompareText(S,'LOWER')= 0)或
[... (S,'DAY')= 0)或
(CompareText(S,'YEAR')= 0)或
(CompareText(S,'MONTH')= 0) )或
[...]
end;



顺便说一下,通过 TExprParser 的源代码,因为它显示了对SQL中找到的 IN 结构的支持。



(英国)系统,日期在DBGrid中显示为dd / mm / yyyy。鉴于此,下面显示的所有过滤器表达式都可以在D7中工作,而不会产生异常,并返回预期的结果:

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $>过程TForm1。 Button1Click(Sender:TObject);
begin

// CD的ADate字段由
// CDS1.FieldByName('ADate')初始化AsDateTime:= Now - random(365);

edFilter.Text:='ADate =''10 / 2/2017'''; //工作,日期格式= dd / mm / yyyy
edFilter.Text:='Month(ADate)= 2'; // works
edFilter.Text:='Year(ADate)= 2017'; //作品
edFilter.Text:='(Day(ADate)= 10)和(Year(ADate)= 2017)'; //作品

CDS1.Filtered:= False;
CDS1.Filter:= edFilter.Text;
CDS1.Filtered:= True;
end;

如果您没有得到类似的结果,我建议您先查看您的区域设置以及如何在TDBGrid中显示日期。

与其他筛选方法相比,筛选器表达式并不是特别有效,即使用 OnFilterRecord 事件。



在事件处理程序中,您可以使用例如将DecodeDateTime解码为年,月,日等组件,并将您喜欢的任何测试应用于其值。然后将接受设置为True或False。



更新答案在这里
Delphi :检查DataSet的记录是否可见或被过滤
您遇到的问题是
TExprParser.TokenSymbolIsFunc()不在用户的语言中。



您可以使用下面的代码来转换过滤器表达式中的日期函数名称。
查看嵌入式注释,了解它是如何工作的
$ b $ $ $ $ $ $ $ $ $ b $ TForm1 = class(TForm)
[...]
public
NameLookUp:TStringList;
[...]
end;

procedure TForm1.FormCreate(Sender:TObject);
begin
NameLookUp:= TStringList.Create;
//假设Y,M& C是本地语言名称
NameLookUp.Add('Y = Year');
NameLookUp.Add('M = Month');
NameLookUp.Add('D = Day');
[...]
end;

procedure TForm1.Log(const Title,Msg:String);
begin
Memo1.Lines.Add(Title +':'+ Msg);
end;

function TForm1.TranslateExpression(const Input:String; ADataSet:TDataSet):String;
var
SS:TStringStream;
TokenText:String;
LookUpText:String;
解析器:TParser;
CH:Char;
begin
SS:= TStringStream.Create(Input);
解析器:= TParser.Create(SS);
结果:='';
尝试
CH:= Parser.Token;
//下面的翻译通过使用Classes.Pas中的TParser对Parser.Token<>中的
进行解析来实现输入。 #0 do begin
TokenText:= Parser.TokenString;
case
的情况下toSymbol:begin
//下面将转换符号
//的TokenText,但前提是TokenText不是ADataSet的字段名称
if ADataSet。 FindField(TokenText)= Nil然后开始
LookUpText:= NameLookUp.Values [TokenText];
如果LookUpText<> ''then
Result:= Result + LookUpText
else
Result:= Result + TokenText;
end
else
结果:=结果+ TokenText;
end;
toString:
//在输入中包含TokenText的单引号和嵌入其中的元素
//将被删除,因此恢复周围的元素和
//将嵌入的
Result:= Result +''''+ StringReplace(TokenText,'''','''''',[rfReplaceAll])+'''';
else
结果:=结果+ TokenText;
end; {case}
if Result<> ''然后
结果:=结果+'';
CH:= Parser.NextToken;
end;
终于
Parser.Free;
SS.Free;
end;
Log('TransResult',Result);
end;

procedure TForm1.btnSetFilterExprClick(Sender:TObject);
begin
//经过例如edFilter.Text测试=
// LastName ='aaa'和Y(BirthDate)= 2000
UpdateFilter2;
end;

程序TForm1.UpdateFilter2;
var
T1:Integer;
begin
CDS1.OnFilterRecord:= Nil;
T1:= GetTickCount;
CDS1.DisableControls;
尝试
CDS1.Filtered:= False;
CDS1.Filter:= TranslateExpression(edFilter.Text,CDS1);
if CDS1.Filter<> ''然后开始
CDS1.Filtered:= True;
end;
Log('Filter update time',IntToStr(GetTickCount - T1)+'ms');
终于
CDS1.EnableControls;
end;
end;


I have a TClientDataSet in Delphi 7 and I'd like to apply a filter which I type into a simple TEdit, so it looks like this:

CDS.Filter:=Edit1.Text;
CDS.Filtered:=True;

Now I looked at the Helpfile for filtering records and according to it I should be able to Filter DateTime-Fields as well. But whenever I write something like this into my Edit:

DAY(EDATUM)=17  

and apply the filter I get a "Type Mismatch in Expression"-Exception.

I have tried numerous different formats of the example above.

DATE(DAY(EDATUM))=DATE(DAY(17))     //Doesn't work
DAY(EDATUM)='17'                    //Doesn't work
DAY(EDATUM)=DAY(17)                 //Doesn't work   
DAY(EDATUM)=DAY(DATE('17.09.2016'))
...
...

the only one that works is

EDATUM='17.09.2016'                 //Works

But I want to filter on Days months and years seperately and not have them together in a string.

Nothing I found online elsewhere worked either.

Any Idea what I'm doing wrong?

Edatum is a TimeStamp in a Firebird 1.5 Database.

解决方案

If you want to use a Filter expression instead of an OnFilterRecord handler, it is worthwhile taking a look at the source of the TExprParser class, which is what TClientDataSet uses for textual filters. It is contained in the DBCommon.Pas unit file in your Delphi source. The D7 TExprParser supports the following functions:

function TExprParser.TokenSymbolIsFunc(const S: string) : Boolean;
begin
  Result := (CompareText(S, 'UPPER') = 0) or
            (CompareText(S, 'LOWER') = 0) or
            [...]
            (CompareText(S, 'YEAR') = 0) or
            (CompareText(S, 'MONTH') = 0) or
            (CompareText(S, 'DAY') = 0) or
            [...]
end;

Btw, it is worthwhile looking through the rest of TExprParser's source because it reveals things like support for the IN construct found in SQL.

On my (UK) system, dates display in a DBGrid as dd/mm/yyyy. Given that, all of the filter expressions shown below work in D7 without producing an exception and return the expected results:

procedure TForm1.Button1Click(Sender: TObject);
begin

  //  ADate field of CDS is initialised by
  //  CDS1.FieldByName('ADate').AsDateTime := Now - random(365);

  edFilter.Text := 'ADate = ''10/2/2017''';  //  works, date format = dd/mm/yyyy
  edFilter.Text := 'Month(ADate) = 2';       //  works
  edFilter.Text := 'Year(ADate) = 2017';     //  works
  edFilter.Text := '(Day(ADate) = 10) and (Year(ADate) = 2017)';        //  works

  CDS1.Filtered := False;
  CDS1.Filter := edFilter.Text;
  CDS1.Filtered := True;
end;

If you don't get similar results, I'd suggest you start by looking at your regional settings and how dates are displayed in a TDBGrid.

Filter expressions are not particularly efficient compared to the alternative method of filtering, namely to use the OnFilterRecord event.

In the event handler, you can use e.g. DecodeDateTime to decode it into its Year, Month, Day, etc components and apply whatever tests you like to their values. Then set Accept to True or False.

Update I gather from your comment to an answer here Delphi: check if Record of DataSet is visible or filtered that the problem you had with this was that the date functions supported by TExprParser.TokenSymbolIsFunc() are not in your user's language.

You can use the code below to translate the date function names in the filter expression. See the embedded comments for explanation of how it works

type
  TForm1 = class(TForm)
    [...]
  public
    NameLookUp : TStringList;
    [...]
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  NameLookUp := TStringList.Create;
  //  Assume Y, M & C are the local-language names
  NameLookUp.Add('Y=Year');
  NameLookUp.Add('M=Month');
  NameLookUp.Add('D=Day');
  [...]
end;

procedure TForm1.Log(const Title, Msg : String);
begin
  Memo1.Lines.Add(Title + ' : ' + Msg);
end;

function TForm1.TranslateExpression(const Input : String; ADataSet : TDataSet) : String;
var
  SS : TStringStream;
  TokenText : String;
  LookUpText : String;
  Parser : TParser;
  CH : Char;
begin
  SS := TStringStream.Create(Input);
  Parser := TParser.Create(SS);
  Result := '';
  try
    CH := Parser.Token;
    //  following translates Input by parsing it using TParser from Classes.Pas
    while Parser.Token <> #0 do begin
      TokenText :=  Parser.TokenString;
      case CH of
        toSymbol : begin
          //  The following will translate TokenText for symbols
          //  but only if TokenText is not a FieldName of ADataSet
          if ADataSet.FindField(TokenText) = Nil then begin
            LookUpText := NameLookUp.Values[TokenText];
            if LookUpText <> '' then
              Result := Result + LookUpText
            else
              Result := Result + TokenText;
          end
          else
            Result := Result + TokenText;
        end;
        toString :
          //  SingleQuotes surrounding TokenText in Input and ones embedded in it
          //  will have been stripped, so reinstate the surrounding ones and
          //  double-up the embedded ones
        Result := Result + '''' + StringReplace(TokenText, '''', '''''', [rfReplaceAll]) + '''';
        else
          Result := Result + TokenText;
      end; { case }
      if Result <> '' then
        Result := Result + ' ';
      CH := Parser.NextToken;
    end;
  finally
    Parser.Free;
    SS.Free;
  end;
  Log('TransResult', Result);
end;

procedure TForm1.btnSetFilterExprClick(Sender: TObject);
begin
  //  Following tested with e.g edFilter.Text =
  //  LastName = 'aaa' and Y(BirthDate)  = 2000
  UpdateFilter2;
end;

procedure TForm1.UpdateFilter2;
var
  T1 : Integer;
begin
  CDS1.OnFilterRecord := Nil;
  T1 := GetTickCount;
  CDS1.DisableControls;
  try
    CDS1.Filtered := False;
    CDS1.Filter := TranslateExpression(edFilter.Text, CDS1);
    if CDS1.Filter <> '' then begin
      CDS1.Filtered := True;
    end;
    Log('Filter update time', IntToStr(GetTickCount - T1) + 'ms');
  finally
    CDS1.EnableControls;
  end;
end;

这篇关于如何使用TClient数据集的日期过滤功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆