MySQL TIPS 1 怪盗ロワイヤルのように3分に1ポイント回復

MySQLネタもそろそろ尽きてきたので、趣向を変えてMySQLの実践Tipを紹介していこうと思います。
まずは怪盗ロワイヤルのように3分に1ポイント回復する実装について以下のように3分に一度バッチを回して、全ユーザの体力を+1ずつアップデートするのは効率が悪いので、リアルタイムに計算する方法を考えます。

UPDATE `charactor` SET hp=IF((hp+1)>100,100,hp+1)

まずはテーブルやデータを準備

CREATE TABLE  `charactor` (
`charactor_id` INT             NOT NULL AUTO_INCREMENT PRIMARY KEY
                                       COMMENT  'キャラクターID',
`hp`           INT             NOT NULL COMMENT  'HP',
`max_hp`       INT             NOT NULL COMMENT  '最大HP',
`updated_time` TIMESTAMP( 11 ) NOT NULL COMMENT  '更新日時'
) ENGINE = MYISAM COMMENT =  'キャラクターテーブル';
INSERT INTO  `charactor` (`hp`,`max_hp`) 
VALUES ('56','124');

SQLでリアルタイムに演算

  • SELECT時
-- 現在ヒットポイントの演算
SELECT 
if(
 max_hp <
   floor(hp + (unix_timestamp(now()) - unix_timestamp(updated_time) )/(60 * 3)),
 max_hp,
 floor(hp + (unix_timestamp(now()) - unix_timestamp(updated_time) )/(60 * 3))
)
as hp,
max_hp  
 FROM charactor 
WHERE charactor_id = 1 
  • UPDATE時
-- 現在ヒットポイントから減算
UPDATE  
charactor 
SET 
hp = if(
 max_hp <
   floor(hp + (unix_timestamp(now()) - unix_timestamp(updated_time))/(60 * 3)),
 max_hp,
 floor(hp + (unix_timestamp(now()) - unix_timestamp(updated_time))/(60 * 3))
) - 5 
WHERE charactor_id = 1 

冗長なのでviewを作る(失敗編)

CREATE OR REPLACE ALGORITHM = MERGE VIEW  `v_charactor` AS 
SELECT 
charactor_id ,
IF( max_hp < floor( hp + (
UNIX_TIMESTAMP( NOW( ) ) - UNIX_TIMESTAMP( updated_time ) ) /(60 *3) ) , max_hp, floor( hp + (
UNIX_TIMESTAMP( NOW( ) ) - UNIX_TIMESTAMP( updated_time ) ) /(60 *3)
)
) AS hp, max_hp
FROM charactor 
UPDATE `v_charactor` SET hp=hp-5 WHERE charactor_id=1

#1348 - Column 'hp' is not updatable

....更新できませんね。

viewのマニュアルを見ると更新可能なviewの条件に入ってないのでそりゃ無理か。

ビューカラムは派生カラムではなく、単純なカラムリファレンスでなければなりません。派生カラムは単純なカラムリファレンスでなく、表現から派生したものです。

しょうがないので、トリガー*1で実現してみる。...続く

*1:トリガーだと重くなりそうなので、最終的にはphp等アプリケーションで実装することになりそうですが、一応SQLだけで実現を目指します。