SQLite R*Tree 模块(三十三)

返回:SQLite—系列文章目录   

上一篇:SQLite FTS3 和 FTS4 扩展(三十二)

下一篇:SQLite轻量级会话扩展(三十四)

1. 概述

R-Tree 是一个特殊的 专为执行范围查询而设计的索引。R-树最常见的是 用于地理空间系统,其中每个条目都是一个矩形,最小且 最大 X 和 Y 坐标。给定一个查询矩形,R 树能够 快速查找查询矩形中包含的所有条目 或与查询矩形重叠。这个想法很容易扩展到 用于 CAD 系统的三维。R-Trees 也可用于时域 范围查找。例如,假设数据库记录了起始和 大量事件的结束时间。R-Tree 能够快速 查找在给定期间任何时间处于活动状态的所有事件 时间间隔,或在特定时间间隔内开始的所有事件, 或在给定时间间隔内开始和结束的所有事件。 等等。

R-Tree 概念起源于 Toni Guttman:R-Trees: A Dynamic Index Structure for Spatial Searching, 1984 ACM SIGMOD 数据管理国际会议, 第47-57页。 在SQLite中发现的实现是对Guttman原始的改进 想法,通常称为“R*Trees”,由 Norbert Beckmann、Hans-Peter Kriegel、Ralf Schneider、Bernhard Seeger:R*-树:一种高效且鲁棒的点访问方法 和矩形。SIGMOD会议1990:322-331。

2. 编译 R*Tree 模块

SQLite R*Tree 模块的源代码作为一部分包含在内 的合并。但是,根据配置选项 以及您正在使用的特定版本的 SQLite,它可能会也可能不会 默认启用。要确保启用 R*Tree 模块, 只需使用定义的 SQLITE_ENABLE_RTREE C 预处理器宏进行编译。使用许多编译器,可以完成此操作 通过向编译器添加选项“-DSQLITE_ENABLE_RTREE=1” 命令行。

3. 使用 R*Tree 模块

SQLite R*Tree 模块是作为虚拟表实现的。每个 R*Tree 索引都是一个 列数介于 3 到 11 之间的奇数的虚拟表。 第一列始终是 64 位有符号整数主键。 其他列是成对的,每个维度一对,包含 分别是该维度的最小值和最大值。 因此,一维 R*Tree 有 3 列。 二维 R*Tree 有 5 列。 三维 R*Tree 有 7 列。 一个 4 维 R*Tree 有 9 列。 一个 5 维 R*Tree 有 11 列。SQLite R*Tree 实现 不支持宽度超过 5 维的 R*Tree。

SQLite R*Tree 的第一列类似于整数主 普通 SQLite 表的 key 列。它只能存储 64 位签名 整数值。在此列中插入 NULL 值会导致 SQLite 自动生成新的唯一主键值。如果尝试 用于在此列中插入任何其他非整数值, R-Tree 模块在写入之前将其静默转换为整数 进入数据库。

最小值/最大值对列存储为 32 位浮点值 “rtree”虚拟表或“rtree_i32”虚拟表中的 32 位有符号整数 表。与常规 SQLite 表不同,它可以将数据存储在各种 数据类型和格式,R*Tree 严格执行这些存储类型。 如果将任何其他类型的值插入到此类列中,则 r 树 模块在写入 数据库的新记录。

3.1. 创建 R*Tree 索引

将按如下方式创建新的 R*Tree 索引:

CREATE VIRTUAL TABLE <name> USING rtree(<column-names>);

<name> 是应用程序为 R*Tree 索引和 <column-names> 是一个逗号分隔的列表 3 到 11 列之间。 虚拟<名称>表创建三个影子表,以实际 存储其内容。这些影子表的名称为:

<name>_node

<name>_rowid

<name>_parent

影子表是普通的 SQLite 数据表。您可以查询它们 如果您愿意,请直接使用,尽管这不太可能透露任何特别的信息 有用。 您可以更新、删除、插入甚至删除影子表,尽管这样做会损坏您的 R*Tree 索引。 因此,最好直接忽略影子表。认识到他们 保留您的 R*Tree 索引信息并让它照原样运行。

例如,请考虑创建一个二维 R*Tree 索引,以便在 空间查询:

CREATE VIRTUAL TABLE demo_index USING rtree(
   id,              -- Integer primary key
   minX, maxX,      -- Minimum and maximum X coordinate
   minY, maxY       -- Minimum and maximum Y coordinate
);

3.1.1. 列命名详细信息

