如何用Rust Diesel实现带关联记录计数条件的DELETE操作?
问题描述
我正在使用Rust的Diesel库,数据库架构如下:
table! { table1 (id) { id -> Uuid, ... } } table! { table2 (id) { id -> Uuid, ... } } table! { table3 (id) { id -> Uuid, table1_id -> Uuid, table2_id -> Uuid, ... } } ... allow_tables_to_appear_in_same_query!(table1, table2, table3, ...);
我需要实现如下SQL查询,但用Diesel编写时遇到了困难:
DELETE FROM table1 t1 WHERE ( SELECT COUNT(*) FROM table3 t3 WHERE t3.table2_id = {id_value} AND t1.id = t3.table1_id ) = 1;
实际业务场景中,对应表名为budgets、users和user_budgets,需求是删除某用户的所有非共享预算——即未通过user_budgets关联其他用户的预算。
我尝试了以下代码:
diesel::delete( table1.filter( dsl::count( table3_fields::table2_id .eq({id_value}) .and(table1_fields::id.eq(table3_fields::table1_id)), ) .eq(1), ), )
但出现了如下错误:
error[E0277]: the trait bound `diesel::expression::is_aggregate::Yes: MixedAggregates<diesel::expression::is_aggregate::No>` is not satisfied --> path/to/file.rs:345:21 | 345 | table1.filter( | ^^^^^^ the trait `MixedAggregates<diesel::expression::is_aggregate::No>` is not implemented for `diesel::expression::is_aggregate::Yes` | = help: the following other types implement trait `MixedAggregates<Other>`: <diesel::expression::is_aggregate::Yes as MixedAggregates<diesel::expression::is_aggregate::Never>> <diesel::expression::is_aggregate::Yes as MixedAggregates<diesel::expression::is_aggregate::Yes>> = note: required for `diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<diesel::expression::count::count::count<diesel::sql_types::Bool, diesel::expression::grouped::Grouped<diesel::expression::operators::And<diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<table3::columns::table2_id, diesel::expression::bound::Bound<diesel::sql_types::Uuid, uuid::Uuid>>>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<table1::columns::id, table3::columns::table1_id>>>>>, diesel::expression::bound::Bound<BigInt, i64>>>` to implement `NonAggregate` = note: required for `SelectStatement<FromClause<table1::table>>` to implement `FilterDsl<diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<diesel::expression::count::count::count<diesel::sql_types::Bool, diesel::expression::grouped::Grouped<diesel::expression::operators::And<diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<table3::columns::table2_id, diesel::expression::bound::Bound<diesel::sql_types::Uuid, uuid::Uuid>>>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<table1::columns::id, table3::columns::table1_id>>>>>, diesel::expression::bound::Bound<BigInt, i64>>>>`
请问是否可以不使用sql_query来实现该功能?(注:我使用的是Postgres后端)
解决方案
完全可以不用sql_query实现,问题出在你直接在filter中使用聚合函数count,未正确构造子查询。Diesel的类型系统会阻止聚合与非聚合表达式混合的写法,必须将聚合逻辑放在子查询或关联聚合中。
写法一:分组子查询匹配
先统计符合条件的table1_id,再匹配删除:
use diesel::dsl::count; // 构造子查询:筛选出符合条件且关联数为1的table1_id let target_ids = table3 .filter(table3::table2_id.eq(id_value)) .group_by(table3::table1_id) .having(count(table3::id).eq(1)) .select(table3::table1_id); // 删除匹配的table1记录 diesel::delete(table1) .filter(table1::id.eq_any(target_ids)) .execute(conn)?;
写法二:关联子查询(与原始SQL逻辑完全一致)
利用Diesel对关联聚合的支持,直接生成和你目标SQL一致的逻辑:
use diesel::dsl::count; diesel::delete(table1) .filter( count(table3::id) .filter(table3::table2_id.eq(id_value)) .filter(table3::table1_id.eq(table1::id)) .eq(1) ) .execute(conn)?;
错误原因说明
你之前的写法错误地将count直接作用在布尔表达式上,没有将聚合逻辑包裹在子查询或关联聚合中。SQL规范中,WHERE子句里的聚合函数必须放在子查询内(除非配合GROUP BY,但本场景不适用),Diesel的类型系统会强制执行这一规范,因此抛出了类型不匹配的错误。
内容的提问来源于stack exchange,提问作者tedtanner
相关产品推荐
相关产品推荐

