2017年2月25日土曜日

Codeigniterのupdate_batchで複数条件指定する時の罠。(whereメソッドに注意編)

オフィス狛 技術部です。

前回に引き続き、Codeigniterの「update_batch」について説明していきます。

前回は、下記の様な例で「update_batch」の説明をしました。
$data = array(
   array(
      'emp_id' => 'A001' ,
      'name' => 'koma taro' ,
      'tel' => '090-0000-1111'
   ),
   array(
      'emp_id' => 'A002' ,
      'name' => 'koma saya' ,
      'tel' => '090-0000-2222'
   )
);
$this->db->update_batch('emptable', $data, 'emp_id');

「emptable」(社員テーブル)が更新対象のテーブル名、
「data」が更新&条件用の配列、
「emp_id」(社員ID)が更新時に条件として使用するカラムの指定、となります。

さて、ここで例えば、社員テーブルには、年度ごとにデータが格納されているとします。
(「year」(年度)というカラムがある)
つまり、2016年度と2017年度だけを考えても社員ID「A001」は2件存在する事になります。
でも、更新対象としたいのは2017年度のデータだけだとしたら・・・???

「$this->db->update_batch('emptable', $data, 'emp_id');」の第三引数は、
1つしか指定できません。

さて、困った、という事で、調べてみると、下記の情報が見つかりました。
stackoverflow - Codeigniter update_batch() with included update of the where key
上記のベストアンサーは、「Codeigniterのupdate_batchを拡張しないと無理だよ」と言っています。
さすがにそれは影響範囲大きいなぁ、と思っていたところ、ベストアンサー以外の回答で、
$this->db->where('option1', $option1);
$this->db->update_batch('table_name', $data, 'option2');
とあり、「これだったらお手軽だ」と思って、試してみました。
【注意】結論から言うと、上記では正常に動作しないので、決して真似しないで下さい。

$data = array(
   array(
      'emp_id' => 'A001' ,
      'name' => 'koma taro' ,
      'tel' => '090-0000-1111'
   ),
   array(
      'emp_id' => 'A002' ,
      'name' => 'koma saya' ,
      'tel' => '090-0000-2222'
   )
);
$this->db->where('year', '2017');
$this->db->update_batch('emptable', $data, 'emp_id');
すると、生成されたSQLは、
UPDATE `emptable` SET `name` = CASE
WHEN `emp_id` = 'A001' THEN 'koma taro'
WHEN `emp_id` = 'A002' THEN 'koma saya'
ELSE `name` END,
`tel` = CASE
WHEN `emp_id` = 'A001' THEN '090-0000-1111'
WHEN `emp_id` = 'A002' THEN '090-0000-2222'
ELSE `tel` END
WHERE `year` = '2017'
AND `emp_id` IN ('A001','A002')
となり、年度の条件も付いているので、万々歳、と言う事で、
この方式を採用する事にしました。

やっぱりダメだった


ところが、しばらくして、この処理を実行すると、
2017年度のデータと一緒に、2016年度のデータも更新されている事が判明しました。
しかも、更新されない事もある、との事。

何度か試してみると、確かに生成SQLに「`year` = '2017'」が含まれていない事がありました。

ここで改めてリファレンスと読んでみると、
..note:: $batch_size より多くの行数が渡された場合、 複数のクエリが実行され、それぞれ $batch_size のフィールド/値ペア の分だけ操作を行う。

Codeigniter『データベースリファレンス - クエリビルダクラス - update_batch 』より。
とあります。
・・・・「複数のクエリが実行される」???

先の例だと、例えば1000人の社員を一気に更新する際は、
単純に1000 ÷ batch_size(デフォルトは100)の 10 回クエリが実行されるようです。

しかも、
「$this->db->whereで指定した条件は、最初の1回目のクエリにしか反映されない」
という事のようです。

つまり、最初の1回目のクエリのみ2017年度の社員が更新されて、
2回目以降は2016年度と、2017年度の社員が両方更新されてしまっていたようです。

・・・・いや、どうせだったら、最初の1回目も条件反映しないでよ。

という事で、今回は「update_batch」を使うのをやめて、
「update」複数回実行する、という実装に変更しました。

・・・まあ、テーブル設計が杜撰なのは認めます。
きっと「update_batch」は、そういうテーブル設計を想定していないのだと思います。

「update_batch」を使う際は、ご注意下さい。

【追記】

タイトル詐欺になってしまうので、補足しておくと、
基本は、「update_batch」で複数条件指定は出来ないと思います。
やるならば、先に記載した通り、
Codeigniterのupdate_batch(system/database/drivers/DB_query_builder.php)を
拡張する事になります。


, ,

0 件のコメント:

コメントを投稿