MySQL でも楽に bulk insert したい
以前 PostgreSQL で unnest を使って楽に bulk insert する方法を紹介した https://eagletmt.hateblo.jp/entry/2021/09/01/030453 。 これの MySQL 版が欲しかったものの array 型が存在しない MySQL では無理かなと思っていたんだけど、MySQL 8.0 からサポートされた JSON 型なら array を表現できて json_table() という関数を使うと達成できそうなことにふと気付いた。
json_table() を使うと JSON の値からテーブル (行) に変換することができる。つまりこれを使えば unnest に近いことが可能になる。
https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html
MySQL [(none)]> select * from json_table('[{"x": 1, "y": 2}, {"x": 3, "y": 4}]', '$[*]' columns (x integer path '$.x', y integer path '$.y')) as t; +------+------+ | x | y | +------+------+ | 1 | 2 | | 3 | 4 | +------+------+ 2 rows in set (0.001 sec)
文法がなかなか独特だけど1つの JSON の値 (配列) から行に変換できている。なのでプログラム側は JSON にシリアライズさえできれば insert into select で bulk insert が可能になる。 PostgreSQL 版と同様に Rust の sqlx を使った例はこんなかんじになる。
#[derive(Debug, serde::Serialize)] struct Record { x: i32, y: i32, } #[tokio::main] async fn main() -> anyhow::Result<()> { let records: Vec<_> = (0..1000) .map(|i| Record { x: 2 * i, y: 2 * i + 1, }) .collect(); let pool = sqlx::MySqlPool::connect("mysql://...").await?; let query = "insert into bulk_inserts (x, y) select * from json_table(?, '$[*]' columns (x integer path '$.x', y integer path '$.y')) as t"; sqlx::query(query) .bind(sqlx::types::Json(records)) .execute(&pool) .await?; Ok(()) }
がんばってプレースホルダを組み立てる必要がなくなって便利。