🔨
SQLxのクエリビルダーでよく使うパターン
RustのライブラリであるSQLxのクエリビルダーは文字列を結合していくだけの低レベルな機能しか提供しません。
毎回組み立てるのは大変なので、いくつかのよく使うパターンをここにメモすることにします。データベースはMariaDB(MySQL)を使いましたがPostgreSQLやSQLiteもたぶん同じです。
VALUES
句でまとめてINSERT
したいとき
いわゆるバルクインサートです。
出力
INSERT INTO users(id, name) VALUES (?, ?), (?, ?)
コード
use sqlx::QueryBuilder;
use sqlx::MySql;
fn main() {
let users = vec![
(1, "一郎"),
(2, "太郎"),
];
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new("INSERT INTO users(id, name) ");
query_builder
.push_values(users, |mut separated, user| {
separated
.push_bind(user.0)
.push_bind(user.1);
});
println!("{}", query_builder.sql());
let _query = query_builder.build();
}
作ったクエリはいつものように.execute()
とかできます。
IN
句で複数の値に一致するレコードを検索する
クエリビルダーを使っていますが、format!
マクロを使ってベクターの数だけ?
を展開する方法もあるようです。
出力
SELECT * FROM users WHERE name IN (?, ?)
コード
use sqlx::QueryBuilder;
use sqlx::MySql;
fn main() {
let names = vec!["一郎", "太郎"];
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new("SELECT * FROM users WHERE name IN (");
let mut separated = query_builder.separated(", ");
for value_type in names.iter() {
separated.push_bind(*value_type);
}
query_builder.push(")");
println!("{}", query_builder.sql());
let _query = query_builder.build();
}
作ったクエリはいつものように.fetch_optional()
とか.fetch_all()
とかできます。
WHERE
句の条件をON/OFFする
パラメータによって検索機能を設けるときに便利です。フロントエンドからJSONで検索条件を投げられた状況を想定しています。
出力
SELECT * FROM users WHERE id = ? AND name = ?
コード
use sqlx::QueryBuilder;
use sqlx::MySql;
fn main() {
let params = serde_json::json!({ "id": 1, "name": "一郎" });
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new("SELECT * FROM users WHERE ");
let mut separated = query_builder.separated(" AND ");
if let Some(id) = params.get("id") {
separated.push("id = ").push_bind_unseparated(id.as_number().unwrap().as_i64());
}
if let Some(name) = params.get("name") {
separated.push("name = ").push_bind_unseparated(name.as_str());
}
println!("{}", query_builder.sql());
let _query = query_builder.build();
}
作ったクエリはいつものように.fetch_optional()
とか.fetch_all()
とかできます。
UPDATE
する項目をON/OFFする
パラメータによってHTTPメソッドのPATCHエンドポイントを作るときを想定しています。
出力
UPDATE users SET id = ?, name = ? WHERE id = ?
コード
use sqlx::QueryBuilder;
use sqlx::MySql;
fn main() {
let params = serde_json::json!({ "id": 5, "name": "一郎" });
let map = params.as_object().unwrap();
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new("UPDATE users SET ");
let mut separated = query_builder.separated(", ");
for (key, value) in map.iter() {
separated
.push(key)
.push_unseparated(" = ")
.push_bind_unseparated(value);
}
query_builder.push(" WHERE id = ").push_bind(1);
println!("{}", query_builder.sql()); // -> UPDATE users SET id = ?, name = ? WHERE id = ?
let _query = query_builder.build();
}
作ったクエリはいつものように.execute()
とかできます。
Discussion