FireDAC 下的 Sqlite [10] - 使用 R-Tree 搜索.

R-Tree 主要用于三维空间的搜索, 据说这种搜索算法非常之快, 哪怕百万条记录也是眨眼间的事!

SQLite 支持 1-5 维, FireDAC 也提供了 TFDSQLiteRTree 控件以方便定义回调函数. 为了简单, 我用二维表进行了成功的测试.

建立 R-Tree 表(索引)时需要使用特定语法, 譬如:

FDConnection1.ExecSQL(‘CREATE VIRTUAL TABLE MyRTreeTable USING rtree(Id, minX, maxX, minY, maxY)‘);
//必须是 VIRTUAL 表
//USING rtree, 是必须的; 也可以是 USING rtree_i32
//Id, minX, maxX, minY, maxY; 这是 ID 与二维空间的数据, 这里无需指定参数类型; 因为参数类型是内定的: Id 是 64 位无符号整形(且是主键), 后面的数据是 32 位浮点
//如果使用 rtree_i32 定义, 后面的数据则都是 32 为整形; 另外如果指定了 SQLITE_RTREE_INT_ONLY 参数, 无论怎么定义, 内部都用整形计算.


为此我做了两个例子, 第一个例子先没有使用 TFDSQLiteRTree(也就是没用回调).

本例除了使用 TFDConnection, TFDPhysSQLiteDriverLink, TFDGUIxWaitCursor, TDataSource, TDBGrid 外, 还有一个 TPaintBox, 用于绘图和点击测试, 用到它的 OnPaint 和 OnMouseUp 事件.

可把下面代码直接贴在空白窗体上, 以快速完成窗体设计:



代码:

var VBitmap: TBitmap; //当做内存画布

procedure TForm1.FormCreate(Sender: TObject);
const
  W = 50; H = 30;
var
  i,x,y,x1,x2,y1,y2: Integer;
begin
  FDConnection1.Params.Add(‘DriverID=SQLite‘);
  FDConnection1.ExecSQL(‘CREATE VIRTUAL TABLE MyRTreeTable USING rtree(Id, minX, maxX, minY, maxY)‘); //建表
  FDConnection1.Connected := True;

  {为数据库添加模拟数据}
  FDConnection1.StartTransaction;
  try
    for i := 0 to 100 do
    begin
      x := Random(PaintBox1.Width);
      y := Random(PaintBox1.Height);
      FDConnection1.ExecSQL(‘INSERT INTO MyRTreeTable VALUES(:id, :x1, :x2, :y1, :y2)‘, [i, x, x+W, y, y+H]);
    end;
    FDConnection1.Commit;
  except
    FDConnection1.Rollback;
  end;

  {呈现}
  FDQuery1.Open(‘SELECT * FROM MyRTreeTable ORDER BY Id‘);
  for i := 0 to DBGrid1.Columns.Count - 1 do DBGrid1.Columns[i].Width := 66; //默认的网格列太宽了, 处理一下

  {根据刚刚添加的数据绘制一张内存图片}
  VBitmap := TBitmap.Create;
  VBitmap.SetSize(PaintBox1.Width, PaintBox1.Height);
  VBitmap.Canvas.Brush.Color := clWhite;
  VBitmap.Canvas.FillRect(Rect(0, 0, VBitmap.Width, VBitmap.Height));

  FDQuery1.First;
  while not FDQuery1.Eof do
  begin
    x1 := FDQuery1.Fields[1].AsInteger;
    x2 := FDQuery1.Fields[2].AsInteger;
    y1 := FDQuery1.Fields[3].AsInteger;
    y2 := FDQuery1.Fields[4].AsInteger;
    VBitmap.Canvas.Brush.Color := Random($EEEEEE);
    VBitmap.Canvas.FillRect(Rect(x1, y1, x2, y2));
    FDQuery1.Next;
  end;
end;

{在 OnMouseUp 事件中执行了 R-Tree 搜索}
procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  i: Integer;
begin
  Caption := Format(‘%d, %d‘, [X, Y]);
  FDQuery1.Open(‘SELECT * FROM MyRTreeTable WHERE minX <= :X AND maxX > :X AND minY <= :Y AND maxY > :Y‘, [X,Y]); //[X,X,Y,Y] ?
  for i := 0 to DBGrid1.Columns.Count - 1 do DBGrid1.Columns[i].Width := 66; //这行只为缩小列宽
end;

{呈现前面绘制的内存图片}
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  PaintBox1.Canvas.Draw(0, 0, VBitmap);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  VBitmap.Free;
end;


测试效果图:



第二个例子效果同上, 但使用了 TFDSQLiteRTree, 它除了设定几个参数外, 主要是使用其 OnCalculate, 该事件对应 SQLite 内部的相关回调函数.


var VBitmap: TBitmap;

{这是 FDSQLiteRTree1 的 OnCalculate 事件}
procedure TForm1.FDSQLiteRTree1Calculate(ARTree: TSQLiteRTreeData; const AParams, AColumns: TSQLiteRTreeDoubleArray; var AResult: Boolean);
begin
  AResult := PtInRect( //换成了 WinAPI.PtInRect
    Rect(Trunc(AColumns[0]), Trunc(AColumns[2]), Trunc(AColumns[1]), Trunc(AColumns[3])),  //是出 Id 外的空间的数据
    Point(Trunc(AParams[0]), Trunc(AParams[1]))                                            //AParams 是 MyRTreeCallback 函数的参数
  );
end;

procedure TForm1.FormCreate(Sender: TObject);
const
  W = 50; H = 30;
var
  i,x,y,x1,x2,y1,y2: Integer;
begin
  {添加了下面四行来设定 FDSQLiteRTree1 的参数, 这些参数一般可以在设计时指定}
  FDSQLiteRTree1.DriverLink := FDPhysSQLiteDriverLink1;
  FDSQLiteRTree1.RTreeName := ‘MyRTreeCallback‘; //这是后面 SQL 语句中使用的函数名
//  FDSQLiteRTree1.OnCalculate := FDSQLiteRTree1Calculate; //事件已在设计时指定
  FDSQLiteRTree1.Active := True;

  FDConnection1.Params.Add(‘DriverID=SQLite‘);
  FDConnection1.ExecSQL(‘CREATE VIRTUAL TABLE MyRTreeTable USING rtree(Id, minX, maxX, minY, maxY)‘); //这行有改变
  FDConnection1.Connected := True;

  FDConnection1.StartTransaction;
  try
    for i := 0 to 100 do
    begin
      x := Random(PaintBox1.Width);
      y := Random(PaintBox1.Height);
      FDConnection1.ExecSQL(‘INSERT INTO MyRTreeTable VALUES(:id, :x1, :x2, :y1, :y2)‘, [i, x, x+W, y, y+H]);
    end;
    FDConnection1.Commit;
  except
    FDConnection1.Rollback;
  end;

  FDQuery1.Open(‘SELECT * FROM MyRTreeTable ORDER BY Id‘);
  for i := 0 to DBGrid1.Columns.Count - 1 do DBGrid1.Columns[i].Width := 66;

  VBitmap := TBitmap.Create;
  VBitmap.SetSize(PaintBox1.Width, PaintBox1.Height);
  VBitmap.Canvas.Brush.Color := clWhite;
  VBitmap.Canvas.FillRect(Rect(0, 0, VBitmap.Width, VBitmap.Height));

  FDQuery1.First;
  while not FDQuery1.Eof do
  begin
    x1 := FDQuery1.Fields[1].AsInteger;
    x2 := FDQuery1.Fields[2].AsInteger;
    y1 := FDQuery1.Fields[3].AsInteger;
    y2 := FDQuery1.Fields[4].AsInteger;
    VBitmap.Canvas.Brush.Color := Random($EEEEEE);
    VBitmap.Canvas.FillRect(Rect(x1, y1, x2, y2));
    FDQuery1.Next;
  end;
end;

procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  i: Integer;
begin
  Caption := Format(‘%d, %d‘, [X, Y]);
//  FDQuery1.Open(‘SELECT * FROM MyRTreeTable WHERE minX <= :X AND maxX > :X AND minY <= :Y AND maxY > :Y‘, [X,Y]);
  FDQuery1.Open(‘SELECT * FROM MyRTreeTable WHERE Id MATCH MyRTreeCallback(:X, :Y)‘, [X,Y]);  // MyRTreeCallback 是通过 FDSQLiteRTree1.RTreeName 指定的
  for i := 0 to DBGrid1.Columns.Count - 1 do DBGrid1.Columns[i].Width := 66;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  PaintBox1.Canvas.Draw(0, 0, VBitmap);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  VBitmap.Free;
end;


FireDAC 下的 Sqlite [10] - 使用 R-Tree 搜索.

原文地址:https://www.cnblogs.com/yjhb/p/11804249.html

时间: 2024-10-07 18:52:48

FireDAC 下的 Sqlite [10] - 使用 R-Tree 搜索.的相关文章

FireDAC 下的 Sqlite [8] - 自定义函数