在 CREATE VIRTUAL TABLE 语句中对 “rtree” 的参数中, 列的名称取自每个参数的第一个标记。 每个参数中的所有后续标记都将被静默忽略。 这意味着,例如,如果尝试为列提供类型亲和力,或者将约束(如 UNIQUE 或 NOT NULL 或 DEFAULT)添加到 一列,这些额外的标记被接受为有效,但它们不会更改 rtree 的行为。 在 RTREE 虚拟表中,第一列的类型亲和力始终为 INTEGER,所有其他数据列的类型亲和性为 REAL。 在RTREE_I32虚拟表中,所有列的类型亲和力均为 INTEGER。

推荐的做法是在 rtree 规范中省略任何额外的标记。 让 “rtree” 的每个参数都是一个普通的标签,即 相应的列,并从参数列表中省略所有其他标记。

3.2. 填充 R*Tree 索引

通常的 INSERT、UPDATE 和 DELETE 命令适用于 R*Tree 索引就像在常规表上一样。因此,要将一些数据插入到我们的示例中 R*Tree index,我们可以做这样的事情:

INSERT INTO demo_index VALUES
  (28215, -80.781227, -80.604706, 35.208813, 35.297367),
  (28216, -80.957283, -80.840599, 35.235920, 35.367825),
  (28217, -80.960869, -80.869431, 35.133682, 35.208233),
  (28226, -80.878983, -80.778275, 35.060287, 35.154446),
  (28227, -80.745544, -80.555382, 35.130215, 35.236916),
  (28244, -80.844208, -80.841988, 35.223728, 35.225471),
  (28262, -80.809074, -80.682938, 35.276207, 35.377747),
  (28269, -80.851471, -80.735718, 35.272560, 35.407925),
  (28270, -80.794983, -80.728966, 35.059872, 35.161823),
  (28273, -80.994766, -80.875259, 35.074734, 35.172836),
  (28277, -80.876793, -80.767586, 35.001709, 35.101063),
  (28278, -81.058029, -80.956375, 35.044701, 35.223812),
  (28280, -80.844208, -80.841972, 35.225468, 35.227203),
  (28282, -80.846382, -80.844193, 35.223972, 35.225655);

上面的条目是 14 的边界框(经度和纬度) 北卡罗来纳州夏洛特附近的邮政编码。一个真正的数据库将有数千个, 数以百万计或数十亿个这样的条目,但这个 14 行的小样本将 足以说明这些想法。

3.3. 查询 R*Tree 索引

任何有效的查询都将针对 R*Tree 索引。R*树 实现只是进行某些类型的查询,特别是 有效。对主键的查询是有效的:

SELECT * FROM demo_index WHERE id=28269;

当然,一个普通的SQLite表也会对它的 整数主键效率高,所以前一个并不重要。 使用 R*Tree 的主要原因是 您可以有效地对坐标进行范围查询 范围。例如,SQLite项目的主要办公室是 位于 35.37785, -80.77470。 要找到哪些邮政编码可以为该办公室服务,可以正确:

SELECT id FROM demo_index
 WHERE minX<=-80.77470 AND maxX>=-80.77470
   AND minY<=35.37785  AND maxY>=35.37785;

上面的查询将快速找到所有包含 SQLite 主办公室在其边界框中,即使 R*Tree 包含许多条目。前面是一个例子 “contained-within”查询。R*Tree 还支持“重叠” 查询。例如,查找所有重叠的邮政编码边界框 邮政编码为 28269:

SELECT A.id FROM demo_index AS A, demo_index AS B
 WHERE A.maxX>=B.minX AND A.minX<=B.maxX
   AND A.maxY>=B.minY AND A.minY<=B.maxY
   AND B.id=28269;

第二个查询将找到 28269 条目(因为每个边界框 与自身重叠)以及其他足够接近的邮政编码 28269 它们的边界框重叠。

请注意,R*Tree 索引中的所有坐标都不是必需的 进行约束,以使索引搜索高效。 例如,人们可能想要查询与 北纬35度线:

SELECT id FROM demo_index
 WHERE maxY>=35.0  AND minY<=35.0;

但是,一般来说,R*Tree 模块的约束越多 必须使用,边界框越小,越快 结果会回来的。

3.4. 舍入误差

默认情况下,坐标使用 32 位浮点存储在 R*Tree 中 点值。当坐标不能用 32位浮点数,下界坐标向下舍入 并将上界坐标四舍五入。因此,边界框可能 比指定稍大,但永远不会更小。这 这正是执行更常见的“重叠”查询所需的 应用程序希望在 R*Tree 中查找重叠的每个条目 查询边界框。向外舍入条目边界框可能会导致 在重叠的查询中出现很少的额外条目,如果 条目边界框对应于查询边界框的边缘。但 重叠查询永远不会错过有效的表条目。

