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 keyminX, maxX,      -- Minimum and maximum X coordinateminY, 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_indexWHERE minX<=-80.77470 AND maxX>=-80.77470AND 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 BWHERE A.maxX>=B.minX AND A.minX<=B.maxXAND A.maxY>=B.minY AND A.minY<=B.maxYAND B.id=28269;

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

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

SELECT id FROM demo_indexWHERE 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_indexWHERE 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 keyobjname TEXT,            -- name of the objectobjtype TEXT,            -- object typeboundary 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_indexWHERE demo_data.id=demo_index.idAND contained_in(demo_data.boundary, 35.37785, -80.77470)AND minX<=-80.77470 AND maxX>=-80.77470AND minY<=35.37785  AND maxY>=35.37785;

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

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

SELECT objname FROM demo_dataWHERE 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 keyminX, maxX,      -- Minimum and maximum X coordinateminY, 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_index2WHERE contained_in(boundary, 35.37785, -80.77470)AND minX<=-80.77470 AND maxX>=-80.77470AND 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 表。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端三剑客 HTML+CSS+JavaScript ① 基础入门

光永远会照亮你 —— 24.4.18 一、C/S架构和B/S架构 C:Client&#xff08;客户端&#xff09; B:Browser&#xff08;浏览器&#xff09; S:Server&#xff08;服务器&#xff09; C/S 架构&#xff1a; B/S 架构&#xff1a; 大型专业应用、安全性要求较高的应用&#xff0c;还…

binary tree Leetcode 二叉树算法题

144.二叉树的前序遍历 前序遍历是&#xff1a;根-左-右 所以记录序列的的时候放在最前面 递归 class Solution {List<Integer> ans new ArrayList<>();public List<Integer> preorderTraversal(TreeNode root) {if(root null) return ans;ans.add(root…

【HCIP】OSPF的高级特性

OSPF的高级特性1 --- 不规则区域 一、OSPF不规则区域类型 产生原因&#xff1a;区域划分不合理&#xff0c;导致的问题 1、非骨干区域无法和骨干区域保持连通 2、骨干区域被分割 造成后果&#xff1a;非骨干区域没和骨干区域相连&#xff0c;导致ABR将不会帮忙转发区域间的路由…

【数据结构练习题】堆——top-k问题

♥♥♥♥♥个人主页♥♥♥♥♥ ♥♥♥♥♥数据结构练习题总结专栏♥♥♥♥♥ ♥♥♥♥♥上一章&#xff1a;【数据结构练习题】二叉树(1)——1.相同的树2.另一颗树的子树3.翻转二叉树4.平衡二叉树5.对称二叉树♥♥♥♥♥ 文章目录 1.top-k问题1.1问题描述1.2思路分析1.3绘图分析…

理光打印机设置扫描文件到共享文件夹教程(线上和现场)

在线设置。 1.点击用户工具/计数器按钮。 2.点击系统设置。 3.点击端口设置&#xff0c;点击机器IPV4地址。 4.获得打印机IP地址。 5.回到共享电脑&#xff0c;新建一个账户或者使用当前账户&#xff0c;为了隐私安全起见&#xff0c;最好设置密码。 6.关闭防火墙。 7.启用…

Mac 下安装PostgreSQL经验

使用homebrew终端软件管理器去安装PostgreSQL 如果没有安装brew命令执行以下命令 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 沙果开源物联网系统 SagooIoT | SagooIoT 1.使用命令安装postgreSQL brew i…

JavaScript之分时函数、分时间段渲染页面、提高用户体验、参数归一化、高阶函数、分段、appendChild、requestIdleCallback

MENU 前言效果图html原始写法优化方式一(参数归一化)优化方式二(当浏览器不支持requestIdleCallback方法的时候)优化方式三(判断环境) 前言 当前需要向页面插入十万个div元素&#xff0c;如果使用普通的渲染方式&#xff0c;会造成延迟。这时候就需要通过分时函数来实现渲染了。…

【Pytorch】VSCode实用技巧 - 默认终端修改为conda activate pytorch

VScode修改配置使得启动终端为conda环境 文章目录 VScode修改配置使得启动终端为conda环境1、找到settings.json 文件2、查找 conda / mamba 相关内容3、编辑 settings.json 文件4、异常处理5、补充检验 VScode跑项目&#xff0c;在启动pytorch项目时往往会有千奇百怪的问题&am…

大学生前端学习第一天:了解前端

引言&#xff1a; 哈喽&#xff0c;各位大学生们&#xff0c;大家好呀&#xff0c;在本篇博客&#xff0c;我们将引入一个新的板块学习&#xff0c;那就是前端&#xff0c;关于前端&#xff0c;GPT是这样描述的&#xff1a;前端通常指的是Web开发中用户界面的部分&#xff0c;…

数据库设计的三范式

简单来说就是&#xff1a;原子性、唯一性、独立性 后一范式都是在前一范式已经满足的情况进行附加的内容 第一范式&#xff08;1NF&#xff09;&#xff1a;原子性 存储的数据应不可再分。 不满足原子性&#xff1a; 满足原子性&#xff1a; 第二范式&#xff08;2NF&#xf…

探索设计模式的魅力:开启智慧之旅,AI与机器学习驱动的微服务设计模式探索

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索AI与机器学习驱动的微服务设计模式之旅✨ 亲爱的科技爱好者们&#xff0c;有没…

LabVIEW多设备控制与数据采集系统

LabVIEW多设备控制与数据采集系统 随着科技的进步&#xff0c;自动化测试与控制系统在工业、科研等领域的应用越来越广泛。开发了一种基于LabVIEW平台开发的多设备控制与数据采集系统&#xff0c;旨在解决多设备手动设置复杂、多路数据显示不直观、数据存储不便等问题。通过RS…

【记录】Python3|Selenium 下载 PDF 不预览不弹窗(2024年)

版本&#xff1a; Chrome 124Python 3.12Selenium 4.19.0 版本与我有差异不要紧&#xff0c;只要别差异太大比如 Chrome 用 57 之前的版本了&#xff0c;就可以看本文。 如果你从前完全没使用过、没安装过Selenium&#xff0c;可以参考这篇博客《【记录】Python3&#xff5c;Se…

密码学 | 承诺:绑定性 + 隐藏性

&#x1f951;原文&#xff1a;承诺方案&#xff08;Commitment&#xff09;学习笔记 &#x1f951;写在前面&#xff1a; 本文属搬运博客&#xff0c;自己留存学习。本文只会讲承诺的两个安全属性&#xff0c;不会再讲解承诺的定义。 正文 承诺方案需要满足两个安全属性&…

Oracle之SQL plus的一些经验心得

每次登入SQL plus后,不知道时哪个用户登入,非常不方便,只能使用show user查看。 以下时可以通过一些设置实现上述的效果,知道时哪个用户登入,和实现输出效果等 1)SQL plus使用细则 SQL plus登录时,我们可以设置一些通用的设置,在每次登入SQL plus的时候生效。 [root@c…

安装Zipkin

官网&#xff1a;https://zipkin.io/pages/quickstart.html Jar包方式 下载 方式一&#xff1a;百度网盘下载 链接&#xff1a;https://pan.baidu.com/s/1PRV1RamJ8IWX32IJb7jw3Q?pwde8vu 提取码&#xff1a;e8vu 方式二&#xff1a;Central Repository: io/zipkin/zipk…

react 项目路由配置(react-router-dom 版本 v6.3、v6.4)

根据 react-router-dom 的版本&#xff0c;有不同的方式 一、react-router-dom v6.3 用到的主要 api: BrowserRouteruseRoutesOutlet 下面是详细步骤&#xff1a; 1、index.js BrowserRouter 用来实现 单页的客户端路由使用 BrowserRouter 包裹 App放在 顶级 位置&#x…

(1)认识人工智能

第一章 认识人工智能 引言 本人目前大三&#xff0c;双非一本的人工智能专业&#xff0c;代码能力不算太差&#xff0c;做过项目&#xff0c;也打了比赛&#xff0c;获了奖&#xff0c;但是走技术路线总会有否定自己的感觉&#xff0c;可能是感觉自己的才能没有在搞技术方面实…

小红书电商运营实战课,从0打造全程实操(65节视频课)

课程内容&#xff1a; 1.小红书的电商介绍 .mp4 2.小红书的开店流程,mp4 3.小红书店铺基础设置介绍 ,mp4 4.小红书店铺产品上架流程 .mp4 5.客服的聊天过程和子账号建立 .mp4 6.店铺营销工具使用和后台活动参加 .mp4 7.小红书产品上架以及拍单教程,mp4 8.小红书如何选品…

数据分析_商品维度占比及变化可视化分析(Pandas和Matplotlib)

数据分析_商品维度占比及变化可视化分析(Pandas和Matplotlib) 分析维度包括: 各商品年度销量占比 各商品月度销量变化 构建测试数据 这里你可以了解到: 如何生成时间相关的数据。 如何从列表&#xff08;可迭代对象&#xff09;中生成随机数据。 Pandas 的 DataFrame 自…