高级查询
聚合查询
let count: usize = db.select::<User>().count(|u| u.id).await?;
let total: Option<i32> = db.select::<Product>().sum(|p| p.price).await?;
let avg: Option<f64> = db.select::<User>().avg(|u| u.age).await?;
let max: Option<i32> = db.select::<User>().max(|u| u.age).await?;
let min: Option<i32> = db.select::<User>().min(|u| u.age).await?;
带条件:
let adult_count: usize = db
.select::<User>()
.filter(|u| u.age.ge(18))
.count(|u| u.id)
.await?;
GROUP BY 和 HAVING 查询
基础分组
use ormer::Select;
let sql = Select::<User>::new()
.select_column(|u| u.id.count())
.group_by(|u| u.age)
.to_sql();
构建器的无参
to_sql()仅用于预览:使用默认方言、不做能力校验,输出随启用的 feature 变化。需要与实际执行一致的精确 SQL 时,请用执行器的to_sql()(真实方言并校验),例如db.select::<User>().to_sql()?。
多字段选择 + 分组
let sql = Select::<User>::new()
.select_column(|u| (u.department, u.id.count()))
.group_by(|u| u.department)
.to_sql();
HAVING 条件过滤
let sql = Select::<User>::new()
.select_column(|u| (u.department, u.id.count()))
.group_by(|u| u.department)
.having(|u| u.id.count().gt(5))
.to_sql();
多字段分组
let sql = Select::<User>::new()
.select_column(|u| (u.department, u.age, u.id.count(), u.score.avg()))
.group_by(|u| (u.department, u.age))
.to_sql();
完整查询:WHERE + GROUP BY + HAVING + ORDER BY + LIMIT
let sql = Select::<User>::new()
.filter(|u| u.age.ge(18))
.select_column(|u| (u.department, u.id.count(), u.score.avg()))
.group_by(|u| u.department)
.having(|u| u.id.count().gt(0))
.order_by(|u| u.department)
.range(0..10)
.to_sql();
支持的聚合函数
count()- 计数,返回usizesum()- 求和,返回原类型(数值类型)avg()- 平均值,返回f64max()- 最大值,返回原类型(数值类型)min()- 最小值,返回原类型(数值类型)
统一表达式
字段、投影、过滤、排序、分组和部分聚合能力共用表达式 AST,可组合函数、CASE、cast、collate、窗口函数和后端操作符:
let rows: Vec<(i32, String, String)> = db
.select::<User>()
.filter(|u| u.email.to_lower().eq("alice@example.com"))
.map_to(|u| {
(
u.id.alias("user_id"),
u.email,
ormer::expr!(match u.status {
"paid" => "done",
"new" => "open",
_ => "other",
})
.alias("status_label"),
)
})
.order_by(|u| u.email.collate("nocase").asc())
.collect()
.await?;
聚合表达式支持 FILTER、内部排序和 OVER;不支持的数据库会把 FILTER 渲染为 CASE WHEN:
let rows: Vec<(i32, i32)> = db
.select::<Order>()
.map_to(|o| {
(
o.user_id,
o.total
.sum()
.filter(|o| o.paid.eq(true))
.over(|w| w.partition_by(o.user_id)),
)
})
.collect()
.await?;
分组支持 ROLLUP、CUBE 和 GROUPING SETS;PostgreSQL / MSSQL / DuckDB 使用原生语法,MySQL 仅支持 WITH ROLLUP,SQLite 和 ClickHouse 会拒绝高级分组:
let sql = Select::<Sale>::new()
.select_column(|s| (s.region, s.amount.sum()))
.rollup(|s| (s.region, s.city))
.to_sql();
let sql = Select::<Sale>::new()
.select_column(|s| (s.region, s.amount.sum()))
.grouping_sets(|s| ((s.region,), ()))
.to_sql();
也可组合 row value、JSON 路径、全文检索、DISTINCT ON 和行锁:
let rows: Vec<User> = db
.select::<User>()
.distinct_on(|u| u.org_id)
.filter(|u| (u.org_id, u.email).eq((org_id, email)))
.filter(|u| u.profile.json_text("role").eq("admin"))
.filter(|u| u.bio.matches_text("rust"))
.for_update()
.skip_locked()
.collect()
.await?;
DISTINCT ON 在 PostgreSQL / DuckDB 使用原生语法,其他数据库改写为窗口函数;ORDER BY 必须以分区 key 开头。
多字段全文检索可用 DSL:fields 指定检索列、query 提供检索词、mode 选择匹配模式(FullTextMode::Natural 默认 / Boolean / WebSearch),可选 language 设置语言、rank(FullTextRank::Relevance) 按相关度排序:
let rows: Vec<Article> = db
.select::<Article>()
.fields(|a| (a.title, a.body))
.query("rust ormer")
.mode(ormer::FullTextMode::Boolean)
.rank(ormer::FullTextRank::Relevance)
.collect()
.await?;
serde_json::Value 字段可声明静态 JSON path 和数组 path:
#[derive(Debug, Model)]
#[table = "users"]
struct User {
#[primary]
id: i64,
#[field(settings.active: bool)]
#[field(settings.retry_count: i64)]
#[field(tags[]: String)]
profile: serde_json::Value,
}
let users: Vec<User> = db
.select::<User>()
.filter(|u| u.profile.settings.active.eq(true))
.filter(|u| u.profile.settings.retry_count.ge(3))
.filter(|u| u.profile.tags.contains_all(["admin", "write"]))
.collect()
.await?;
db.update::<User>()
.filter(|u| u.id.eq(1))
.set(|u| u.profile.settings.retry_count.set(3))
.execute()
.await?;
JOIN 查询
#[derive(Debug, Model)]
#[table = "users"]
struct User {
#[primary(auto)]
id: i32,
name: String,
}
#[derive(Debug, Model)]
#[table = "roles"]
struct Role {
#[primary]
id: i32,
user_id: i32,
role_name: String,
}
LEFT JOIN
let user_roles: Vec<(User, Option<Role>)> = db
.select::<User>()
.left_join::<Role>(|u, r| u.id.eq(r.user_id))
.collect()
.await?;
INNER JOIN
let user_roles: Vec<(User, Role)> = db
.select::<User>()
.inner_join::<Role>(|u, r| u.id.eq(r.user_id))
.collect()
.await?;
RIGHT JOIN
let user_roles: Vec<(Option<User>, Role)> = db
.select::<User>()
.right_join::<Role>(|u, r| u.id.eq(r.user_id))
.collect()
.await?;
JOIN 带过滤
let admin_users: Vec<(User, Role)> = db
.select::<User>()
.inner_join::<Role>(|u, r| u.id.eq(r.user_id))
.filter(|u| u.name.eq("Alice".to_string()))
.collect()
.await?;
JOIN 右表排序与分页 (LATERAL JOIN)
当 JOIN 条件中使用了 order_by / order_by_desc 或 range 时,框架会自动生成 LATERAL JOIN SQL,实现对右表的排序和分页。
// 右表按 role_name 降序,只取第1条
let user_roles: Vec<(User, Option<Role>)> = db
.select::<User>()
.left_join::<Role>(|u, r| u.id.eq(r.user_id).order_by_desc(r.role_name).range(..1))
.collect()
.await?;
// 仅排序
let user_roles: Vec<(User, Option<Role>)> = db
.select::<User>()
.left_join::<Role>(|u, r| u.id.eq(r.user_id).order_by_desc(r.role_name))
.collect()
.await?;
// 仅分页
let user_roles: Vec<(User, Option<Role>)> = db
.select::<User>()
.left_join::<Role>(|u, r| u.id.eq(r.user_id).range(..3))
.collect()
.await?;
支持的 JOIN 类型:left_join、inner_join、right_join。
可与主查询的 filter、order_by / order_by_desc、limit / range、for_update 等方法组合使用:
let page: Vec<(User, Option<Role>)> = db
.select::<User>()
.left_join::<Role>(|u, r| u.id.eq(r.user_id))
.order_by(|u| u.name)
.limit(10)
.collect()
.await?;
派生表 JOIN
Select 与 ProjectionSelect(select_column / map_to 的返回类型)可通过 as_model::<R>() 提升为派生表。无主键的投影结果类型使用 ViewModel。
#[derive(Debug, ormer::ViewModel)]
struct UserTotal {
user_id: i32,
total: i64,
}
let totals = db
.select::<Order>()
.select_column(|o| (o.user_id, o.amount.sum()))
.group_by(|o| o.user_id)
.as_model::<UserTotal>();
let rows: Vec<(User, Option<UserTotal>)> = db
.select::<User>()
.left_join_derived(totals, |u, t| u.id.eq(t.user_id))
.collect()
.await?;
也可以把派生表作为主查询来源:
let hot_users: Vec<UserTotal> = db
.from_derived(totals)
.filter(|t| t.total.gt(1000_i64))
.order_by_desc(|t| t.total)
.range(..10)
.collect()
.await?;
多表关联
两表 (from)
let users: Vec<User> = db
.select::<User>()
.from::<Role>()
.filter(|u, r| u.id.eq(r.user_id))
.filter(|_, r| r.role_name.eq("admin".to_string()))
.collect()
.await?;
三表 (from3)
let users: Vec<User> = db
.select::<User>()
.from3::<Role, Permission>()
.filter(|u, r, p| u.id.eq(r.user_id).and(r.id.eq(p.role_id)))
.collect()
.await?;
四表 (from4)
let users: Vec<User> = db
.select::<User>()
.from4::<Role, Permission, Department>()
.filter(|u, r, p, d| {
u.id.eq(r.user_id)
.and(r.id.eq(p.role_id))
.and(u.department_id.eq(d.id))
})
.collect()
.await?;
子查询
IN 子查询
let subquery = db.select::<Role>().map_to(|r| r.user_id);
let users: Vec<User> = db
.select::<User>()
.filter(|u| u.id.is_in(subquery))
.collect()
.await?;
EXISTS / NOT EXISTS
使用 Select::exists() 和 Select::not_exists() 构建子查询表达式:
let users_with_roles: Vec<User> = db
.select::<User>()
.filter(|_u| {
Select::<Role>::new()
.filter(|r| r.name.eq("admin"))
.exists() // 或 .not_exists()
})
.collect()
.await?;
可与外层条件组合:
.filter(|p| p.age.ge(18).or(
Select::<Role>::new().filter(|r| r.uid.eq(p.id)).exists()
))
模型关系加载
模型使用 #[has_many]、#[belongs_to]、#[has_one] 或 #[through] 声明关系后,可以按需加载关联对象:
let user = db.find_by_id::<User>(1).await?.unwrap();
let posts = db
.find_related(&user, UserWhere::default().posts)
.await?;
使用 preload 为一批父模型一次性加载关系,避免逐条查询:
let mut users = db.select::<User>().collect::<Vec<_>>().await?;
db.preload(&mut users, UserWhere::default().posts).await?;
include 用于在查询结果中加载关系,也支持关系内排序、分页和嵌套加载:
let posts: Vec<Post> = db
.select::<Post>()
.include(|post| post.user)
.collect()
.await?;
let users: Vec<User> = db
.select::<User>()
.filter(|user| user.roles.any(|role| role.name.eq("admin")))
.include(|user| user.roles.order_by(|role| role.name.asc()).range(..20))
.include(|user| user.user_roles.include(|user_role| user_role.role))
.collect()
.await?;
关系字段不会参与表列映射;集合关系结果为空时返回空 Vec,单对象关系未找到目标时为 None。
批量查询编排
batch 顺序执行多个已构造查询,并按 tuple 结构返回结果;batch_many 顺序执行查询集合并返回 Vec。
let (users, orders): (Vec<User>, Vec<Order>) = db
.batch((
db.select::<User>().include(|u| u.roles),
db.select::<Order>().include(|o| o.items),
))
.await?;
let orders: Vec<Vec<Order>> = db.batch_many(order_queries).await?;
不会合并 SQL,也不会自动开启事务;需要事务时,在 transaction 闭包里使用 tx.batch(...)。
集合操作
UNION / UNION ALL
// UNION
let sql = Select::<User>::new()
.filter(|u| u.age.gt(30))
.union(Select::<User>::new().filter(|u| u.age.lt(18)))
.to_sql();
// UNION ALL
let sql = Select::<User>::new()
.filter(|u| u.age.gt(30))
.union_all(Select::<User>::new().filter(|u| u.age.lt(18)))
.to_sql();
INTERSECT / EXCEPT
let sql = Select::<User>::new()
.filter(|u| u.age.gt(18))
.intersect(Select::<User>::new().filter(|u| u.age.lt(65)))
.to_sql();
let sql = Select::<User>::new()
.filter(|u| u.age.gt(18))
.except(Select::<User>::new().filter(|u| u.name.eq("admin")))
.to_sql();
操作数支持各自的 order_by 和 range;渲染时两个操作数都会括号包装((SELECT ...) UNION (SELECT ...)),操作数内的 ORDER BY/LIMIT 保留在括号内、对其自身生效:
let sql = Select::<User>::new()
.filter(|u| u.age.gt(30))
.order_by(|u| u.name)
.range(..10)
.union(
Select::<User>::new()
.filter(|u| u.age.lt(18))
.order_by_desc(|u| u.age)
.range(..5),
)
.to_sql();
集合查询通过 select_union 执行:
let union = Select::<User>::new()
.filter(|u| u.age.gt(30))
.union(Select::<User>::new().filter(|u| u.age.lt(18)));
let users: Vec<User> = db.select_union(union).collect().await?;
关联查询的 count()
from / from3 / from4 关联查询提供 count(),生成与原子查询同谓词的 SELECT COUNT(*) FROM (<原子查询>),不受 range() / order_by() 影响, 适合列表分页场景计算 total_count:
let total = db
.select::<User>()
.from::<Role>()
.filter(|u, r| u.id.eq(r.uid))
.filter(|_, r| r.name.eq("admin".to_string()))
.count()
.await?; // i64
同一谓词与 range() 组合即可实现"当页数据 + 总条数",两者谓词一致、不会漂移。
完整示例
use ormer::{Database, DbType, Model};
#[derive(Debug, Model)]
#[table = "users"]
struct User {
#[primary(auto)]
id: i32,
name: String,
age: i32,
}
#[derive(Debug, Model)]
#[table = "roles"]
struct Role {
#[primary(auto)]
id: i32,
user_id: i32,
role_name: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = Database::connect(DbType::Sqlite, "file:test.db").await?;
db.create_table::<User>().execute().await?;
db.create_table::<Role>().execute().await?;
db.insert(&vec![
User { id: 1, name: "Alice".to_string(), age: 25 },
User { id: 2, name: "Bob".to_string(), age: 30 },
]).await?;
db.insert(&vec![
Role { id: 1, user_id: 1, role_name: "admin".to_string() },
Role { id: 2, user_id: 2, role_name: "user".to_string() },
]).await?;
// 聚合查询
let count: usize = db.select::<User>().count(|u| u.id).await?;
let avg_age: Option<f64> = db.select::<User>().avg(|u| u.age).await?;
// LEFT JOIN
let user_roles: Vec<(User, Option<Role>)> = db
.select::<User>()
.left_join::<Role>(|u, r| u.id.eq(r.user_id))
.collect()
.await?;
// 多表关联
let admin_users: Vec<User> = db
.select::<User>()
.from::<Role>()
.filter(|u, r| u.id.eq(r.user_id))
.filter(|_, r| r.role_name.eq("admin".to_string()))
.collect()
.await?;
// 子查询
let users_with_roles: Vec<User> = db
.select::<User>()
.filter(|u| u.id.is_in(
db.select::<Role>().map_to(|r| r.user_id)
))
.collect()
.await?;
db.drop_table::<Role>().execute().await?;
db.drop_table::<User>().execute().await?;
Ok(())
}