但是,对于“包含在”样式查询中,将边界四舍五入 框向外可能会导致某些条目从结果集中排除 如果条目边界框的边缘对应于查询的边缘 边界框。为了防止这种情况,应用程序应扩展其 通过向下舍入 在每个维度中,较低的坐标和向上舍入顶部的坐标。

3.5. 同时阅读和写作

这是 Guttman R-Tree 算法的本质,任何写入都可能 从根本上重构树,并在此过程中更改扫描顺序 节点。因此,通常无法修改 R 树的查询中间的 R 树。尝试这样做 将失败,并显示SQLITE_LOCKED“数据库表已锁定”错误。

因此,例如,假设一个应用程序对 R 树运行一个查询,例如 这:

SELECT id FROM demo_index
 WHERE maxY>=35.0  AND minY<=35.0;

然后,对于返回的每个“id”值,假设应用程序创建一个 UPDATE 语句,如下所示,并绑定返回的“id”值 “?1”参数:

UPDATE demo_index SET maxY=maxY+0.5 WHERE id=?1;

然后,UPDATE 可能会失败并出现SQLITE_LOCKED错误。原因是 初始查询尚未运行完成。它正在记住它的位置 在扫描 R 树的过程中。因此,对 R-Tree 的更新不能 可以容忍,因为这会中断扫描。

这仅是 R-Tree 扩展的限制。普通表 SQLite能够同时读取和写入。其他虚拟表 可能(也可能不会)这种能力。R-Tree 可以读取 并在某些情况下同时编写,如果它能弄清楚如何 在开始更新之前可靠地运行查询直至完成。但 您不应该指望每个查询都如此。一般来说,它是 最好避免同时对同一 R-Tree 运行查询和更新 时间。

如果您确实需要根据复杂的查询来更新 R-Tree 同样的 R 树,最好先运行复杂的查询并存储 结果在一个临时表中,然后根据值更新 R 树 存储在临时表中。

4. 有效使用 R*Trees

对于 3.24.0 之前的 SQLite 版本 (2018-06-04), R*Tree 索引存储的有关对象的唯一信息是 其整数 ID 及其边界框。其他信息需要 存储在单独的表中,并使用 R*Tree 索引 主键。对于上面的示例,可以创建一个辅助 表如下:

CREATE TABLE demo_data(
  id INTEGER PRIMARY KEY,  -- primary key
  objname TEXT,            -- name of the object
  objtype TEXT,            -- object type
  boundary BLOB            -- detailed boundary of object
);

在此示例中,demo_data.boundary 字段旨在保存一些 对象精确边界的二进制表示形式。 R*Tree 索引仅包含轴对齐的矩形边界,用于 对象。R*Tree 边界只是真实对象的近似值 边界。因此,通常的情况是 R*Tree 索引用于 将搜索范围缩小到候选对象列表,然后更详细 并对每个候选者进行昂贵的计算,以找出 候选人真正符合搜索条件。

眼:R*Tree 索引通常不提供确切的答案,而只是提供确切的答案 将潜在答案集从数百万减少到数十个。

假设 demo_data.boundary 字段包含一些专有数据描述 邮政编码的复杂二维边界,并假设 应用程序已使用 sqlite3_create_function() 接口来 创建了应用程序定义的函数“contained_in(boundary,lat,long)” 接受 demo_data.boundary 对象以及纬度和经度 如果纬度/经度包含在 边界。 人们可能会认为“contained_in()”是一个相对较慢的 我们不想太频繁调用的函数。 然后是查找主要邮政编码的有效方法 SQLite办公室将运行如下查询:

SELECT objname FROM demo_data, demo_index
 WHERE demo_data.id=demo_index.id
   AND contained_in(demo_data.boundary, 35.37785, -80.77470)
   AND minX<=-80.77470 AND maxX>=-80.77470
   AND minY<=35.37785  AND maxY>=35.37785;

请注意上面的查询是如何工作的:R*Tree 索引在外部运行 循环查找包含 SQLite 主办公室的条目 边界框。 对于找到的每一行,SQLite 都会查找 demo_data 表中的相应条目。然后,它使用边界 demo_data表中的字段作为 contained_in() 的参数 函数,如果该函数返回 true,那么我们知道 坐标位于该邮政编码边界内。

在不使用 R*Tree 索引的情况下,人们会得到相同的答案 使用以下更简单的查询:

SELECT objname FROM demo_data
 WHERE contained_in(demo_data.boundary, 35.37785, -80.77470);

