Как выполнить удаление с подзапросом в Diesel к базе данных Postgres? - PullRequest
0 голосов
/ 24 марта 2020

У меня есть следующая схема в базе данных Postgres:

Table A {
  ID
  Name
}

Table B {
  ID FOREIGN KEY (A.ID)
}

Я пытаюсь написать следующий запрос в Diesel:

DELETE FROM B
WHERE B.ID in (SELECT ID from A WHERE A.Name = $VAR)

$VAR является переменная, переданная моим приложением.

Моя первая попытка написать это была следующая:

fn deleteB(conn: &PgConnection, n: &str) {
    use schema::A::dsl::*;
    use schema::A;
    use schema::B;

    let res = A::table
        .filter(Name.eq(n))
        .select(ID);
        .load(conn)
        .unwrap();
    assert!(res.len() < 2);
    let b_id: i32 = *res.iter().nth(1).unwrap_or(&0);

    let _rows = diesel::delete(
        B::table
        .filter(ID.eq(n_id))
    )
    .execute(conn)
    .unwrap();
}

Это компилируется, но не работает: оператор SELECT для идентификатора всегда возвращает 0. Он не соответствует ни одной из вставленных записей в A, хотя я проверял вручную, что они там есть. Я уверен, что у меня есть ошибка в процессе сопоставления (&str против &String может быть?), Но я решил попробовать другое решение, так как оно мне не понравилось, так как оно должно выполняться два отдельных утверждения против базы данных.

Моя вторая попытка выглядит следующим образом:

fn deleteB(conn: &PgConnection, n: &str) {
    use schema::A::dsl::*;
    use schema::A;
    use schema::B;

    let source = B::table.inner_join(A::table)
            .filter(Name.eq(n));
    let s = delete(source)
            .execute(conn)
            .unwrap();
}

Мне это кажется более дизельным-идиоматическим c (предупреждение о спойлере, я почти не знаю фреймворк) но, конечно же, он не компилируется:

    |
410 |     let s = delete(source)
    |                    ^^^^^^ the trait `diesel::query_builder::IntoUpdateTarget` is not implemented for `diesel::query_builder::SelectStatement<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<mobility2::schema::B::table...` (very long error)

Я изучал вышеупомянутую черту, когда понял, что это кажется тривиальной операцией (само удаление), и мне, вероятно, лучше обратиться за помощью ,

Как правильно написать идиоматический c дизельный код для выполнения оператора удаления, который я хочу выполнить?

1 Ответ

3 голосов
/ 25 марта 2020

Поскольку операторы Diesel отображаются буквально на SQL, написанный вами запрос приведет к:

DELETE FROM TABLE B INNER JOIN TABLE A ON … WHERE name = $1

Это недопустимо SQL, что приведет к ошибке времени компиляции.

Чтобы получить запрос, который вы хотите написать, вам нужно сделать что-то вроде:

#[macro_use]
extern crate diesel;

use diesel::delete;
use diesel::prelude::*;

mod schema {
    table! {
        A(ID) {
            ID -> Integer,
            name -> Text,
        }
    }

    table! {
        B(ID) {
            ID -> Integer,
        }
    }

    allow_tables_to_appear_in_same_query!(A, B);
}

fn deleteB(conn: &PgConnection, n: &str) {
    use schema::A;
    use schema::B;

    let s = delete(B::table)
        .filter(B::ID.eq_any(A::table.filter(A::name.eq(n)).select(A::ID)))
        .execute(conn)
        .unwrap();
}
...