# 9/27(火) ペアプロ:正規化すべきか検討 [TOC] ## ペアプロにあたって必要な情報 ### [カラムに空の配列が代入された時、バリデーションエラーを発生させずそのまま更新できるカスタムバリデーションを作りたい \| FBC](https://bootcamp.fjord.jp/questions/1438) より。 > 正規化すべきかどうかを検討するために、以下のような問いに対する答えを考えてみてください。 > - 正規化した場合、面倒だけど得られるメリットは何か? > - 正規化を崩した場合(つまり今の設計を維持する場合)、データの管理は楽になるが、その代わりに発生するデメリットは何か? おそらくこの問いは裏表の関係なので、片方が出せればもう片方の答えも自ずと導かれるものだと思います。 > > このメリットデメリットを天秤にかけた上で「こっち」という選択肢を選べれば、エンジニアとして合格点です👍 ### 分報のコメント https://discord.com/channels/715806612824260640/824909262911438888/1024225752364875807 より > 正規化しないと困るケースを考えるには、reviewers のユーザー情報にユーザー名以外の情報が含まれるケースを考えてみるとよいとおもいます > > 正規化していないと複数の pullreq のレコード間でユーザーの情報を重複して持たないといけないことになります > > 今回はユーザーの識別子であるユーザー名しか情報として持っていないので、正規化していなくても問題にはなりません > こういうケース。ただこのテーブルの情報は常に GitHub からしか降ってこず、アプリ側で reviewers のデータを更新することはないので、正規化してなくて困ることはなおさらなさそうです ``` { id: 123 reviewers: [{ login: @foo, name: 'Foo' }] } { id: 456 reviewers: [{ login: @foo, name: 'Foo' }, { login: @bar, name: 'Bar' }] } ``` --- ## ポスグレ以外は、カラムに配列で保存できる機能がない。カンマ区切りで行える(絶対やってはいけない) データベースには配列入れる機能はそもそもない ポスグレが特殊 他のシステムだとカンマ区切りで保存はできる(絶対やっちゃいけない)→正規化する 記事の非正規系のテーブル 仕入れ先と商品 = 1:n 今の私の設計だと 津軽ファームのレコードに[りんご,ぶどう,りんご]になってる これの良くないところ 非正規系だと更新がうまくいかないらしい カンマ区切りの悪いところ - 普通外部キーを設定する - かんま区切りだとただの文字列での保存なので外部キー制約かけられない - 配列も外部キーかけられない= デメリットの一つ https://docs.google.com/spreadsheets/d/1VhzVqhx-DDsE9c5UfyyknKzvXLeIA5kXym7oVLTSE1s/edit#gid=944604890 ## 正規化する場合、Userモデルとは別に、GitHubUserモデルが必要になる - userモデルのuidとreviewersのuidを日もづける意味はあまりない - プルリクのデータはこの自作サービスのシステムとは関係ないデータ。usersモデルは、システム内でできたデータ - なので、Usersモデルと PullRequestモデルの間に中間テーブルを作成することはできない - GitHubからふってきたuser一覧とシステム内のUser一覧が存在している。これを一つのモデルで管理できないので、正規化する場合、Usersモデルとは別にgithub_usersモデルの作成が必要になる。 - 正規化するとしたら、GitHubからふってきたuserのモデルが必要 ## ER図 - 正規化した場合のDB設計 ```mermaid erDiagram users { string uid string login_name string email string password_digest } github_users { string uid bigint user_id } github_issues { serial id } github_pull_requests { serial id } github_issue_assignees { bigint github_issue_id bigint github_user_id } github_pull_request_requested_reviewers{ bigint github_pull_request_id bigint github_user_id } users |o--o| github_users : todo github_users }o--|| github_issue_assignees : todo github_issues }o--|| github_issue_assignees : todo github_users }o--|| github_pull_request_requested_reviewers : todo github_pull_requests }o--|| github_pull_request_requested_reviewers : todo ``` - issue と userはn:n - github_usersは1つのモデル(issueとPRで2つとかは作らない) - github_users と pull_requests の関係は n:nなので、中間テーブルが必要 - github_usersはシステムのユーザーと関係ないユーザー - **reviewersの情報に、uidだけでなく、メアドや名前など、値を複数入れるなら、正規化が必要**。でも、今回uidしか入れてないのでいらない。 ## github_usersとusersのレコードが作成されるタイミング - github_usersから見たusers - まだサインアップしてないuserがアサインされてたりレビュー依頼されてたりすることがあり得るので、`0~1` - usersから見たgithub_users - github_usersはアサインされたりレビュー依頼されたり消されたりした人のユーザー(システムのユーザー) - まだissueとかプルリクのレビューをやっていないけど、とりあえず登録している、という状況があり得る - なのでgithub_usersに存在しないユーザーが、システムのuser(usersモデル)にあることがあり得る - よって、`0~1` - なので、2つのモデルの関係は`0~1`:`0~1` - この2つを関連付けする場合、userのidをgithub_usersに外部キーとして設定することになる。 そうすると、Railsの関連付けの仕組みによって、 `@user.github_user.github_issues`で欲しいデータを取得できる。=これが正規化して中間テーブルを作った場合のメリット (でも正規化していない現在の設計でも、`Issue.where("#{user.uid} = ANY(assignees)")`でuserがassignされたissueのリストは取得できる。) しかし、中間テーブルの作成/削除が非常に大変 - アサインされた時はcreateするだけなので、簡単 - 問題は外された時が難しい ```ruby 1回目 [foo, bar] 2回目 [foo] # 外された時にレコードを削除しないといけない ``` ```ruby! params[:assignees] = [123, 456, ...] # アサインされた時 params[:assignees].each do |assignees| GitHubIssueAssignees.create! end ``` 外されたときは、誰が外されたかの情報ははいってこない。消されたあとの情報がくる。  誰が削除されたかの情報が渡ってこない中で、差分をつきとめて差分を削除しないといけない。これが大変。 検索してひっかからないものを削除しないといけない - `全体 - ひっかかったもの = 引っかからないもの` - SQL文 `NOT IN 〜`構文を使えばいけるかも - Railsで書くと ```ruby! @user.github_user&.github_pull_requests.sum(:point) @user.github_user&.github_issues.sum(:point) ``` ## 今の正規化していないDB設計で起こりうる問題 - 一番の懸念事項:トップページに出したい情報は出せるのか→クリア - 次点:パフォーマンスの問題→未確認 - サイトの速さに問題ないかcurlコマンドとかクロムの機能 - 最初に、SQLのクエリ実行速度が遅くないか`rails s`した時にrack mini プロファイダーとかログで見る。 - 1秒かかってたら遅い - 本番環境とローカルでSQLのかかる時間は結構遅い - 1番の違い:ネットワークを経由してるかそうか - herokuのサーバーはアメリカorヨーロッパにしかない。日本→アメリカのサーバにアクセスすること自体がそもそも遅い - 日本からSQL発行→アメリカのサーバーのDBに接続する - ローカルだと、サーバーもDBも同じPC内だから早い。 - まずはローカルで実行速度を確認する。1秒以上だと遅い。 - 500ミリセックだと遅め。おそくても数百ミリ。 ## 結論 - 中間テーブルを作るメリット:レコードの作成や削除のタイミングが複雑なわりにデータのメリットがない。 - 正規化しているのは、システム内でデータ作成したUsersモデルではなく、GitHubの世界での正規化なので、あまり意味がない。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up