后一个查询的问题在于它必须应用 contained_in() 函数添加到demo_data表中的所有条目。 在倒数第二个查询中使用 R*Tree 可减少 对整个表的一小部分子集的 contained_in() 函数的调用。 R*Tree索引本身并没有找到确切的答案,它只是 限制了搜索空间。

4.1. 辅助列

从 SQLite 版本 3.24.0 (2018-06-04) 开始,r 树表 可以具有存储任意数据的辅助列。 可以使用辅助柱代替 辅助表,例如“demo_data”。

辅助列在列名前标有“+”符号。 辅助列必须位于所有坐标边界列之后。 RTREE 表的总列数不能超过 100 列。换言之, 包含整数主键列的列计数, 坐标边界列和所有辅助列必须小于 100。 以下示例显示了一个包含辅助列的 r 树表,该表具有辅助列 等同于上面的两个表“demo_index”和“demo_data”:

CREATE VIRTUAL TABLE demo_index2 USING rtree(
   id,              -- Integer primary key
   minX, maxX,      -- Minimum and maximum X coordinate
   minY, maxY,      -- Minimum and maximum Y coordinate
   +objname TEXT,   -- name of the object
   +objtype TEXT,   -- object type
   +boundary BLOB   -- detailed boundary of object
);

通过将位置数据和相关信息组合成相同的 表、辅助柱可提供更清洁的模型 并减少加入的需要。 例如,demo_index 和 demo_data 之间的较早联接现在可以 写成一个简单的查询,如下所示:

SELECT objname FROM demo_index2
 WHERE contained_in(boundary, 35.37785, -80.77470)
   AND minX<=-80.77470 AND maxX>=-80.77470
   AND minY<=35.37785  AND maxY>=35.37785;

4.1.1. 限制

对于辅助列,只有列的名称很重要。 类型相关性将被忽略。 约束,例如 NOT NULL、UNIQUE、REFERENCES 或 CHECK 也被忽略。但是,未来的版本 的 SQLite 可能会开始关注类型亲和力和 约束,因此建议辅助列的用户离开 两者都为空白,以避免将来的兼容性问题。

5. 整数值 R 树

默认虚拟表 (“rtree”) 将坐标存储为 单精度(4 字节)浮点数。如果整数坐标 需要,请改用“rtree_i32”声明表:

CREATE VIRTUAL TABLE intrtree USING rtree_i32(id,x0,x1,y0,y1,z0,z1);

rtree_i32将坐标存储为 32 位有符号整数。 即使它使用整数存储值,rtree_i32虚拟的 table 仍然在内部使用浮点计算作为 R 树算法。

6. 自定义 R-Tree 查询

通过在 SELECT 查询的 WHERE 子句中使用标准 SQL 表达式, 程序员可以查询所有 R*Tree 条目 与特定边界框相交或包含在特定边界框中。 自定义 R*Tree 查询,使用 MATCH SELECT 的 WHERE 子句中的运算符,允许程序员查询 与任意区域或形状相交的 R*Tree 条目集,而不是 只是一个盒子。例如,此功能在计算 R*Tree 中从位于 在 3D 空间中。

自定义 R*Tree 查询的区域由 R*Tree 几何回调定义 由应用程序实现,并通过调用一个 SQLite 注册 以下两个 API:

int sqlite3_rtree_query_callback(
  sqlite3 *db,
  const char *zQueryFunc,
  int (*xQueryFunc)(sqlite3_rtree_query_info*),
  void *pContext,
  void (*xDestructor)(void*)
);
int sqlite3_rtree_geometry_callback(
  sqlite3 *db,
  const char *zGeom,
  int (*xGeom)(sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes),
  void *pContext
);

sqlite3_rtree_query_callback() 随 SQLite 版本 3.8.5 (2014-06-04) 一起提供,是首选接口。 sqlite3_rtree_geometry_callback() 较旧且不太灵活 支持向后兼容性的接口。

调用上述 API 之一会创建一个新的 SQL 函数,该函数由 第二个参数(zQueryFunc 或 zGeom)。当该 SQL 函数出现时 在 MATCH 运算符的右侧和 MATCH 运算符是 R*Tree 虚拟表中的任意列,然后是回调 由第三个参数(xQueryFunc 或 xGeom)定义,用于确定 如果特定对象或子树与所需区域重叠。

例如,可以使用如下所示的查询来查找所有 与圆重叠的 R*树条目居中 45.3,22.9 和 半径 5.0:

SELECT id FROM demo_index WHERE id MATCH circle(45.3, 22.9, 5.0)

无论哪种方式,自定义查询的 SQL 语法都是相同的 接口,sqlite3_rtree_geometry_callback() 或 sqlite3_rtree_query_callback(), 用于注册 SQL 函数。但是,较新的查询样式 回调使应用程序能够更好地控制查询的进行方式。

