【问题标题】:Filter Dataset using SQL Query使用 SQL 查询过滤数据集
【发布时间】:2014-08-15 16:08:32
【问题描述】:

我在我的 delphi 项目中使用 Zeos 和 mysql。 我想做的是使用文本框过滤数据集。

为此,我在文本框“OnChange”事件中使用以下查询:

ZGrips.Active := false;
ZGrips.SQL.Clear;
ZGrips.SQL.Add('SELECT Part_Name, Description, OrderGerman, OrderEnglish FROM Part');
ZGrips.SQL.Add('WHERE Part_Name LIKE ' + '"%' + trim(txt_search.Text) + '%"');
ZGrips.Active := true;

在我运行并在文本框中键入第一个字符后,我的 DBGrid 中得到了空数据集, 所以 DBGrid 什么都没有显示,那么如果我输入第二个字符,我会在 DBGrid 中得到一些结果。甚至更奇怪的行为:如果我将在我的 SQL 查询中使用 AS 子句,例如:

Part_Name AS blablabla,
Description AS blablabla,
OrderGerman AS OG,
OrderEnglish AS OE

在这种情况下,DBGrid 只显示 2 列:Part_Name 和 Description,我不明白为什么它忽略了第 3 列和第 4 列。

提前感谢您的帮助。

【问题讨论】:

  • 在用户输入文本框时不断重新执行 SQL 查询不是一个好主意。相反,请执行以下操作:a) 在表单上放置一个 TTimer,间隔为 2-300 b) 在文本编辑中,只需在其 OnTimer 事件中启用计时器 c),禁用 TTimer 并执行 SQL 查询。这可能无法解决您的问题,但应该更容易找出问题所在。当然,在理想情况下,您应该在后台线程中执行 SQL 查询,以免 UI 在执行时停止。
  • 哦,有人可能会建议您将 SQL 查询重写为参数化查询,以减少 SQL 注入的范围 - en.wikipedia.org/wiki/Sql_injection
  • 同意 MartynA 关于将其置于计时器上,这允许用户快速输入多个字符,而无需对每个字符更改执行查询。
  • 好的,我会设置计时器,看看它是否改变了什么。我想知道,我对另一种形式使用相同的方法并且效果很好..
  • 另一个注意事项:此网格来自 TMS 网格包 - TDBAdvGrid。当我使用 DBGrid 时,它显示所有 4 列,但 DBAdvGrid 仅显示 2 列..

标签: mysql sql delphi filter dbgrid


【解决方案1】:

始终使用参数
首先您需要使用参数,否则当用户在搜索框中输入错误的字符时,您的查询将中断或更糟。
见:How does the SQL injection from the "Bobby Tables" XKCD comic work?
参数还可以让您查询更快,因为数据库引擎只需对查询进行一次解码。
如果您更改参数,引擎将知道查询本身没有更改并且不会重新解码它。

不要使用clearadd
只需一次性以文本形式提供 SQL,速度更快。
这是特别的。在循环中为真,在循环之外您不会注意到差异。

你的代码应该是这样的:

procedure TForm1.SetupSearch; //run this only once.
var
  SQL: string;
begin
  ZGrips.Active:= false;
  SQL:= 'SELECT Part_Name, Description, OrderGerman, OrderEnglish FROM Part' +
        'WHERE Part_Name LIKE :searchtext');  //note no % here.
  ZGrips.SQL.Text:= SQL;  //don't use clear and don't use SQL.Add.
end; 

//See: http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.StdCtrls.TEdit.OnChange  
procedure TForm1.Edit1Change(Sender: TObject);
begin
  if Edit1.Modified then begin
    Timer1.Active:= true;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Active:= false;
  if Edit1.Text <> ZGrips.Params[0].AsString then begin
    ZGrips.Params[0].AsString:= Edit1.Text + '%'
    ZGrips.Active:= true;
  end;
end;

使用计时器
根据@MartinA 的建议,使用计时器并仅经常启动查询。
您遇到的奇怪行为可能是因为您在旧查询有时间完成之前停止并重新激活了新查询。

Params[index: integer] 属性比 ParamsByName 属性快一点。
尽管这在循环之外并不重要。

允许数据库使用索引!
仅使用尾随通配符 % 比使用前导通配符更快,因为数据库只能在有尾随通配符的情况下使用索引。
如果要使用前导通配符,请考虑以相反的顺序存储数据并改用尾随通配符。

全文索引比like好很多
当然,如果您同时使用前导通配符和尾随通配符,那么您必须使用全文索引。
在 MySQL 中,您将使用 MATCH AGAINST 语法,
见:Differences between INDEX, PRIMARY, UNIQUE, FULLTEXT in MySQL?
和:Which SQL query is better, MATCH AGAINST or LIKE?

最新版本的 MySQL 在 InnoDB 中支持全文索引。
记住永远不要使用 MyISAM,它是不可靠的。

【讨论】:

  • 感谢您的帮助。我听从了你的建议,现在它可以正常工作了。你是对的 - 我停止和开始新查询的速度太快了。计时器在这里解决了这个问题。还改变了 LIKE 附近的奇怪语法,现在在这里使用这样的参数: ... := '%' + txt_search.Text + '%' 开头和结尾的两个通配符会导致 innoDB 引擎出现任何问题吗?在 TEdit 的“OnChange”事件上,我没有使用“if”子句来查看是否“Edit1.Modified”,好像发生了“OnChange”事件,这是由 Edit1 的修改引起的。对吗?
  • Modified 的测试在那里是有原因的。 OnChange 可以在编辑实际上没有更改时触发。 is Modified 会仔细检查更改并防止在 EditBox 中实际未发生更改时触发新查询。
  • 前导 %like 本身不会导致问题,但会阻止 MySQL(或任何其他数据库)在该行上使用索引,从而大大减慢查询速度.
猜你喜欢
  • 2015-09-15
  • 2020-03-02
  • 2013-12-13
  • 1970-01-01
  • 1970-01-01
  • 2014-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多