You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust使用rusqlite时如何拆分match内字符串并遍历查询关联数据

实现方案

先修正现有代码的明显错误

  • 建表SQL语法错误:restaurant_table 建表语句末尾缺少右括号
  • 接口返回类型错误:你要返回所有餐桌列表,原函数返回单个Table不符合需求,需改为Vec<Table>
  • 拼写错误:luanch_server应为launch_server,time_tp_prepare应为time_to_prepare,preperation_time应为preparation_time(可选修正,不影响核心逻辑)

核心逻辑实现思路

用两次数据库查询完成需求,避免循环查询数据库导致性能问题,全程用Rust迭代器的函数式写法处理数据:

  1. 第一次查询拿到所有restaurant_table的记录,收集所有餐桌数据,同时提取所有需要查询的商品ID
  2. 第二次批量查询所有需要的商品,存入HashMap方便快速匹配
  3. 遍历每个餐桌,拆分逗号分隔的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 08:57:04