6.1. 遗留的 xGeom 回调

旧版 xGeom 回调使用四个参数进行调用。第一个 参数是指向 sqlite3_rtree_geometry 结构的指针,该结构提供 有关如何调用 SQL 函数的信息。第二个论点 是每个 R 树条目中的坐标数,并且始终相同 对于任何给定的 R*Tree。一维 R*Tree 的坐标数为 2, 4 表示 2 维 R*Tree,6 表示 3 维 R*Tree,依此类推。 第三个参数 aCoord[],是一个 nCoord 坐标数组,用于定义 要测试的边界框。最后一个参数是一个指针,其中 回调结果应该写入。结果为零 如果 aCoord[] 定义的边界框完全在外部 由 xGeom 回调定义的区域,如果结果为非零 边界框位于 xGeom 区域内或与 xGeom 区域重叠。xGeom 回调通常应返回SQLITE_OK。如果 xGeom 返回任何其他内容 比 SQLITE_OK,则 R 树查询将中止并显示错误。

第一个参数的sqlite3_rtree_geometry结构 xGeom 回调指向的结构如下所示。一模一样 sqlite3_rtree_geometry 结构用于同一 MATCH 运算符的每个回调 查询。sqlite3_rtree_geometry的内容 结构由 SQLite 初始化,但 随后未修改。回调可以自由地对 结构的 pUser 和 xDelUser 元素(如果需要)。

typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
struct sqlite3_rtree_geometry {
  void *pContext;                 /* Copy of pContext passed to s_r_g_c() */
  int nParam;                     /* Size of array aParam */
  double *aParam;                 /* Parameters passed to SQL geom function */
  void *pUser;                    /* Callback implementation user data */
  void (*xDelUser)(void *);       /* Called by SQLite to clean up pUser */
};

sqlite3_rtree_geometry的 pContext 成员 结构始终设置为 pContext 的副本 参数传递给 sqlite3_rtree_geometry_callback() 当 回调已注册。aParam[] 数组(大小 nParam)包含参数 传递给 MATCH 运算符右侧的 SQL 函数的值。 在上面的示例“circle”查询中,nParam 将设置为 3,aParam[] 数组将包含三个值 45.3、22.9 和 5.0。

sqlite3_rtree_geometry结构的 pUser 和 xDelUser 成员是 最初设置为 NULL。pUser 变量可以通过回调设置 实现为可能对后续有用的任意值 在同一查询中调用回调(例如,一个 指向用于测试区域交集的复杂数据结构的指针)。 如果 xDelUser 变量设置为非 NULL 值,则在 查询已完成运行,SQLite 会自动调用它,并带有 pUser 变量的值作为唯一参数。换言之,xDelUser 可以设置为 pUser 值的析构函数。

xGeom 回调始终对 r 树进行深度优先搜索。

6.2. 新的 xQueryFunc 回调

较新的 xQueryFunc 回调从 r 树接收更多信息 每次调用时的查询引擎,并将更多信息发送回查询引擎 在它返回之前。 为了帮助保持接口的可管理性,xQueryFunc 回调发送和接收 来自查询引擎的信息作为 sqlite3_rtree_query_info结构:

struct sqlite3_rtree_query_info {
  void *pContext;                   /* pContext from when function registered */
  int nParam;                       /* Number of function parameters */
  sqlite3_rtree_dbl *aParam;        /* value of function parameters */
  void *pUser;                      /* callback can use this, if desired */
  void (*xDelUser)(void*);          /* function to free pUser */
  sqlite3_rtree_dbl *aCoord;        /* Coordinates of node or entry to check */
  unsigned int *anQueue;            /* Number of pending entries in the queue */
  int nCoord;                       /* Number of coordinates */
  int iLevel;                       /* Level of current node or entry */
  int mxLevel;                      /* The largest iLevel value in the tree */
  sqlite3_int64 iRowid;             /* Rowid for current entry */
  sqlite3_rtree_dbl rParentScore;   /* Score of parent node */
  int eParentWithin;                /* Visibility of parent node */
  int eWithin;                      /* OUT: Visiblity */
  sqlite3_rtree_dbl rScore;         /* OUT: Write the score here */
  /* The following fields are only available in 3.8.11 and later */
  sqlite3_value **apSqlParam;       /* Original SQL values of parameters */
};

sqlite3_rtree_query_info结构的前五个字段是相同的 对sqlite3_rtree_geometry结构,并具有完全相同的含义。 sqlite3_rtree_query_info结构还包含 nCoord 和 aCoord 字段 与 xGeom 回调中同名参数的含义相同。

