I am using the Rust Diesel library and have a schema that looks like this:
The schema looks like this:
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, ...);
I am struggling to represent this SQL query with Rust Diesel:
DELETE FROM table1 t1
WHERE (
SELECT COUNT(*) FROM table3 t3 WHERE t3.table2_id = {id_value}
AND t1.id = t3.table1_id
) = 1;
The desired result of the query is to delete records from table1
when the count of related records in table3
that are also associated with a specific record from table2
is 1. The real tables 1, 2, and 3 are called budgets
, users
, and user_budgets
, respectively, if that helps with intuition for what I am doing. I want to be able to delete all budgets for a user that aren't "shared," meaning they are also associated with other users via the user_budgets
table.
I'm not sure how to approach doing this in Diesel. I have tried doing this:
diesel::delete(
table1.filter(
dsl::count(
table3_fields::table2_id
.eq({id_value})
.and(table1_fields::id.eq(table3_fields::table1_id)),
)
.eq(1),
),
)
That doesn't work; I get the following error (edited with some search/replace to match my hypothetical schema):
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>>>>`
Is there a way to do this without resorting sql_query
?
(FYI, I am using a Postgres backend)
This should accomplish that:
diesel::delete(table1::table).filter(
table3::table
.filter(table3::table2_id.eq(id_value))
.filter(table1::id.eq(table3::table1_id))
.count()
.single_value()
.eq(1),
);
The output from debug_query()
looks pretty much identical:
DELETE FROM "table1" WHERE ((SELECT COUNT(*) FROM "table3" WHERE (("table3"."table2_id" = $1) AND ("table1"."id" = "table3"."table1_id")) LIMIT $2) = $3) -- binds: [78e50fcc-6b13-4ee2-aece-4959f3a67f76, 1, 1]
Tripped me up for a while because I forgot that .single_value()
is needed to treat a SELECT
as an expression. After that it was fairly straightforward. Full code available here on the playground.
See also: