在移动应用、桌面工具或者嵌入式设备中做天气预报功能时,网络不稳定或者用户想离线查看历史天气,都需要把数据落到本地。SQLite凭借零配置、单文件、跨平台的特性,成为这类场景首选的本地存储方案。它不像MySQL需要部署服务进程,也不像纯文件存储难以检索,用一套SQL就能完成写入、更新与复杂查询。

一、为什么选择SQLite做天气本地存储
天气预报数据具有明显的结构化和时效性特征。每条记录通常包含城市、日期、温度、湿度、天气状况等字段,且按天或按小时粒度生成。如果直接用文本文件或SharedPreferences保存,当需要查询“某城市最近七天温度走势”时,就不得不把全部数据读进内存再遍历过滤,既慢又费电。
SQLite以表形式组织数据,可以为常用查询字段建立索引。它还是事务型数据库,保证写入中途断电不会破坏整个库文件。对于客户端来说,引入官方提供的sqlite3库几乎不增加多少包体积,却换来了可靠的增删改查能力,这是很多轻量项目愿意采用它的核心原因。
二、数据表结构设计
设计天气表时,首先要确定唯一标识一条记录的维度。通常城市编码(如ADCODE)加上观测日期(或小时时间戳)就能唯一定位。将它们设为联合主键,既能防止重复插入,也能让按城市+时间范围的查询走主键索引,速度极快。
下面给出一份实用的建表语句,包含常用气象字段,并用WAL模式提升并发读写表现。注意日期字段用TEXT存储ISO格式,便于跨语言解析。
CREATE TABLE IF NOT EXISTS weather_record (
city_code TEXT NOT NULL,
obs_date TEXT NOT NULL,
temp_min REAL,
temp_max REAL,
humidity INTEGER,
condition TEXT,
update_time TEXT,
PRIMARY KEY (city_code, obs_date)
);
-- 开启WAL,写不阻塞读
PRAGMA journal_mode=WAL;
-- 普通同步级别,兼顾安全与性能
PRAGMA synchronous=NORMAL;
三、写入与更新天气数据
天气接口每次刷新返回最新预报,本地要用“存在即更新,不存在则插入”的逻辑。SQLite的INSERT OR REPLACE配合联合主键正好满足该需求,避免先查后写带来的竞态问题。
以下Python示例演示从接口拿到数据后落库的过程。使用参数化语句防止注入,也避免字符串拼接出错。
import sqlite3
def save_weather(db_path, city_code, obs_date, temp_min, temp_max, humidity, condition):
conn = sqlite3.connect(db_path)
try:
cur = conn.cursor()
cur.execute(
"INSERT OR REPLACE INTO weather_record "
"(city_code, obs_date, temp_min, temp_max, humidity, condition, update_time) "
"VALUES (?, ?, ?, ?, ?, ?, datetime('now'))",
(city_code, obs_date, temp_min, temp_max, humidity, condition)
)
conn.commit()
finally:
conn.close()
这种写法在每小时拉取一次预报时非常高效。如果某天数据已经存在,新请求只会覆盖旧值,不会产生冗余行。同时datetime('now')自动记录本地更新时间,方便后续判断数据新鲜度。
四、高效查询与过期清理
用户打开天气页,常常需要某城市最近N天的记录。利用联合主键的左前缀原则,仅用city_code就能快速定位该城市所有数据,再按obs_date排序限制条数即可。
SELECT obs_date, temp_min, temp_max, condition FROM weather_record WHERE city_code = '110000' ORDER BY obs_date DESC LIMIT 7;
本地存储不能无限增长,否则文件变大拖慢查询。可以每天定时删除三十天前的记录,保持库体轻量。下面的语句清理过期数据,执行一次即可批量删除。
DELETE FROM weather_record
WHERE obs_date < date('now', '-30 days');
在代码层可以用定时任务或应用启动时触发该清理。配合前面建立的索引,删除操作同样能快速定位目标行,不会全表扫描。
五、常见误区与注意事项
有的开发者把所有城市天气塞进一张没有主键的表,靠程序判断重复,结果多次刷新后表里出现几千条相同日期的数据,查询越来越卡。明确联合主键是从源头规避该问题的关键。
另一个误区是频繁打开关闭连接。在移动端,每次写库都connect再close会带来不小开销。建议对单进程维持一个长连接,或用连接池管理。此外,WAL模式虽好,但记得在备份或迁移数据库时连同-wal和-shm文件一起拷贝,否则可能丢近期写入。
六、小结
用SQLite承载天气预报本地存储,核心在于合理的表结构、主键去重、索引加速以及定期清理。它用极小代价让应用具备离线能力,在弱网环境下依旧能提供连贯的天气体验。只要避开重复插入和连接滥用的坑,这套方案能稳定支撑长期运行的客户端产品。