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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 06:35:51