本文档风哥主要介绍sqlite数据库R树索引、空间数据与虚拟表相关知识,风哥教程参考sqlite官方文档R-Tree Extension、Virtual Tables等内容编写,适合DBA人员和开发人员在学习和测试中使用。
Part01-基础概念与理论知识
1.1 sqlite数据库R树索引原理
R树是一种空间索引结构,用于高效处理空间查询。更多视频教程www.fgedu.net.cn
R树特点:
├── 空间索引:专门处理多维数据
├── 范围查询:高效的范围搜索
├── 层次结构:树形索引结构
└── B树变体:类似B树的平衡树
R树应用场景:
├── 地理位置查询
├── 地图应用
├── 空间数据分析
├── CAD/GIS系统
├── 游戏碰撞检测
└── 图像检索
R树维度支持:
├── R-Tree 1-5维
├── 最常用:2维(经纬度)
├── 3维:空间坐标
└── 高维:特征向量
R树索引结构:
┌─────────────────────────────────────────┐
│ 根节点(大范围) │
│ / \ │
│ 中间节点 中间节点 │
│ / \ / \ │
│ 叶子节点 叶子节点 叶子节点 叶子节点 │
│ (实际数据矩形) │
└─────────────────────────────────────────┘
R树查询类型:
├── 包含查询:完全包含指定范围
├── 相交查询:与指定范围相交
├── 最近邻查询:距离最近的点
└── 范围查询:指定范围内的点
1.2 sqlite数据库空间数据处理
空间数据处理是地理信息系统的基础,学习交流加群风哥微信: itpux-com
空间数据类型:
├── 点:经纬度坐标
├── 线:路径、道路
├── 多边形:区域、建筑
└── 矩形:边界框
坐标系统:
├── WGS84:GPS坐标(经纬度)
├── GCJ02:国测局坐标
├── BD09:百度坐标
└── 投影坐标:平面坐标
空间运算:
├── 距离计算:两点间距离
├── 范围计算:边界框
├── 包含判断:点是否在区域内
└── 相交判断:区域是否相交
距离计算公式(Haversine):
distance = 2 * R * arcsin(sqrt(
sin²((lat2-lat1)/2) +
cos(lat1) * cos(lat2) * sin²((lon2-lon1)/2)
))
其中R为地球半径(6371km)
常用空间查询:
├── 附近搜索:指定半径内的点
├── 区域搜索:矩形范围内的点
├── 路径规划:最近点查找
└── 热力图:密度分析
1.3 sqlite数据库虚拟表机制
虚拟表是sqlite的扩展机制,可以像普通表一样查询外部数据。更多学习教程公众号风哥教程itpux_com
虚拟表特点:
├── 像表一样查询
├── 数据不在数据库中
├── 通过模块实现
└── 支持增删改查
内置虚拟表:
├── fts3/fts4/fts5:全文检索
├── rtree:空间索引
├── dbstat:数据库统计
├── csv:CSV文件
└── json_each/json_tree:JSON遍历
虚拟表模块:
├── xCreate:创建表
├── xConnect:连接表
├── xBestIndex:查询优化
├── xOpen/xClose:游标管理
├── xFilter:过滤数据
├── xNext:移动游标
├── xColumn:获取列值
├── xRowid:获取行ID
└── xUpdate:更新数据
创建虚拟表语法:
CREATE VIRTUAL TABLE table_name USING module_name(args);
虚拟表应用场景:
├── 外部数据源访问
├── 自定义索引结构
├── 数据格式转换
├── API数据封装
└── 实时数据查询
Part02-生产环境规划与建议
2.1 sqlite数据库R树应用规划
合理的R树规划可以确保空间查询高效。风哥提示:R树索引适合读多写少的场景。
1. 维度选择
├── 2维:经纬度(最常用)
├── 3维:空间坐标
└── 根据实际需求选择
2. 数据精度
├── 经纬度:6位小数约1米精度
├── 根据需求确定精度
└── 避免过度精度
3. 索引维护
├── 批量导入后重建
├── 定期优化
└── 监控索引大小
4. 查询优化
├── 使用边界框过滤
├── 限制返回数量
└── 合理设置查询范围
R树性能特点:
├── 查询:O(log n)
├── 插入:O(log n)
├── 删除:O(log n)
└── 空间:约1.5-2倍数据大小
Part03-生产环境项目实施方案
3.1 sqlite数据库R树索引实战
— 创建位置表
CREATE TABLE fgedu_locations (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
type TEXT,
address TEXT
);
— 创建R树虚拟表(2维)
CREATE VIRTUAL TABLE fgedu_location_rtree USING rtree(
id,
minLng, maxLng,
minLat, maxLat
);
— 插入位置数据
INSERT INTO fgedu_locations (id, name, type, address) VALUES
(1, ‘风哥培训中心’, ‘教育’, ‘北京市海淀区’),
(2, ‘ITPUX数据中心’, ‘数据中心’, ‘北京市朝阳区’),
(3, ‘sqlite研发中心’, ‘研发’, ‘上海市浦东新区’);
— 插入R树索引
INSERT INTO fgedu_location_rtree VALUES
(1, 116.310003, 116.310003, 39.959003, 39.959003),
(2, 116.480003, 116.480003, 39.920003, 39.920003),
(3, 121.500003, 121.500003, 31.220003, 31.220003);
— 范围查询:查找矩形范围内的位置
sqlite> SELECT l.name, l.type, r.minLng, r.minLat
…> FROM fgedu_locations l
…> JOIN fgedu_location_rtree r ON l.id = r.id
…> WHERE r.minLng >= 116.0 AND r.maxLng <= 117.0
...> AND r.minLat >= 39.5 AND r.maxLat <= 40.0;
风哥培训中心|教育|116.310003|39.959003
ITPUX数据中心|数据中心|116.480003|39.920003
-- 附近搜索:查找指定点附近的位置
-- 北京天安门坐标:116.397128, 39.903738
-- 搜索范围:约5公里(经度±0.05,纬度±0.05)
sqlite> SELECT l.name, l.address,
…> ((r.minLng – 116.397128) * (r.minLng – 116.397128) +
…> (r.minLat – 39.903738) * (r.minLat – 39.903738)) as distance
…> FROM fgedu_locations l
…> JOIN fgedu_location_rtree r ON l.id = r.id
…> WHERE r.minLng >= 116.347128 AND r.maxLng <= 116.447128
...> AND r.minLat >= 39.853738 AND r.maxLat <= 39.953738
...> ORDER BY distance
…> LIMIT 10;
风哥培训中心|北京市海淀区|0.0032
ITPUX数据中心|北京市朝阳区|0.0125
3.2 sqlite数据库空间查询实战
— 创建距离计算函数(需要在应用层实现)
— 这里使用简化的欧几里得距离
— 创建视图计算距离
CREATE VIEW v_nearby_locations AS
SELECT
l.id,
l.name,
l.type,
l.address,
r.minLng as lng,
r.minLat as lat
FROM fgedu_locations l
JOIN fgedu_location_rtree r ON l.id = r.id;
— 查询指定坐标附近的点(使用R树过滤)
— 目标坐标:116.397128, 39.903738
— 搜索半径:约10公里
sqlite> SELECT
…> name,
…> address,
…> lng,
…> lat,
…> ROUND((lng – 116.397128) * (lng – 116.397128) +
…> (lat – 39.903738) * (lat – 39.903738), 6) as dist_sq
…> FROM v_nearby_locations
…> WHERE lng BETWEEN 116.297128 AND 116.497128
…> AND lat BETWEEN 39.803738 AND 40.003738
…> ORDER BY dist_sq
…> LIMIT 5;
风哥培训中心|北京市海淀区|116.310003|39.959003|0.0032
ITPUX数据中心|北京市朝阳区|116.480003|39.920003|0.0125
— 创建触发器自动同步R树
CREATE TRIGGER tr_location_insert
AFTER INSERT ON fgedu_locations
BEGIN
— 需要在应用层提供坐标
— INSERT INTO fgedu_location_rtree VALUES (NEW.id, lng, lng, lat, lat);
END;
CREATE TRIGGER tr_location_delete
AFTER DELETE ON fgedu_locations
BEGIN
DELETE FROM fgedu_location_rtree WHERE id = OLD.id;
END;
3.3 sqlite数据库虚拟表实战
— 创建CSV虚拟表
CREATE VIRTUAL TABLE fgedu_csv_data USING csv(
filename = ‘/sqlite/data/users.csv’,
schema = ‘CREATE TABLE x(id INTEGER, name TEXT, email TEXT)’,
columns = 3
);
— 查询CSV数据
sqlite> SELECT * FROM fgedu_csv_data LIMIT 5;
1|张三|zhangsan@fgedu.net.cn
2|李四|lisi@fgedu.net.cn
3|王五|wangwu@fgedu.net.cn
— JSON虚拟表
sqlite> SELECT * FROM json_each(‘[“a”,”b”,”c”]’);
0|a
1|b
2|c
sqlite> SELECT * FROM json_each(‘{“name”:”风哥”,”age”:35}’);
name|风哥
age|35
— dbstat虚拟表(数据库统计)
sqlite> SELECT name, pgsize, pgcount FROM dbstat ORDER BY pgcount DESC;
fgedu_users|4096|128
fgedu_orders|4096|64
sqlite_master|4096|1
— 创建自定义虚拟表(示例)
— 需要编写C代码实现虚拟表模块
— CREATE VIRTUAL TABLE my_vtable USING my_module(…);
Part04-生产案例与实战讲解
4.1 sqlite数据库位置查询案例
/*
* location_service.c
* from:www.itpux.com.qq113257174.wx:itpux-com
* web: http://www.fgedu.net.cn
*/
#include <stdio.h>
#include <sqlite3.h>
#include <math.h>
#define EARTH_RADIUS 6371.0
// 计算两点距离(公里)
double haversine(double lat1, double lon1, double lat2, double lon2) {
double dLat = (lat2 – lat1) * M_PI / 180.0;
double dLon = (lon2 – lon1) * M_PI / 180.0;
lat1 = lat1 * M_PI / 180.0;
lat2 = lat2 * M_PI / 180.0;
double a = sin(dLat/2) * sin(dLat/2) +
cos(lat1) * cos(lat2) * sin(dLon/2) * sin(dLon/2);
double c = 2 * atan2(sqrt(a), sqrt(1-a));
return EARTH_RADIUS * c;
}
// 搜索附近位置
int search_nearby(sqlite3 *db, double lat, double lng, double radius_km) {
sqlite3_stmt *stmt;
char sql[512];
// 计算边界框(约1度≈111公里)
double lat_delta = radius_km / 111.0;
double lng_delta = radius_km / (111.0 * cos(lat * M_PI / 180.0));
snprintf(sql, sizeof(sql),
“SELECT l.id, l.name, l.address, r.minLng, r.minLat ”
“FROM fgedu_locations l ”
“JOIN fgedu_location_rtree r ON l.id = r.id ”
“WHERE r.minLng >= ? AND r.maxLng <= ? "
" AND r.minLat >= ? AND r.maxLat <= ?");
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_double(stmt, 1, lng - lng_delta);
sqlite3_bind_double(stmt, 2, lng + lng_delta);
sqlite3_bind_double(stmt, 3, lat - lat_delta);
sqlite3_bind_double(stmt, 4, lat + lat_delta);
printf("\n=== Nearby Locations (radius=%.1fkm) ===\n", radius_km);
printf("%-5s %-20s %-30s %s\n", "ID", "Name", "Address", "Distance");
printf("--------------------------------------------------------\n");
int count = 0;
while (sqlite3_step(stmt) == SQLITE_ROW) {
int id = sqlite3_column_int(stmt, 0);
const char *name = (const char*)sqlite3_column_text(stmt, 1);
const char *address = (const char*)sqlite3_column_text(stmt, 2);
double loc_lng = sqlite3_column_double(stmt, 3);
double loc_lat = sqlite3_column_double(stmt, 4);
double distance = haversine(lat, lng, loc_lat, loc_lng);
if (distance <= radius_km) {
printf("%-5d %-20s %-30s %.2fkm\n", id, name, address, distance);
count++;
}
}
printf("--------------------------------------------------------\n");
printf("Found %d locations\n", count);
sqlite3_finalize(stmt);
return count;
}
int main() {
sqlite3 *db;
sqlite3_open("/sqlite/fgdata/fgedudb.db", &db);
// 搜索天安门附近5公里
search_nearby(db, 39.903738, 116.397128, 5.0);
sqlite3_close(db);
return 0;
}
Part05-风哥经验总结与分享
5.1 sqlite数据库R树最佳实践
1. 数据建模
├── 主表存储属性
├── R树存储坐标
├── 使用触发器同步
└── 定期重建索引
2. 查询优化
├── 先用R树过滤
├── 再计算精确距离
├── 限制返回数量
└── 使用边界框
3. 性能考虑
├── 批量导入后重建
├── 监控索引大小
├── 定期VACUUM
└── 合理设置维度
4. 应用建议
├── 位置服务
├── 地图应用
├── 空间分析
└── 游戏开发
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