xQueryFunc 必须将 sqlite3_rtree_query_info 的 eWithin 字段设置为 其中一个值NOT_WITHIN、PARTLY_WITHIN 或 FULLY_WITHIN,具体取决于是否 或者 aCoord[] 定义的边界框是否完全在区域之外, 分别与区域重叠或完全位于区域内。在 此外,xQueryFunc 必须将 rScore 字段设置为非负值,即 指示应分析查询的子树和条目的顺序 然后回来了。首先处理较小的分数。

顾名思义,R*Tree 被组织成一棵树。每个节点的 树是一个边界框。树的根是一个封装的边界框 树的所有元素。根下方是许多子树(通常 20 个或更多),每个都有自己较小的边界框,每个框都包含一些 R*Tree 条目的子集。子树可以有子树,依此类推 直到最后到达树的叶子,这是真正的 R*Tree 条目。

通过将根节点作为唯一条目来初始化 R*Tree 查询 在按 rScore 排序的优先级队列中。 查询通过从具有 最低分。如果该条目是叶子(意味着它是实际的 R*Tree 条目而不是子树),然后该条目 作为查询结果的一行返回。 如果提取的优先级队列条目是节点(子树), 然后,将该节点的下一个子节点传递给 xQueryFunc 回调。 如果节点具有更多子节点,则该节点将返回到优先级队列。 否则,它将被丢弃。xQueryFunc 为其的子元素 回调集 eWithin 添加到 PARTLY_WITHIN 或 FULLY_WITHIN 优先级队列,使用回调提供的分数。子元素 返回NOT_WITHIN被丢弃。查询将运行,直到优先级队列为 空。

R*Tree 中的每个叶条目和节点(子树)都有一个整数“级别”。 叶子的等级为 0。第一个包含叶子的子树有 A 级 1.R*Tree 的根具有最大的级别值。这 sqlite3_rtree_query_info结构中的 mxLevel 条目是 R*Tree 的根。sqlite3_rtree_query_info 中的 iLevel 条目给出了 被询问对象的水平。

大多数 R*Tree 查询都使用深度优先搜索。这是通过设置 rScore 等于 iLevel。深度优先搜索通常是首选,因为它 最小化优先级队列中的元素数,从而减少内存 要求和处理速度。但是,某些应用程序可能更喜欢 广度优先搜索,可以通过将 rScore 设置为 mxLevel-iLevel 来完成。 通过为 rScore 创建更复杂的公式,应用程序可以行使 详细控制子树和叶子的搜索顺序 返回 R*Tree 条目。例如,在具有许多 数以百万计的 R*Tree 条目,则 rScore 可以排列成 首先返回最大或最重要的条目,允许 应用程序快速显示最重要的信息,以及 在可用时填写较小和不太重要的详细信息。

sqlite3_rtree_query_info结构的其他信息字段包括 如果需要,可供 xQueryFunc 回调使用。iRowid 字段 是元素的 rowid(R*Tree 中 3 到 11 列中的第一列) 正在考虑中。iRowid 仅对叶子有效。eParentWithin 和 rParentScore 值是 eWithin 和 rScore 值的副本 包含当前行的子树。anQueue 字段是一个数组 的 mxLevel+1 个无符号整数,用于表示当前元素数 每个级别的优先级队列。

6.3. 自定义查询的其他注意事项

自定义 R*Tree 查询函数的 MATCH 运算符必须是顶级运算符 WHERE 子句的 AND 连接项,否则将无法使用 由 R*Tree 查询优化器执行,则查询将无法运行。 如果 MATCH 运算符连接到 WHERE 子句的其他术语 例如,通过 OR 运算符,查询将失败并显示错误。

同一 WHERE 子句中允许使用两个或多个 MATCH 运算符,只要 因为它们由 AND 运算符连接。然而 R*Tree 查询引擎仅包含单个优先级队列。优先级 分配给搜索中每个节点的优先级是任何 的 MATCH 运算符。

7. 实施细节

以下各节介绍了 R*Tree 实现的一些低级细节: 这对于故障排除或性能分析可能很有用。

7.1. 影子表

一个 R*Tree 索引的内容实际上存储在三个普通的 SQLite 表的名称派生自 R*Tree 的名称。这些 三个表称为“影子表”。这是他们的架构:

CREATE TABLE %_node(nodeno INTEGER PRIMARY KEY, data)
CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode)
CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno)

每个影子表名称中的“%”将替换为 R*Tree 虚拟表。因此,如果 R*Tree 表的名称是“xyz”,那么 三个影子表分别为“xyz_node”、“xyz_parent”和“xyz_rowid”。

