域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。
  • 方案1️⃣AXUM.RS 域名 = 3000
  • 方案2️⃣方案1️⃣ + 本站所有专题原始 Markdown 文档 = 5000
  • 方案3️⃣方案2️⃣ + 本站原始数据库 = 5500
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名(方案1️⃣),或者该域名到期,本站将启用新的免费域名继续提供服务。
  2. 如果有人购买了 AXUM.RS 域名,且同时购买了内容和/或数据库(方案2️⃣/方案3️⃣),本站将关闭。届时我们或许会以另一种方式与你再相遇。

使用sqlx的QueryBuilder构建复杂、动态的查询

本章我们讨论如何优雅方便地使用 sqlx 构建复杂的、动态的 SQL。sqlx 提供了 QueryBuilder 结构体,它可以方便地实现 SQL 的构建。

QueryBuilder 常用方法

// query_as
sqlx::query_as("SELECT * FROM member WHERE id=? AND is_del=?").bind(id).bind(false);

// QueryBuild
let mut q = QueryBuilder::new("SELECT * FROM member WHERE id="); // 不用占位符!
q.push_bind(id)
.push(" AND is_del=") // 不用占位符!
.push_bind(false);

使用 QueryBuilder 改造 list()

改造前:

pub async fn list(
    conn: &sqlx::MySqlPool,
    page: u32,
) -> Result<Paginate<Vec<model::member::Member>>> {
    let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM member")
        .fetch_one(conn)
        .await
        .map_err(Error::from)?;

    let sql = format!(
        "SELECT * FROM member ORDER BY id DESC LIMIT {} OFFSET {}",
        DEFAULT_PAGE_SIZE,
        page * DEFAULT_PAGE_SIZE
    );
    let data = sqlx::query_as(&sql)
        .fetch_all(conn)
        .await
        .map_err(Error::from)?;

    Ok(Paginate::new(count.0 as u32, page, data))
}

改造后:

我们使用 QueryBuilder 代替了之前使用 format! 拼接 SQL 的做法。

改造前:

pub async fn exists(conn: &sqlx::MySqlPool, name: &str, id: Option<u32>) -> Result<bool> {
    let sql = "SELECT COUNT(*) FROM member WHERE name=?";
    let with_id_sql: String;

    let q = match id {
        Some(id) => {
            with_id_sql = format!("{} AND id<>?", sql);
            sqlx::query_as(&with_id_sql).bind(&name).bind(id)
        }
        None => sqlx::query_as(sql).bind(&name),
    };

    let count: (i64,) = q.fetch_one(conn).await.map_err(Error::from)?;

    Ok(count.0 > 0)
}

改造后:

pub async fn exists(conn: &sqlx::MySqlPool, name: &str, id: Option<u32>) -> Result<bool> {
    let mut q = sqlx::QueryBuilder::new("SELECT COUNT(*) FROM member WHERE name=");
    q.push_bind(name);

    if let Some(id) = id {
        q.push(" AND id<>").push_bind(id);
    };

    let count: (i64,) = q
        .build_query_as()
        .fetch_one(conn)
        .await
        .map_err(Error::from)?;

    Ok(count.0 > 0)
}

再次提醒:**和 query()/query_as()不同,QueryBuilderpush()方法的 SQL 语句里,不需要使用 ? 占位符

再次提醒:**和 query()/query_as()不同,QueryBuilderpush()方法的 SQL 语句里,不需要使用 ? 占位符

要查看完整内容,请先登录