Rustのdieselでbatch upsertをやる方法について、検索してもなかなか苦労したのですごく簡単にメモしておきます。バックエンドはPostgresを想定しています。MySQLでもこれができるかはわかりません。
結論
diesel::insert_into(...).values(...).on_conflict(...).do_update().set(...)
でいける。
excluded句を入れたい場合
たとえばcolumn_a
というカラムをexcluded
したいとします。最後のset
の呼び出しのところで、.set(column_a.eq(excluded(column_a)))
とすれば実装できます。複数カラムの場合、set
にタプルを投げ込むと指定できます。たとえばcolumn_a
、column_b
、column_c
に対してexcluded
したい場合、
diesel::insert_into(...) .values(...) .on_conflict(...) .do_update() .set((column_a.eq(excluded(column_a)), column_b.eq(excluded(column_b), column_c.eq(excluded(column_c))))
のようにです。
複数カラム指定を楽にする
ただカラムの数が増えてくるとだんだん間違えます。そこで、このタプルの生成はマクロに任せてしまうといいと思います。次のようなマクロを書きます。
macro_rules! excluded { ( $( $column:expr ),* ) => {{ use diesel::{upsert::excluded, ExpressionMethods}; ($( ($column.eq(excluded($column))) ),*) }} }
これを先ほどのset
に投げ込みます。
diesel::insert_into(...) .values(...) .on_conflict(...) .do_update() .set(excluded! { column_a, column_b, column_c })
これで、あとはコードが展開されて勝手にexcluded
を含むタプルが生成されます。結構便利だと思うので、困ったなと思ったらぜひ使ってみてください。