App Inventor 2 SQLite数据库教程 - 本地数据存储与查询

« 返回首页

概述

App Inventor内置的TinyDB是简单的键值存储,不支持复杂查询。SQLite扩展提供完整的SQL数据库能力。

已有文档:extensions/SQLite.md 介绍了SQLite扩展。db.md 介绍了TinyDB和TinyWebDB。本文聚焦SQLite实战用法。

TinyDB vs SQLite

特性 TinyDB SQLite
存储方式 键值对 关系型表
查询 只能按Key SQL查询
数据量 小 大
排序/过滤 手动实现 SQL语句
多表 ❌ ✅
聚合统计 手动 SQL聚合函数
适合场景 简单配置 结构化数据

SQLite扩展

导入

  1. 下载SQLite扩展.aix文件
  2. Designer → Extension → Import Extension

核心方法

方法 说明
OpenDatabase(dbName) 打开/创建数据库
ExecuteSQL(sql) 执行SQL语句
QuerySQL(sql) 查询并返回结果
CloseDatabase() 关闭数据库

事件

事件 参数 说明
OnResult(result) 查询结果 查询返回
OnError(error) 错误信息 执行出错

基本操作

创建数据库和表

when Screen1.Initialize() {
  SQLite1.OpenDatabase("myapp.db")
  // 创建用户表
  var 建表SQL = join("CREATE TABLE IF NOT EXISTS users (", "id INTEGER PRIMARY KEY AUTOINCREMENT, ", "name TEXT NOT NULL, ", "phone TEXT, ", "email TEXT, ", "created_at DATETIME DEFAULT CURRENT_TIMESTAMP)")
  SQLite1.Execute(建表SQL)
}

插入数据

when 按钮_添加用户.Click() {
  var SQL = join("INSERT INTO users (name, phone, email) VALUES ('", 文本输入框_姓名.Text, "', '", 文本输入框_电话.Text, "', '", 文本输入框_邮箱.Text, "')")
  SQLite1.Execute(SQL)
  标签_状态.Text = "✅ 用户已添加"
}

参数化查询(防SQL注入)

when 按钮_安全添加.Click() {
  var SQL = "INSERT INTO users (name, phone, email) VALUES (?, ?, ?)"
  SQLite1.执行参数化SQL(SQL, [文本输入框_姓名.Text, 文本输入框_电话.Text, 文本输入框_邮箱.Text])
}

查询数据

when 按钮_查询全部.Click {
  SQLite1.QuerySQL("SELECT * FROM users ORDER BY created_at DESC")
}

when SQLite1.OnResult(结果) {
  // 结果是列表,每项是一个字典
  var 显示 = ""
  var i = 1
  while i <= 列表长度(结果) {
    var 记录 = 列表第i项(结果)
    var name = dictLookup(记录, "name", "")
    var phone = dictLookup(记录, "phone", "")
    var email = dictLookup(记录, "email", "")
    var 显示 = 显示 + name + " | " + phone + " | " + email + "\n"
    var i = i + 1
  }
  标签_用户列表.Text = 显示
}

条件查询

when 按钮_搜索.Click {
  var SQL = "SELECT * FROM users WHERE name LIKE '%" + 输入框_搜索.Text + "%'"
  SQLite1.QuerySQL(SQL)
}

更新数据

when 按钮_更新.Click {
  var SQL = "UPDATE users SET phone = '" + 输入框_新电话.Text + "' WHERE name = '" + 文本输入框_姓名.Text + "'"
  SQLite1.Execute(SQL)
}

删除数据

when 按钮_删除.Click() {
  var SQL = join("DELETE FROM users WHERE id = ", 选中ID)
  SQLite1.Execute(SQL)
}

实战案例:学生成绩管理

创建表结构

when Screen1.Initialize() {
  SQLite1.OpenDatabase("school.db")
  SQLite1.Execute(join("CREATE TABLE IF NOT EXISTS students (", "id INTEGER PRIMARY KEY AUTOINCREMENT, ", "name TEXT NOT NULL, ", "class TEXT, ", "student_no TEXT UNIQUE)"))
  SQLite1.Execute(join("CREATE TABLE IF NOT EXISTS scores (", "id INTEGER PRIMARY KEY AUTOINCREMENT, ", "student_id INTEGER, ", "subject TEXT, ", "score REAL, ", "exam_date TEXT, ", "FOREIGN KEY(student_id) REFERENCES students(id))"))
}

