はじめに
RustでMySQLを使う場合、事前に構造体を定義する方法が主流ですが、
直接SQLを書きたいケースも多いのではないでしょうか。
今回はDieselを使って直SQLでデータを抽出する方法を紹介します。
用意したデータ
ソースはこちらから
テーブル
type
type_id | type_name |
---|---|
1 | ノーマル |
2 | ほのお |
3 | みず |
4 | でんき |
5 | くさ |
6 | こおり |
7 | かくとう |
8 | どく |
9 | じめん |
10 | ひこう |
11 | エスパー |
12 | むし |
13 | いわ |
14 | ゴースト |
15 | ドラゴン |
16 | あく |
17 | はがね |
18 | フェアリー |
MySQLに接続
Dieselというライブラリを使用します。
https://github.com/diesel-rs/diesel
[dependencies]
diesel = { version = "*", features = ["mysql"] }
DBに接続するための関数を作ります。
use diesel::mysql::MysqlConnection;
use diesel::prelude::*;
use dotenv::dotenv;
pub fn establish_connection() -> MysqlConnection {
let database_url = "mysql://[user]:[password]@host[:port][/database]";
MysqlConnection::establish(&database_url)
.expect(&format!("Error connecting to {}", database_url))
}
※mysql://[user]:[password]@host[:port][/database]には実際の接続情報を入力してください。
シンプルなSQLを書いてみる
use crate::utils::establish_connection;
use diesel::deserialize::QueryableByName;
use diesel::mysql::MysqlConnection;
use diesel::prelude::*;
use diesel::sql_query;
mod utils;
type DB = diesel::mysql::Mysql;
#[derive(Debug)]
pub struct Monster {
monster_id: i32,
name: String,
type1_id: i32,
type2_id: Option<i32>,
}
impl QueryableByName<DB> for Monster {
fn build<R: diesel::row::NamedRow<diesel::mysql::Mysql>>(
row: &R,
) -> diesel::deserialize::Result<Self> {
Ok(Monster {
monster_id: row.get("monster_id")?,
name: row.get("name")?,
type1_id: row.get("type1_id")?,
type2_id: row.get("type2_id")?,
})
}
}
fn simple_sql() {
let connection: MysqlConnection = establish_connection();
let monsters: Vec<Monster> = sql_query(
"
SELECT
monster_id,
name,
type1_id,
type2_id
FROM
monster
",
)
.load(&connection)
.unwrap();
println!("{:?}", monsters)
}
直接SQLを使うには、diesel::sql_query
を使います。
diesel::deserialize::QueryableByName
を使ってデータを受け取るための型を定義します。
PreparedStatementっぽいこと
use diesel::sql_types::Text;
use diesel::sql_types::Integer;
fn prepared_statement_sql() {
let connection: MysqlConnection = establish_connection();
let monsters: Vec<Monster> = sql_query(
"
SELECT
monster_id,
name,
type1_id,
type2_id
FROM
monster
WHERE
monster_id = ?
OR name = ?
",
)
.bind::<Integer, _>(4)
.bind::<Text, _>("ヒトカゲ")
.load(&connection)
.unwrap();
println!("{:?}", monsters[0])
}
SQLの中に?
を書いて、
.bind::<Integer, _>(4)
や.bind::<Text, _>("ヒトカゲ")
と書くことで安全にSQLの中に数値や文字列を挿入することができます。
JOIN
#[derive(Debug)]
pub struct MonsterFull {
monster_id: i32,
name: String,
type1_id: i32,
type2_id: Option<i32>,
type1_name: String,
type2_name: Option<String>,
}
impl QueryableByName<DB> for MonsterFull {
fn build<R: diesel::row::NamedRow<diesel::mysql::Mysql>>(
row: &R,
) -> diesel::deserialize::Result<Self> {
Ok(MonsterFull {
monster_id: row.get("monster_id")?,
name: row.get("name")?,
type1_id: row.get("type1_id")?,
type2_id: row.get("type2_id")?,
type1_name: row.get("type1_name")?,
type2_name: row.get("type2_name")?,
})
}
}
fn complex_sql() {
let connection: MysqlConnection = establish_connection();
let monsters: Vec<MonsterFull> = sql_query(
"
SELECT
m.monster_id,
m.name,
m.type1_id,
m.type2_id,
t1.type_name AS type1_name,
t2.type_name AS type2_name
FROM
monster m
LEFT JOIN
type t1
ON
m.type1_id = t1.type_id
LEFT JOIN
type t2
ON
m.type2_id = t2.type_id
",
)
.load(&connection)
.unwrap();
println!("{:?}", monsters);
}
JOINなどの複雑な操作も受け取るための型さえ用意すれば簡単にできます。
おわりに
この方法を使えば、Rustから直SQLを書いてデータを抽出することができました。
直接SQLを書きたい方は是非参考にしてもらえればと思います。