LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログ

DBのビューを使ってみたらRailsのコード修正が(ほぼ)要らなくなった話

Androidエンジニアの高橋です。

突然ですが、皆さんデータベースのビューって使ったことありますか? CREATE VIEW で作るアレです。

自分は過去にビューを使うプロジェクトでお仕事した経験はありますが、そんなに理解も深くなく、なんとなく「プログラム書きたくないSQLおじさんが使うもの」くらいに思ってました。(すごい偏見)

そんなビューへの偏見を払拭するような活用例を最近見つけたのでご紹介したいと思います。

ビュー自体については説明を割愛させて頂きますmm

課題

以下のようなproducts というテーブルがあったとします。

products
 id |    name    | type | price
----+------------+------+--------
  1 | エアコン   | A    | 100000
  2 | 空気洗浄機 | A    |  50000
  3 | 椅子       | B    |   8000
  4 | テーブル   | B    |  12000
  5 | 炊飯器     | C    |  60000

このテーブルをpriceの安い順に並び替えして表示したい場合、Railsで実装すると、

Product.order("price")

となるのはすぐわかるかと思います。

では、急遽「大型家電半額キャンペーン」が始まったことにより、type=A のレコードのみprice半額表示で並び替えをしたいと言う要件が出てきたとします。

RailsのActiveRecordを使って頑張るのもいいかもしれませんが、このような条件が複雑なソート処理をする場合にはActiveRecordだとクエリを表現しづらいです。

そこでビューを使ってみます。

ビューの作成

以下のようなビュー定義でtype=Aのみ半額表示になるビューを作成します。

  CREATE VIEW discounted_products AS
  SELECT
    id,
    name,
    type,
    CASE WHEN type = 'A'
      THEN (price * 0.5)::integer
      ELSE price
    END AS price
  FROM products;

ポイントは元のテーブルスキーマとビューのスキーマが一致している事です。

テーブルの中身を確認すると...

 id |    name    | type | price
----+------------+------+-------
  1 | エアコン   | A    | 50000
  2 | 空気洗浄機 | A    | 25000
  3 | 椅子       | B    |  8000
  4 | テーブル   | B    | 12000
  5 | 炊飯器     | C    | 60000

type=Aのみ半額になっていますね。

アプリケーション(Rails)側の修正

Rails側で以下のような修正をしてしまうと、Productモデルを使っている箇所全てを書き換えないといけませんので非推奨です。

# 非推奨
DiscountedProduct.order("price")

ではどうするかと言うと、Productモデルの参照先のテーブルを先ほど作成したビューに向けてあげれば良いです。

class Product < ApplicationRecord
  # 期間限定でビューを参照する。
  self.table_name = "discounted_products"
end

これで、ActiveRecordのクエリ操作を一切修正せずに、type=Aを半額表示することに成功しました!

何が嬉しいか

何がそんなに嬉しいのかと言うと以下2点です。

  • アプリケーション側の修正が(ほぼ)不要になる
  • 期間限定のビューとして、使い捨てが可能

先ほども書きましたが、アプリケーション側の修正が不要になるには元のテーブルスキーマとビューのスキーマが一致している必要があります。

完全一致していなくても、元のテーブルに新たなカラムを追加したビューを作成すれば機能拡張しやすいと思います。(半額表示に加えて元値の金額も出したいなど)

また、半額期間が終了して元の価格に戻したい時も、モデルの向き先を元に戻すだけで済むのでとても楽ちんです。

今回作成したビューはテーブルの実体を持っているわけではなくデータベースの容量を圧迫することもありませんので、期間終了後に放置していても特に問題はないです。(管理という意味では消した方がいいですが。。)

デメリット

通常のビューを使ったクエリは構造的にはサブクエリになりますので、パフォーマンスには要注意です。indexがちゃんと効いているか確認した方がいいでしょう。

パフォーマンスが気になる場合、マテリアルビューを作成する方法もありますが、こちらは幾度ビューの更新をする仕組みを作る必要があり、全体のアーキテクチャを複雑化することに繋がりかねないのでよく検討した方が良さそうです。

また、ビューを使うということは結局のところ生クエリを書く事に他ならないので、場合によっては誰もメンテナンスできないような巨大なクエリが誕生してしまうかもしれません。今回の例みたいな期間限定イベントなどには適していると思いますが、ガッツリ運用していく必要のあるコアなロジックにビューを導入していいかはチームで検討した方がいいと思います。

まとめ

今回は簡単な例でしたので、ビューの威力をあまり伝えられなかったかもしれませんが、「ActiveRecordで記述したいけどクエリが複雑すぎる!」と感じた時に1つのアプローチとしてビューのことを思い出せるといいかと思います。

ご利用は計画的に。