ActiveRecordのincrementはポンコツか?

SQLではカウンタのincrement/decrementは1つの文で済ますことになっている。

UPDATE table SET counter = counter + 1 WHERE ...

ActiveRecordのモデルにはincrementやdecrementの定義がある。そうか便利だなぁ…??

1.9.3-p545 :021 > Tbl.take.increment!(:counter, 2)
D, [2014-09-04T00:36:32.902849 #73566] DEBUG -- :   Tbl Load (0.1ms)  SELECT "tbls".* FROM "tbls" LIMIT 1
D, [2014-09-04T00:36:32.903201 #73566] DEBUG -- :    (0.0ms)  begin transaction
D, [2014-09-04T00:36:32.904809 #73566] DEBUG -- :   SQL (0.4ms)  UPDATE "tbls" SET "counter" = ?, "updated_at" = ? WHERE "tbls"."id" = 1  [["counter", 10], ["updated_at", 2014-09-03 15:36:32 UTC]]
D, [2014-09-04T00:36:32.914149 #73566] DEBUG -- :    (9.1ms)  commit transaction
 => true

SELECTで読んで、Rubyが足し算して、UPDATEで足した結果を書き出す…こんなのってないぞ。それをORマッパーがincrementと名乗るか普通?

正解は、モデルのクラスメソッドのupdate_countersを使う。これは複数のカウンタの操作が可能。第1引数のオブジェクトIDも複数書けるし、第2引数以降のカラム名も複数書ける。オブジェクトから呼べないのが使いにくいのを除けば、普通のSQLが発行される。まあ↓の例ではIDを求めるのにSELECTは発行されるが、UPDATE自体は普通にDB側で加算されてくれる。

1.9.3-p545 :022 > Tbl.update_counters(Tbl.take.id, :counter=>1)
D, [2014-09-04T00:37:01.497430 #73566] DEBUG -- :   Tbl Load (0.2ms)  SELECT "tbls".* FROM "tbls" LIMIT 1
D, [2014-09-04T00:37:01.507024 #73566] DEBUG -- :   SQL (9.0ms)  UPDATE "tbls" SET "counter" = COALESCE("counter", 0) + 1 WHERE "tbls"."id" = 1
 => 1
1.9.3-p545 :023 > Tbl.update_counters(Tbl.take.id, :counter=>1, :counter2=>2)
D, [2014-09-04T00:37:06.088688 #73566] DEBUG -- :   Tbl Load (0.2ms)  SELECT "tbls".* FROM "tbls" LIMIT 1
D, [2014-09-04T00:37:06.098522 #73566] DEBUG -- :   SQL (9.4ms)  UPDATE "tbls" SET "counter" = COALESCE("counter", 0) + 1, "counter2" = COALESCE("counter2", 0) + 2 WHERE "tbls"."id" = 1
 => 1

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です