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 事件.
可把下面代码直接贴在空白窗体上, 以快速完成窗体设计:
object PaintBox1: TPaintBox
Left = 408
Top = 16
Width = 617
Height = 473
OnMouseUp = PaintBox1MouseUp
OnPaint = PaintBox1Paint
end
object DBGrid1: TDBGrid
Left = 0
Top = 0
Width = 393
Height = 503
Align = alLeft
DataSource = DataSource1
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
end
object FDConnection1: TFDConnection
Left = 34
Top = 24
end
object FDPhysSQLiteDriverLink1: TFDPhysSQLiteDriverLink
Left = 143
Top = 24
end
object FDGUIxWaitCursor1: TFDGUIxWaitCursor
Provider = 'Forms'
Left = 260
Top = 24
end
object FDQuery1: TFDQuery
Connection = FDConnection1
Left = 32
Top = 88
end
object DataSource1: TDataSource
DataSet = FDQuery1
Left = 132
Top = 88
end
object FDSQLiteRTree1: TFDSQLiteRTree
DriverLink = FDPhysSQLiteDriverLink1
Left = 256
Top = 96
end
代码:
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 minY :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 minY :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 搜索的更多相关文章
- FireDAC 下的 Sqlite [3] - 获取数据库的基本信息
在空白窗体上添加: TFDConnection, TFDPhysSQLiteDriverLink, TFDGUIxWaitCursor, TMemo procedure TForm1.FormCrea ...
- FireDAC 下的 Sqlite [6] - 加密
主要就是设置 TFDConnection 的两个链接参数: Password, NewPassword, 非常简单. const dbPath = 'C:\Temp\SQLiteTest.sdb'; ...
- FireDAC 下的 Sqlite [9] - 关于排序
SQLite 内部是按二进制排序, 可以支持 ANSI; FrieDAC 通过 TFDSQLiteCollation 支持了 Unicode 排序, 并可通过其 OnCompare 事件自定义排序. ...
- FireDAC 下的 Sqlite [8] - 自定义函数
Sqlite 本身没有这个功能, FireDAC 通过 TFDSQLiteFunction 增加了该功能; 尽管通过某些 SQL 语句或通过视图也可以达到类似效果, 但函数会更灵活些. 本例先建了一个 ...
- FireDAC 下的 Sqlite [4] - 创建数据库
建立数据库的代码: {建立内存数据库的一般代码:} begin FDConnection1.DriverName := 'SQLite'; //同 FDConnection1.Params.Add(' ...
- FireDAC 下的 Sqlite [11] - 关于批量提交 SQL 命令的测试
可把下面代码直接贴在空白窗体上, 以快速完成窗体设计: object DBGrid1: TDBGrid Left = 0 Top = 0 Width = 265 Height = 338 Align ...
- FireDAC 下的 Sqlite [2] - 第一个例子
为了方便测试, 我把官方提供的 C:\Users\Public\Documents\Embarcadero\Studio\14.0\Samples\data\FDDemo.sdb 复制了一份到 C:\ ...
- FireDAC 下的 Sqlite [1] - 前言
很长时间没静下心来写博客了, 现在回来, 是 Delphi 不断地进步让我感动.振奋. Delphi XE5 并入了 FireDAC, 第一印象非常好, 恐怕 dbExpress 等等都要靠边站了. ...
- FireDAC 下的 Sqlite [5] - 数据的插入、更新、删除
先在空白窗体上添加: TFDConnection.TFDPhysSQLiteDriverLink.TFDGUIxWaitCursor.TFDQuery.TDataSource.TDBGrid(并在设计 ...
随机推荐
- css框架,一把锋利的剑
CSS 框架是一系列 CSS 文件的集合体,包含了基本的元素重置,页面排版.网格布局.表单样式.通用规则等代码块,用于简化web前端开发的工作,提高工作效率. 产生原因 互联网行业已经发展了多年,浏览 ...
- 新建 Spring Mvc Web + Maven 的 maven 错误 (二)
新建项目后,可能由于哪边配置不正确,或也可能是编码问题,就有可能在创建初始就可能发生错误: 这是 pom.xml 中提示的错误,有的人说要删除 maven 的本地仓库位置:c:\用户[Users]\A ...
- 第12月第1天 MASConstraintMaker crash
1. crash [valueLabel mas_makeConstraints:^(PAKitMASConstraintMaker *make) { make.left.equalTo(finish ...
- WEB开发常用软件集合
软件 dreamweaver cs6 http://www.cr173.com/soft/74348.html navicat http://pan.baidu.com/s/1b9nNzw subli ...
- Android View坐标系详解(getTop()、getX、getTranslationX...)
View 提供了如下 5 种方法获取 View 的坐标:1. View.getTop().View.getLeft().View.getBottom().View.getRight();2. View ...
- casperjs批量执行多个url
var fs=require("fs"); ////-------sample.js-------// //casperオブジェクトを生成var casper = require( ...
- Phantomjs 抓取、分析某个页面加载时浏览器发起的所有的子请求
var page = require('webpage').create(), system = require('system'), address; if (system.args.length ...
- 关于sklearn,监督学习几种模型的对比
# K近邻,适用于小型数据集,是很好的基准模型,容易解释 from sklearn.neighbors import KNeighborsClassifier # 线性模型,非常可靠的首选算法,适用于 ...
- mac zsh环境配置java_home环境变量
用zsh,修改-/.zshrc 修改这些文件之后,重修打开terminal,配置不会丢 首先确保已经安装了jdk: ## check the present running java which ja ...
- Android Service总结06 之AIDL
Android Service总结06 之AIDL 版本 版本说明 发布时间 发布人 V1.0 初始版本 2013-04-03 Skywang 1 AIDL介绍 AIDL,即And ...