💬
月次のデータを管理するテーブルの設計例
想定しているケース
月次でユーザーのアクティビティを集計したり、売上を集計したりとか。
週次もほぼ同じパターンで設計できる。
色々なパターンを見てきたので自分なりの定型パターンを書いておく
Railsの機能を使うので他のフレームワークの場合は良くない設計かもしれない…
テーブル
-
beginning_of_month
というdate型のカラムを用意し、月初の日付を入れて「月次」とする
name = 'monthly_user_activities'
create_table name, id: :bigint, unsigned: true, force: :cascade, collation: 'utf8mb4_general_ci', options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t|
t.date "beginning_of_month", comment: "対象月(月初の日付を入れる)"
t.bigint "user_id", unsigned: true, null: false, comment: "ユーザーID"
t.integer "comment_count", unsigned: true, null: false, comment: "コメント数"
t.datetime "created_at", null: false, comment: "作成日時", precision: nil
t.datetime "updated_at", null: false, comment: "更新日時", precision: nil
t.index ["beginning_of_month", "user_id"], name: "key_#{name}_1", unique: true
end
ActiveRecord
app/models/monthly_user_activity.rb
class MonthlyUserActivity < ApplicationRecord
belongs_to :user
validates :beginning_of_month,
presence: true
validates :comment_count,
presence: true
scope :on_month, ->(date) { where(beginning_of_month: date.beginning_of_month) }
end
上記のようなコードになる
now = Time.zone.now
MonthlyUserActivity.on_month(now).create!(
user: user,
comment_count: 123
)
みたいに使う。
Rails 7.1以降
normalizes
が使える
app/models/monthly_user_activity.rb
class MonthlyUserActivity < ApplicationRecord
belongs_to :user
normalizes :beginning_of_month, with: -> (date) { date.beginning_of_month }
validates :beginning_of_month,
presence: true
validates :comment_count,
presence: true
end
now = Time.zone.now
MonthlyUserActivity.create!(
beginning_of_month: now,
user: user,
comment_count: 123
)
その他の設計パターン
遭遇したことがあるのは以下のパターン
- '202311' みたいな文字列で管理する
- 202311 という整数で管理する
- year と month に分けて管理する
どれもメリット・デメリットはあると思うが、最終的には beginning_of_month を使うパターンに落ち着いた
beginning_of_week を使えば週次にもできて同じパターンで実装できる
Discussion