こちらは MySQL Advent Calendar 2022 22 日目の記事です。
昨日は @hmatsu47 さんの RDS for MySQL(8.0.30 以降)で Optimized Writes を試してみた でした
MySQLでupdate文を実行すると以下のようにChangedで更新の有無を教えてくれます
mysql> update category set name = 'Action' where category_id = 1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
mysql> update category set name = 'Haction' where category_id = 1;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
CSVから更新クエリ生成して実行したときに、実際に更新された行を検知できたら嬉しかったのでその時の対応を記載します
想定しているケース
- CSVやらなんやらで渡されたデータをもとに更新クエリを作成し実行する
- 何らかの言語・ライブラリを使ってやるほどではない(差分見てupdated_at更新してくれるの便利)
- 一行ずつの更新クエリが大量にある(複数行更新ではない)
実装
もとの更新クエリ(update.sql)がこんなシンプルな作りだったとして
update category set name = 'Haction' where category_id = 1;
update category set name = 'Animation' where category_id = 2;
以下のように修正する
update category set name = 'Haction' where category_id = 1;
select 1 where row_count() > 0;
update category set name = 'Animation' where category_id = 2;
select 2 where row_count() > 0;
そして実行 ( -N
はヘッダーを出力しないオプション )
bash-4.4# mysql sakila -N < update_with_return.sql > updated_ids
bash-4.4# cat updated_ids
1
別実装
変数を使ったパターン
- 変更行のid取得クエリが変わるのが気になる
- 条件(クエリ生成元)にidがない
set @changed = null; -- ここは一回の宣言で使いまわしておk
update category set name = 'Haction' where name = 'Action' and (select @changed := category_id);
select @changed where row_count() > 0;
update category set name = 'Animation' where name = 'Animation' and (select @changed := category_id);
select @changed where row_count() > 0;
bash-4.4# mysql sakila -N < update_with_variable.sql > updated_ids
bash-4.4# cat updated_ids
1
条件に引っ掛かる行が複数あるときは update文に limit 1
を付けて、リダイレクトを追記モード( >>
)で実行、実行後にupdated_idsファイルが増えなくなるまでループするとか
シンプルだけどスマートじゃないのでもっと適切な方法あったら教えてください
明日は @yk0807 さんの MySQLが遅いと思ったときにやること です