ラボ > PHP:各種エラー、FuelPHP:DB関連、MySQL

PHP・MySQLでトランザクション中にupdateしたレコードを再度、updateで未反映

処理の流れ上、updateしたあと、もう一回updateしたかった。

作成日:2018-04-23, 更新日:2018-04-26

結論(2018/04/24)

FuelPHPでしか確認していないので分からないが・・・トランザクション開始したらCommitするまで同一レコードの修正は・・・可能

経緯

▼下記のようなコトをしていた。
A1.レコード追加(id=連番、filePath=''、他のカラムたち)
A2.「1」の「lastInsertId()」でファイル名や保存PATHを決めたい
A3.「2」でファイル名や保存PATHを「1」の「カラム:filePath」に保存

「A1」はinsert or updateのどちらかが実行される
「A2」はファイルのアップロード
「A3」は「A1」のレコードをupdateする。
※「A1」の前でトランザクション開始、「A3」のあとにCommit。

▼問題の内容と経緯
B1.FuelPHPで「updateしたときの戻り値」は「更新したレコード数」なのに・・・「count()」していた。
B2.FuelPHPのバージョンアップに合わせて、PHPもバージョンアップ(PHP7.2にした)
B3.PHP7.2にバージョンアップしたら「配列じゃない値はcount()できない」ってトコでつまづいた。
B4.そのため「B1」の「updateしたときの戻り値」を「count()」しないように修正
B5.その結果「A3」の「updateしたときの戻り値」が「0(false)」になった
B6.「B5」の結果により例外処理の分岐に入って・・・ロールバックされた

原因は、「A3」で変更内容が無いのにupdateしたこと。
FuelPHPで「updateしたときの戻り値」は「更新したレコード数」ってのは「変更したレコード数」になるらしく「変更していないレコード数」は含まれない。

そのため「A3」で変更内容が無いときの「updateしたときの戻り値」は「0(false)」となる。

更新日時の指定の問題

問題をややこしくしていたのは更新日時の指定。

「A1」と「A3」のそれぞれで更新日時をいれていて、その指定が原因にたどり着く邪魔をしていた。
そういう指定をした私がダメなんだけどね・・・。

C1.「$now=time()」で現在の日時を取得
C2.「A1」で更新日を「C1」の「$now」を設定
C3.「A3」も更新日を「C1」の「$now」を設定
※2回updateを実行しようが、「(1回の処理だから更新日は)一緒でいいや」って感じで、このようにしていた。

「A1」で「update」が実行されたときは、更新日時だけは確実に変わるから「更新したレコード数」がある。
「A3」で「update」が実行されると更新日時も使い回しているから「更新したレコード数」が「0」のときがある。
※仮に都度「time()」を指定しても、ミリ秒じゃないと同一日時になってしまうことがある。

更新箇所を確実に作るためにもレコードのバージョンを設定して更新時に「+1」しておけばいいんだろうね。
※参照:MySQLの「+1」した値をupdateしたい

結論(2018/04/23)

以下は、以前に勘違いしていた内容
※FuelPHPが絡まないと勘違いじゃないかもしれない・・・。とりあえずメモとして残しておく。

トランザクション開始したらCommitするまで同一レコードの修正は・・・ムリ

InsertやUpdateされるとCommitされるまでロックがかかるっぽい。

正確には出来るみたいだけど・・・出来てたんだけど出来ないことが分かった。

経緯

1.レコードの追加(or 修正)したい
2.「1」で保存した値を元にファイル保存したい
3.「2」で保存したファイルPATHを「1」のレコードで修正したい

ってなコトをしたかった。具体的にいうと・・・
1.レコード追加(id=連番、filePath=''、他のカラムたち)
2.「1」の「lastInsertId()」でファイル名や保存PATHを決めたい
3.「2」でファイル名や保存PATHを「1」の「カラム:filePath」に保存
・・・こんな感じ。

流れ的に
1.ファイルをup
2.レコードの追加(or 修正)
が望ましい。

でも、「2.レコードの追加(or 修正)」で失敗したときに「1.ファイルをup」のファイルを削除しなくちゃいけない。
DBだったらトランザクションで戻ってくれるし・・・ってことで下記のような処理にしていた。

1.レコードの追加(or 修正)したい
2.ファイルをupしたい
3.upしたPATHを追加(or 修正)したレコードに対して更新したい
※「1」が成功して「3」が「失敗」ってのはレアケースだから、都度対応にするって感じ。

今まで「1」「3」の処理は下記のようにしていた

$result = DBに保存(※FuelPHPが用意してくれている関数を実行);
if ( !count($result) ) {
  ~ エラー処理 ~
}

※かなり昔に書いた内容を使い回していたので・・・なぜ「count()」にしたのかは記憶にない。
※さらにFuelPHPが用意してくれていたupdate用の関数は成功したレコード数を返してくれる。

このソースで今までエラー(警告)が出なかったけど・・・PHPのバージョンを7.2にしたら警告が出てきた。
→「$result」が配列じゃないから「count()」は使えないっていう内容。

ということで「1」「3」を下記のように修正

$result = DBに保存(※FuelPHPが用意してくれている関数を実行);
if ( !$result ) {
  ~ エラー処理 ~
}

するとエラーに・・・。調べると「1:$result=1」「3:$result=0」となっている。
で、やっと「トランザクション中に同一レコードの修正って出来ない感じ?」と思い、調べてみて気付いた。

でも・・・ロックかかっているなら「count($result)」のときにcommitで更新されているのは何故なんだろうと思ったら「count(0)」は「1」になるっぽい。

対応案

下記の流れで「1」or「2」で一回コミットすればいいんだけど・・・気持ち悪い。

1.レコードの追加(or 修正)
2.「1」で保存した値を元にファイル保存
3.「2」で保存したファイルPATHを「1」のレコードに追加