录入成绩

when 按钮_录入.Click {
  // 先查找学生ID
  var 查询SQL = "SELECT id FROM students WHERE name = '" + 输入框_学生.Text + "'"
  SQLite1.QuerySQL(查询SQL)
}

when SQLite1.OnResult(结果) {
  if 列表长度(结果) > 0 {
    var item = 列表第1项(结果)
    var 学生ID = dictLookup(item, "id", "")
    var d = 时钟1.FormatDateTime(时钟1.Now(), "yyyy-MM-dd")
    var 录入SQL = "INSERT INTO scores (student_id, subject, score, exam_date) VALUES (" + 学生ID + ", '" + 下拉框_科目.Selection + "', " + 输入框_分数.Text + ", '" + d + "')"
    SQLite1.Execute(录入SQL)
    标签_状态.Text = "✅ 成绩已录入"
  }
}

统计查询

// 查询某学生所有成绩
SELECT s.name, sc.subject, sc.score, sc.exam_date
FROM scores sc JOIN students s ON sc.student_id = s.id
WHERE s.name = '?'
ORDER BY sc.exam_date DESC

// 查询某科目平均分
SELECT subject, AVG(score) as avg_score, MAX(score) as max_score,
MIN(score) as min_score, COUNT(*) as count
FROM scores WHERE subject = '?'
GROUP BY subject

// 班级成绩排名
SELECT s.name, s.class, SUM(sc.score) as total
FROM scores sc JOIN students s ON sc.student_id = s.id
GROUP BY sc.student_id ORDER BY total DESC

用ListView展示查询结果

when SQLite1.OnResult(结果) {
  var 显示列表 = []
  var i = 1
  while i <= 列表长度(结果) {
    var 记录 = 列表第i项(结果)
    var name = dictLookup(记录, "name", "")
    var subject = dictLookup(记录, "subject", "")
    var score = dictLookup(记录, "score", "")
    var 显示文本 = name + " | " + subject + " | " + score + "分"
    var 显示列表 = append(显示列表, 显示文本)
    var i = i + 1
  }
  ListView1.Elements = 显示列表
}

与已有文档的关系

技术 已有文档 SQLite场景用途
TinyDB db.md 简单配置仍用TinyDB
Web API api_communication.md 数据同步到云端
ListView listview_custom.md 展示查询结果
CSV导出 file_operations.md 导出SQLite数据
JSON解析 json_parse.md 解析API返回数据

常见问题

Q1: SQLite数据库文件在哪里?

数据库文件存储在应用的私有目录:/data/data/包名/databases/。需要root权限才能直接访问。

Q2: 如何导出SQLite数据?

查询全部数据 → 转CSV → 保存到/sdcard/:

when 按钮_导出.Click {
  SQLite1.QuerySQL("SELECT * FROM users")
}
  
when SQLite1.OnResult(结果) {
  var CSV = "id,name,phone,email\n"
  var i = 1
  while i <= 列表长度(结果) {
    var r = 列表第i项(结果)
    var id = dictLookup(r, "id", "")
    var name = dictLookup(r, "name", "")
    var phone = dictLookup(r, "phone", "")
    var email = dictLookup(r, "email", "")
    var CSV = CSV + id + "," + name + "," + phone + "," + email + "\n"
    var i = i + 1
  }
  文件管理器1.SaveFile(CSV, "/sdcard/MyApp/export.csv")
}

Q3: 数据库占用空间大?

-- 清理碎片
VACUUM;

-- 删除旧数据
DELETE FROM scores WHERE exam_date < '2025-01-01';

总结

操作 SQL
建表 CREATE TABLE
插入 INSERT INTO
查询 SELECT
条件 WHERE
排序 ORDER BY
聚合 AVG/SUM/COUNT/MAX/MIN
关联 JOIN
分组 GROUP BY
文档反馈