每个 R*Tree 节点的 %_node 表中都有一个条目。一 R*Tree 节点由一个或多个彼此接近的条目组成。 树的 R*Tree 节点。除根节点以外的所有节点都有一个 %_parent 影子表中标识父节点的条目。 R*Tree 中的每个条目都有一个 rowid。%_rowid 影子表映射条目 rowids 添加到包含该条目的节点。

附加到 %_rowid 表的额外列包含 辅助列的内容。这些额外的名称 %_rowid 列可能与 实际辅助列名称。

7.2. 使用 rtreecheck() SQL 函数进行完整性检查

标量 SQL 函数 rtreecheck(R) 或 rtreecheck(S,R) 运行 对数据库 S 中包含的名为 R 的 rtree 表进行完整性检查。 该函数返回发现的任何问题的人类语言描述, 如果一切正常,则使用字符串“ok”。在 R*Tree 上运行 rtreecheck() 虚拟表integrity_check类似于在 数据库。

示例:验证名为“demo_index”的 R*Tree 格式是否正确 和内部一致,运行:

SELECT rtreecheck('demo_index');

rtreecheck() 函数执行以下检查:

  1. 对于 r 树结构(%_node 表)中的每个单元格,:

    1. 对于每个维度,(coord1 <= coord2)。

    2. 除非单元位于根节点上,否则单元是有界的 由父节点上的父单元格。

    3. 对于叶节点,%_rowid 对应于单元格的 rowid 值的表 指向正确的节点。

    4. 对于非叶节点上的单元格,在 %_parent 表从单元的子节点映射到 它所在的节点。

  2. %_rowid 表中的条目数相同 因为 r 树结构中有叶细胞,并且有 是对应于 %_rowid 表中的每个条目的叶单元格。

  3. %_parent 表中的条目数相同 因为 R 树结构中有非叶细胞,并且 有一个非叶单元格,对应于 %_parent 表。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/561216.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[论文阅读链接]

CVPR2023&#xff1a;Learning Human-to-Robot Handovers from Point Clouds http://t.csdnimg.cn/OfSnShttp://t.csdnimg.cn/OfSnS仿真工具&#xff1a;dm_control: Software and Tasks for Continuous Control dm_control 翻译: Software and Tasks for Continuous Control…

Idea中使用Git详细教学

目录 一、配置 Git 二、创建项目远程仓库 三、初始化本地仓库 方法一&#xff1a; 方法二&#xff1a; 四、连接远程仓库 五、提交与拉取到本地仓库 六、推送到远程仓库 七、克隆远程仓库到本地 方法一&#xff1a; 方法二&#xff1a; 八、Git分支操作 一、配置 G…

嵌入式学习57-ARM7(字符设备驱动框架led)

知识零碎&#xff1a; kernel 内核 printk 内核打印 cat /proc/devices mknod ? 查看指令 gcc -oapp hello.c 字符设备驱动流程 字符设备程序运行流程 gcc中-c和-o是编译时可选的参数 -c …

使用python-can和cantools实现arxml报文解析、发送和接收的完整指南

文章目录 背景一、硬件支持二、环境准备1、python解释器安装2、python库安装 三、 收发案例四、 方法拓展1、canoe硬件调用2、回调函数介绍 结论 背景 在汽车行业中&#xff0c;CAN (Controller Area Network) 总线是用于车辆内部通信的关键技术。arxml文件是一种用于描述CAN消…

linux下摄像头设置固定的设备名

目录 2.热插拔udev机制 3.设置udev的规则 1.查看usb ID 2. 查看usb设备的信息 3.编译规则 4.拓展 1.问题的出现 通过我之前的文章配置完摄像头的开机自启动之后我们会发现有的时候会出现启动不了的情况&#xff0c;通过实验我发现是摄像头的设备名发生了改变&#xff0c;…

网络安全产品---态势感知EDR

态势感知 what SA&#xff0c;Situational Awareness 是对一定时间和空间内的环境元素进行感知&#xff0c;并对这些元素的含义进行理解&#xff0c;最终预测这些元素在未来的发展状态。 why 安全防护思想已经从过去的被动防御向主动防护和智能防护转变。如果不做到主动防御…

【JS】js数字转k、w结尾 | 1000 = 1k

问题 数字转k、w结尾 如&#xff1a;10001k 100001w 码 /*** 数字转k,w* param {Number} num * returns String*/ const numberTokw num > {if (num < 1000) return numlet endStr w,numVal 10000;if (num > 999 && num < 10000) {endStr knumVal …

嵌入式物联网实战开发笔记-乐鑫ESP32芯片功能对比以及功能选型【doc.yotill.com】

