Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

クイズアプリにおけるデータベース設計のアンチパターン

想定する読者

  • ポートフォリオとしてクイズ系アプリを作成している方

アンチパターン例 3つ

例えば4択クイズを作りたいというとき、以下のようなDB設計を思いつくかもしれません。
※筆者が思いつきました。
これらは明確なアンチパターンです。
何故アンチパターンかというと、仕様変更に弱いからです。

part1 マジックナンバーカラム

id question choice1 choice2 choice3 choice4 answer_number
1 次のうち偶数なのは? 3 7 8 1 3
2 「令和」はどう読む? れいわ へいせい しょうわ たいせい 1

仕様変更さん「○×クイズもできるようにしたい〜〜選択肢を2つに減らすだけだし簡単ですよね?選択肢も4つだけじゃなくて、5つとかもあり得るかも♪」

教訓: カラム名にマジックナンバーが入ってたらアンチパターンの合図

part2 フィールドに配列ぶちこみ

id question choices answer_number
1 次のうち偶数なのは? [3, 7, 8, 1] 3
2 「令和」はどう読む? [れいわ, へいわ, しょうわ, たいせい] 1
3 今は平成か? [○, ×] 2
4 次のうち一番大きい数字はどれか? [5, 10, 9, 8, 4, 1] 2

仕様変更さん1「よーし、検索機能を入れることになりました!検索機能って基本的な機能なので簡単ですよね?選択肢の中の文でも検索がかけられるようにしたいです♪」

仕様変更さん2「特定の選択肢は後で編集ができるようにしたいですね〜〜ユーザーさんって誤字とかしちゃうこともあるじゃないですか♪」

教訓: フィールドに配列を入れたくなったらアンチパターンの合図

part3 テーブルを分けて1対多にする(が、分けきれてない)

questionsテーブル

id content answer_number
1 「令和」はどう読む? 1
2 今は平成か? 2

choicesテーブル

id content question_id
1 れいわ 1
2 へいせい 1
3 しょうわ 1
4 たいせい 1
5 2
6 × 2

仕様変更さん「複数の答えを用意できるようにしたいです! この複雑な世の中で、答えって一つじゃないと思うんです♪」

何がダメだったのか: answer_numberはどちらかというとchoicesテーブルにあるべき情報だと思います。関心の範囲と、テーブルの範囲を合致させるように気をつけると、仕様変更による影響が少なくできると思います。

(おそらく)正しい設計

questionsテーブル

id content
1 「令和」はどう読む?
2 次のうち偶数のものはどれか?

choicesテーブル

id content is_answer question_id
1 れいわ true 1
2 へいせい false 1
3 しょうわ false 1
4 たいせい false 1
5 11 false 2
6 12 true 2
7 15 false 2
8 99 false 2
9 18 true 2

アンチパターンと上から目線で言いながら、これが最適かと言われれば微妙です笑
ただし、現役のエンジニアさんに見せたら「良いんじゃない?」って言われたんで、そこまで悪くない設計だとは思います。
マサカリ頂けると有難いです。

ちなみにRailsだったら、どんなmigrationファイル作ればいいの

$ ruby -v
ruby 2.6.5
$ rails -v
Rails 5.2.4
db/migrate/時間_create_questions.rb
class CreateQuestions < ActiveRecord::Migration[5.2]
  def change
    create_table :questions do |t|
      t.text :content, null: false # 問題文の中身
      t.references :user, foreign_key: true # クイズを作った人を想定

      t.timestamps
    end
  end
end
db/migrate/時間_create_choices.rb
class CreateChoices < ActiveRecord::Migration[5.2]
  def change
    create_table :choices do |t|
      t.text :content, null: false
      t.boolean :is_answer
      t.references :question, foreign_key: true

      t.timestamps
    end
  end
end
sumally
テクノロジーで「所有」のあり方をアップデートする
https://pocket.sumally.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away