如何用rusqlite简化SELECT语句并实现SQLite列模式表格展示?
问题描述
我正在开发一款使用rusqlite及多条SQLite语句的应用,现需简化SELECT查询语句并优化输出格式。当前使用的Rust代码如下:
let mut stmt = conn.prepare("SELECT pid, cat, plant, variety FROM plants")?; println!("Columns = {:?}", stmt.column_names()); println!("column 0 = {:?}", stmt.column_names()[0]); // ************************MAP the rows using Plant struct let plant_iter = stmt.query_map([], |row| { Ok(Plant { pid: row.get(0)?, cat: row.get(1)?, plant: row.get(2)?, variety: row.get(3)?, }) })?; println!(); // ********************ITERATE over the mapped rows using struct for plant in plant_iter { print!( "{}{} ", plant.as_ref().unwrap().pid, " ".repeat(4 - plant.as_ref().unwrap().pid.to_string().len()) ); print!( "{}{} ", plant.as_ref().unwrap().cat, " ".repeat(8 - plant.as_ref().unwrap().cat.to_string().len()) ); print!( "{}{} ", plant.as_ref().unwrap().plant, " ".repeat(10 - plant.as_ref().unwrap().plant.to_string().len()) ); print!( "{}{} ", plant.as_ref().unwrap().variety, " ".repeat(20 - plant.as_ref().unwrap().variety.to_string().len()) ); }
当前输出结果:
Columns = ["pid", "cat", "plant", "variety"] column 0 = "pid" 1 veggie tomato Brandywine 2 veggie bok choy prize 3 veggie cabbage brunswick
希望实现类似SQLite中.mode column的格式,即一次性输出带表头且列间距美观的表格,请问如何通过rusqlite将列表头与行数据结合,模拟SQLite的表格视图?
解决方案
可以通过计算自适应列宽+统一格式化输出的方式实现,具体步骤如下:
- 先获取查询的列名,再收集所有行数据,计算每一列的最大宽度(表头长度与所有行对应字段长度的最大值),确保列间距能自适应内容。
- 按照计算好的列宽,先输出格式化后的表头,再输出分隔线(可选),最后逐行格式化输出数据。
完整实现代码:
use rusqlite::{Connection, Result}; #[derive(Debug)] struct Plant { pid: i32, cat: String, plant: String, variety: String, } fn main() -> Result<()> { let conn = Connection::open("plants.db")?; // 准备查询并获取列名 let mut stmt = conn.prepare("SELECT pid, cat, plant, variety FROM plants")?; let column_names = stmt.column_names().to_vec(); // 收集所有行数据,用于计算列宽 let mut plants = Vec::new(); let plant_iter = stmt.query_map([], |row| { Ok(Plant { pid: row.get(0)?, cat: row.get(1)?, plant: row.get(2)?, variety: row.get(3)?, }) })?; for plant in plant_iter { plants.push(plant?); } // 计算每一列的最大宽度:表头长度 vs 所有行对应字段的长度 let mut column_widths: Vec<usize> = column_names .iter() .map(|name| name.len()) .collect(); for plant in &plants { column_widths[0] = column_widths[0].max(plant.pid.to_string().len()); column_widths[1] = column_widths[1].max(plant.cat.len()); column_widths[2] = column_widths[2].max(plant.plant.len()); column_widths[3] = column_widths[3].max(plant.variety.len()); } // 输出格式化表头 for (i, name) in column_names.iter().enumerate() { print!("{name:<width$} ", width = column_widths[i] + 2); // 额外添加2个空格作为列间距 } println!(); // 输出分隔线(可选,模拟SQLite的视觉效果) for &width in &column_widths { print!("{} ", "-".repeat(width)); } println!(); // 逐行输出格式化数据 for plant in plants { print!("{:<width$} ", plant.pid, width = column_widths[0] + 2); print!("{:<width$} ", plant.cat, width = column_widths[1] + 2); print!("{:<width$} ", plant.plant, width = column_widths[2] + 2); println!("{:<width$} ", plant.variety, width = column_widths[3] + 2); } Ok(()) }
代码说明
- 收集数据计算列宽:先把所有行数据存入Vec,避免重复遍历查询结果,确保能准确计算每一列的最大宽度,让列间距自适应内容长度。
- 格式化输出:使用Rust格式化语法
{:<width$}实现左对齐并填充空格,额外添加2个空格作为列间距,和SQLite.mode column的视觉效果一致。 - 分隔线:可选的分隔线能让表头和数据区分更清晰,进一步贴近SQLite的输出样式。
最终输出效果
pid cat plant variety --- ------ -------- ---------- 1 veggie tomato Brandywine 2 veggie bok choy prize 3 veggie cabbage brunswick
内容的提问来源于stack exchange,提问作者Kenneth Wagner
相关产品推荐
相关产品推荐