Sqlite 本身没有这个功能, FireDAC 通过 TFDSQLiteFunction 增加了该功能; 尽管通过某些 SQL 语句或通过视图也可以达到类似效果, 但函数会更灵活些.本例先建了一个成绩表, 然后通过两个 TFDSQLiteFunction 实现了 "总分" 与 "平均分" 的计算. 你可以复制下面文本框中的内容, 然后直接往窗体上贴, 以快速完成窗体设计:object DBGrid1: TDBGrid Left = 8 Top = 88 Width

FireDAC 下的 Sqlite [11] - 关于批量提交 SQL 命令的测试

可把下面代码直接贴在空白窗体上, 以快速完成窗体设计:object DBGrid1: TDBGrid Left = 0 Top = 0 Width = 265 Height = 338 Align = alLeft DataSource = DataSource1 TabOrder = 0 TitleFont.Charset = DEFAULT_CHARSET TitleFont.Color = clWindowText TitleFont.Height = -11 TitleFont.Name

FireDAC 下的 Sqlite [5] - 数据的插入、更新、删除

先在空白窗体上添加: TFDConnection.TFDPhysSQLiteDriverLink.TFDGUIxWaitCursor.TFDQuery.TDataSource.TDBGrid(并在设计时关联好).你也可以复制下面文本框中的内容, 然后直接往窗体上贴, 以快速完成以上的添加过程:object DBGrid1: TDBGrid Left = 16 Top = 88 Width = 361 Height = 329 DataSource = DataSource1 TabOrder =

FireDAC 下的 Sqlite [1] - 前言

很长时间没静下心来写博客了, 现在回来, 是 Delphi 不断地进步让我感动.振奋.Delphi XE5 并入了 FireDAC, 第一印象非常好, 恐怕 dbExpress 等等都要靠边站了.让我最高兴地是 FireDAC 对 Sqlite 的支持!优秀的 Sqlite 早就有很多 Delphi 的包装(http://www.sqlite.org/cvstrac/wiki?p=SqliteWrappers, 从 https://code.google.com/ 等还能搜到更多).有静态引用

FireDAC 下的 Sqlite [3] - 获取数据库的基本信息

position:static(静态定位) 当position属性定义为static时,可以将元素定义为静态位置,所谓静态位置就是各个元素在HTML文档流中应有的位置 podisition定位问题.所以当没有定义position属性时,并不说明该元素没有自己的位置,它会遵循默认显示为静态位置,在静态定位状态下无法通过坐标值(top,left,right,bottom)来改变它的位置. position:absolute(绝对定位) 当position属性定义为absolute时,元素会脱离文档流

FireDAC 下的 Sqlite [9] - 关于排序

今天有幸被召回母校给即将毕业的学弟学妹们讲我这两年的工作史,看了下母校没啥特别的变化,就是寝室都安了空调,学妹们都非常漂亮而已..好了不扯蛋了,说下今天的主题吧.这些天我在深度定制语法高亮功能的同时发现了博客园提供的一些有意思的函数,甚至有几个博客园都没用到,我也不知道怎么才能触发那些功能..打开这个js就可以看到很多好用的东西了,虽然写的不怎么样,但是至少有这些功能. ps: 推荐安装一个代码格式化的插件,否则一坨看着蛋疼.比如第一个就是 log,方便调试. http://www.qidian

FireDAC 下的 Sqlite [7] - 备份、优化、事务(Transaction)/

用 TFDSQLiteBackup 控件, 两三行代码即可完成 Sqlite 数据库的备份. procedure TForm1.Button1Click(Sender: TObject); begin   {先初始化目标}   FDConnection1.DriverName := 'SQLite';   FDConnection1.Params.Add('Database=C:\Temp\FDDemo_Back.sdb'); //如果不指定这个路径, 就是备份到内存   FDConnectio

FireDAC 下的 Sqlite [12] - 备忘录(草草结束这个话题了).

该话题的继续延伸主要就是 SQL 的语法了, 草草收场的原因是现在的脑筋已经进入了 IntraWeb 的世界. 相关备忘会随时补充在下面: //连接多个数据库的参考代码: FDConnection1.ExecSQL('ATTACH ''c:\hr.sdb'' AS hr'); FDConnection1.ExecSQL('ATTACH ''c:\cust.sdb'' AS cust'); FDQuery1.Open('select * from "Orders" o ' +   'le

FireDAC中的SQLite(一)

Windows client software,FireDAC supports two SQLite library linking modes: Static linking: the x86 sqlite3_x86.obj or x64 sqlite3_x64.obj client library is statically linked into application. FireDAC provides sqlite3_Xxx.obj v 3.7.15. That is default