乐鑫ESP32入门到精通项目开发参考百例下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ATvRnAZvxkev-PJfd3EAPg?pwd4e33 提取码&#xff1a;4e33 2.1 初识 ESP32 ESP32-S3 是一款低功耗的 MCU 系统级芯片 (SoC)&#xff0c;支持 2.4 GHz Wi-Fi 和低功耗蓝牙 (…

minio如何配置防盗链

MinIO 是一个开源的对象存储服务器&#xff0c;用于存储大量的数据&#xff0c;同时提供了丰富的功能和 API。配置防盗链可以帮助你控制谁可以访问存储在 MinIO 上的对象。以下是在 MinIO 中配置防盗链的一般步骤&#xff1a; 编辑 config.json 文件&#xff1a; 找到 MinIO 服…

【游戏专区】飞机大战

打过飞机的人都知道&#xff0c;不是那么好打滴&#xff0c;求得麻袋&#xff0c;甩掉你那脑子里的黄色信息。活不多说&#xff0c;我们开始吧。 1、easyX的原理 基于Windows图形编程&#xff0c;将Windows下的复杂程序过程进行封装&#xff0c;仅给用户提供一个简单熟悉的接…

第63天:服务攻防-框架安全CVE 复现DjangoFlaskNode.JSJQuery

目录 思维导图 案例一&#xff1a;JavaScript-开发框架安全-Jquery&Node node.js目录穿越 CVE-2021-21315命令执行 Jquery CVE-2018-9207 案例二&#xff1a;Python-开发框架安全-Django&Flask django cve_2019_14234 CVE-2021-35042 flask ssti 思维导图 案…

【网站项目】党员之家服务系统小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

【电力工程】电力大数据和云架构智能AI服务平台研发建设项目可行性研究报告范例

1、项目概况 本项目拟进行基于电力大数据和云架构的智能 AI 服务平台的研究,具体包括电力多元大数据中心、技术中台、数据中台和智能 AI 中台,基于电力大数据云平台基础构建 BI 可视化开发平台和智能 AI 服务平台。 该项目的实施旨在引领公司在大数据领域发展的新趋势,从功…

SQLite运行时可加载扩展(三十五)

返回&#xff1a;SQLite—系列文章目录 上一篇:SQLite轻量级会话扩展&#xff08;三十四&#xff09; 下一篇&#xff1a;SQLite—系列文章目录 1. 概述 SQLite 能够在运行时加载扩展&#xff08;包括新的应用程序定义的 SQL 函数、整理序列、虚拟表和 VFS&#xff09…

TBWeb开发版V3.2.6免授权无后门Chatgpt系统源码下载及详细安装教程

TBWeb系统是基于 NineAI 二开的可商业化 TB Web 应用&#xff08;免授权&#xff0c;无后门&#xff0c;非盗版&#xff0c;已整合前后端&#xff0c;支持快速部署&#xff09;。相比稳定版&#xff0c;开发版进度更快一些。前端改进&#xff1a;对话页UI重构&#xff0c;参考C…

Go源码--Strings库

1. 简介 strings库 存储了 一些针对 字符串的具体操作 其 代码短小精悍 可以学习到很多编程的思路 尤其是 涉及到字符串使用性能的方面&#xff0c;其源码库有好多的优秀案例可以学习。向强者对齐不一定成为强者&#xff0c;但向弱者对齐一定变为弱者。 介绍思路是先介绍 stri…

9.列表渲染

列表渲染 我们可以使用 v-for 指令基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式的特殊语法&#xff0c;其中 items 是源数据的数组&#xff0c;而 item 是迭代项的别名 <template><div><p v-for"item in names">{{ it…

基于 RT-Thread 的 PPP Device 软件包的详细使用以及AT通用配网过程

一、AT通用上网过程 网络初始化流程 一般情况如下 1、先上电复位模块&#xff1b; 2、间隔一直发送 AT\r 等待模组响应,表示模组启动&#xff0c;并且调试好了波特率&#xff1b; 3、发送ATCPIN?\r 测试卡是否插好&#xff1b; 4、发送 ATCSQ\r 查询信号质量&#xff0c;只有…

【QT进阶】Qt http编程之后端API测试工具postman使用介绍

往期回顾 【QT进阶】Qt Web混合编程之使用ECharts显示各类折线图等-CSDN博客 【QT进阶】Qt Web混合编程之实现ECharts数据交互动态修改-CSDN博客 【QT进阶】Qt http编程之http与https简单介绍-CSDN博客 【QT进阶】Qt http编程之后端API测试工具postman使用介绍 其实这个工具的…

【简单介绍下Faiss原理和使用】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…