Rust使用rusqlite时如何拆分match内字符串并遍历查询关联数据
实现方案
先修正现有代码的明显错误
- 建表SQL语法错误:
restaurant_table建表语句末尾缺少右括号 - 接口返回类型错误:你要返回所有餐桌列表,原函数返回单个
Table不符合需求,需改为Vec<Table> - 拼写错误:
luanch_server应为launch_server,time_tp_prepare应为time_to_prepare,preperation_time应为preparation_time(可选修正,不影响核心逻辑)
核心逻辑实现思路
用两次数据库查询完成需求,避免循环查询数据库导致性能问题,全程用Rust迭代器的函数式写法处理数据:
- 第一次查询拿到所有
restaurant_table的记录,收集所有餐桌数据,同时提取所有需要查询的商品ID - 第二次批量查询所有需要的商品,存入
HashMap方便快速匹配 - 遍历每个餐桌,拆分逗号分隔的ID字符串,从
HashMap中匹配对应的商品,组装成最终的Table结构体返回
完整代码修改
1. 调整database.rs
新增批量查询商品的工具函数,修正建表SQL:
use rusqlite::{Result, params}; use std::collections::HashSet; pub struct ItemData { pub id: i32, pub time_to_prepare: i32, pub name: String } pub struct TableData { pub id: i32, pub item_ids: String // 驼峰命名调整为Rust风格的蛇形命名 } pub fn setup_db() -> Result<String, String>{ let db_connection = match rusqlite::Connection::open("data.sqlite") { Ok(connection) => connection, Err(_) => { return Err("无法连接数据库".into()); } }; match db_connection .execute( "create table if not exists item ( id integer primary key, name varchar(64) not null, preparation_time integer not null ); create table if not exists restaurant_table ( id integer primary key, items varchar(64) not null );", // 补全右括号 [] ) { Ok(_) => Ok("数据库表创建成功".into()), Err(_) => return Err("建表SQL执行失败".into()) } } // 新增:根据ID列表批量查询商品 pub fn get_items_by_ids(conn: &rusqlite::Connection, ids: &[i32]) -> Result<Vec<ItemData>, String> { if ids.is_empty() { return Ok(vec![]); } // 生成IN查询的占位符,比如有3个ID就生成?,?,? let placeholders = ids.iter().map(|_| "?").collect::<Vec<_>>().join(","); let sql = format!("select id, name, preparation_time from item where id in ({})", placeholders); let mut stmt = match conn.prepare(&sql) { Ok(s) => s, Err(_) => return Err("商品查询语句预处理失败".into()) }; // 把ID列表转成参数 let params = ids.iter().map(|id| id as &dyn rusqlite::ToSql).collect::<Vec<_>>(); let results = stmt.query_map(params.as_slice(), |row| { Ok(ItemData { id: row.get(0)?, name: row.get(1)?, time_to_prepare: row.get(2)? }) }); match results { Ok(rows) => rows.collect::<rusqlite::Result<Vec<_>>>().map_err(|_| "商品数据收集失败".into()), Err(_) => Err("商品查询失败".into()) } }
2. 调整data.rs的字段匹配
use serde::{Deserialize, Serialize}; #[derive(Serialize, Clone)] pub struct Item { pub id: i32, pub time_to_prepare: i32, pub name: String } #[derive(Serialize)] pub struct Table { pub id: i32, pub items: Vec<Item> } #[derive(Serialize)] pub struct StatusMessage { pub message: String }
3. 重写main.rs的get_all_tables核心逻辑
#![feature(proc_macro_hygiene, decl_macro)] #[macro_use] extern crate rocket; use rocket_contrib::json::Json; use rusqlite::Result; use std::collections::HashMap; mod database; mod data; // 修正返回类型为餐桌列表 #[get("/api/get-all-tables-v1")] fn get_all_tables() -> Result<Json<Vec<data::Table>>, String> { let db_connection = match rusqlite::Connection::open("data.sqlite") { Ok(connection) => connection, Err(_) => { return Err("无法连接数据库".into()); } }; // 第一步:查询所有餐桌数据 let mut statement = match db_connection.prepare("select id, items from restaurant_table;") { Ok(statement) => statement, Err(_) => return Err("餐桌查询语句预处理失败".into()) }; let results = statement.query_map([], |row| { Ok(database::TableData { id: row.get(0)?, item_ids: row.get(1)? }) }); let tables = match results { Ok(rows) => rows.collect::<rusqlite::Result<Vec<_>>>().map_err(|_| "餐桌数据收集失败".into())?, Err(_) => return Err("餐桌查询失败".into()) }; // 第二步:收集所有需要查询的商品ID,用HashSet去重 let all_item_ids: HashSet<i32> = tables.iter() .flat_map(|t| t.item_ids.split(',')) // 拆分每个餐桌的ID字符串 .filter_map(|id_str| id_str.parse::<i32>().ok()) // 转成i32,过滤无效ID .collect(); let all_item_ids: Vec<i32> = all_item_ids.into_iter().collect(); // 第三步:批量查询所有商品,存入HashMap方便匹配 let items = database::get_items_by_ids(&db_connection, &all_item_ids)?; let item_map: HashMap<i32, data::Item> = items.into_iter() .map(|item| (item.id, data::Item { id: item.id, name: item.name, time_to_prepare: item.time_to_prepare })) .collect(); // 第四步:组装最终的Table列表 let result: Vec<data::Table> = tables.into_iter() .map(|table| { let table_items = table.item_ids.split(',') .filter_map(|id_str| id_str.parse::<i32>().ok()) .filter_map(|id| item_map.get(&id).cloned()) // 从HashMap取对应的商品 .collect(); data::Table { id: table.id, items: table_items } }) .collect(); Ok(Json(result)) } fn main() { match database::setup_db() { Ok(_) => launch_server(), Err(error) => eprintln!("程序启动失败,错误:{}", error) } } fn launch_server() { rocket::ignite().mount("/", routes![get_all_tables]).launch(); }
函数式写法说明
全程用Rust迭代器的map、flat_map、filter_map、collect等方法处理数据,没有手动写循环,符合函数式编程风格,同时所有错误都做了统一处理,无效ID、查不到的商品都会被自动过滤,不会导致接口报错。
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

