Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
SQLアンチパターンから学ぶ『中間テーブル』を使用するメリット
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Kami Makoto
July 26, 2020
Technology
4.8k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
SQLアンチパターンから学ぶ『中間テーブル』を使用するメリット
社内の勉強会の発表資料
Kami Makoto
July 26, 2020
More Decks by Kami Makoto
See All by Kami Makoto
DataHubを活用した集計基盤の構築過程と工夫したこと
kaminotsukai
0
1.4k
AtCoderを1ヶ月全力でやって『体感』したこと
kaminotsukai
0
320
Other Decks in Technology
See All in Technology
アクセスキーが漏れた日にやるべきこと- 無効化の先にある本当の対応
kazzpapa3
0
330
【書籍出版記念】 10周回って、エージェント開発は RAGがすべてだった。〜RAGの歴史と開発現場で見えた実践知〜
akiratameto
20
7.6k
markdown-poster Introduction
kazamori
0
190
「ミスを許さない手順書」を作ってみた 〜 個人的にはこれ以上できることはあまりなさそう/20260827-ssmjp-operation-procedure-update
opelab
4
1k
カーネルまで探検して理解するふたつの自動計装
sumiyae
0
320
ハッカソンで入賞した話 @ Findy LT
asari194617
0
1.5k
Claude Teamプランの コスト最適化を考える
rfdnxbro
0
700
内製AIチャットボット開発で学んだ Datadog Agent Observability活用術
mkdev10
0
140
アクセスキー流出時の対応で再認識した攻撃側と防御側の非対称性について
kazzpapa3
0
300
Digitization部 紹介資料
sansan33
PRO
2
7.7k
電話に出る Python のログの話
shinnosuke_kishida
0
270
Kiro WebとCloud Sessions
nagisa53
2
200
Featured
See All Featured
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
300
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
4
490
Product Roadmaps are Hard
iamctodd
55
12k
Docker and Python
trallard
47
4.1k
[SF Ruby Conf 2025] Rails X
palkan
2
1.3k
Claude Code のすすめ
schroneko
67
230k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
480
Context Engineering - Making Every Token Count
addyosmani
9
1.1k
Visualization
eitanlees
152
17k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.6k
Transcript
SQLアンチパターンから学ぶ! 「多対多には中間テーブルつけておけ」 の理由を理解する
ジェイウォーク - 信号無視
ジェイウォーク - 信号無視 id 1 1, 2 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1
2 user_id account ⼝座テーブル こんなテーブル⾒たことありませんか? ⼀つのカラムに『カンマ区切り』で複数の値が⼊っている
ジェイウォーク - 信号無視 id 1 1, 2 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1
2 user_id account ⼝座テーブル こんなテーブル⾒たことありませんか? これ、アンチパターンです!
ジェイウォーク - 信号無視 この現象はどうやって起きるのか? ⼝座 ユーザー ⼀⼈で複数の⼝座を持てる。 ⼝座は複数⼈で扱うことはできない ⼀⼈で複数の⼝座を持てる。 1つの⼝座(会社の⼝座)を複数⼈で扱える
⽇が過ぎて仕様変更が⼊る・・・ +
ジェイウォーク - 信号無視 この現象はどうやって起きるのか? id 1 ⼭本ジェイ ⽥中ウォーク 2 name
ユーザーテーブル (users) id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1 2 user_id: int account: varchar(100) ⼝座テーブル (accounts) 3 2 zzzzzzz-zzzzzzz ← この⼝座は複数⼈で管理したい 少しでも変更を少なくしたいなあ エンジニアの気持ち
ジェイウォーク - 信号無視 この現象はどうやって起きるのか? id 1 ⼭本ジェイ ⽥中ウォーク 2 name
ユーザーテーブル (users) id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1 2 user_id: varchar(30) account: varchar(100) ⼝座テーブル (accounts) 3 1, 2 zzzzzzz-zzzzzzz 1 user_idのカラムの型をintからvarcharに変更 2 user_idカラムにカンマ区切りでuser_idを挿⼊ 変更点
ジェイウォーク - 信号無視 これの何が問題なの? 1 ユーザーIDカラムの型がvarcharになったことで数値以外も⼊ってしまう 2 クエリの作成が難しくなる(例)ユーザーの共有解除 3 登録できるユーザーがuser_idの桁数によって変化してしまう
ジェイウォーク - 信号無視 1 ユーザーIDカラムの型がvarcharになったことで数値以外も⼊ってしまう id: int 1 1 xxxxxxx-xxxxxx
xxxxxxx-yyyyyy 1 2 user_id: int account: varchar(100) ⼝座テーブル (accounts) 3 2 zzzzzzz-zzzzzzz id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1 2 user_id: int account: varchar(100) ⼝座テーブル (accounts) 3 Sample zzzzzzz-zzzzzzz id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1 2 user_id: varchar(30) account: varchar(100) ⼝座テーブル (accounts) 3 2, Sample zzzzzzz-zzzzzzz 数値じゃない! ⽂字列が⼊るので問題なし! 変更前 変更後
ジェイウォーク - 信号無視 1 ユーザーIDカラムの型がvarcharになったことで数値以外も⼊るようになった id: int 1 1 xxxxxxx-xxxxxx
xxxxxxx-yyyyyy 1 2 user_id: int account: varchar(100) ⼝座テーブル (accounts) 3 2 zzzzzzz-zzzzzzz id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1 2 user_id: int account: varchar(100) ⼝座テーブル (accounts) 3 Sample zzzzzzz-zzzzzzz id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1 2 user_id: varchar(30) account: varchar(100) ⼝座テーブル (accounts) 3 2, Sample zzzzzzz-zzzzzzz 数値じゃない! ⽂字列が⼊るので問題なし! 変更前 変更後 なんでも⼊⼒できるので データが意味不明になる
ジェイウォーク - 信号無視 2 クエリの作成が難しくなる(例)ユーザーの共有解除 <?php $stmt = $pdo->query( “SELECT
user_id FROM accounts WHERE id = 3” ); $row = $stme->fetch(); $contact_list = $row[ʻuser_idʼ]; $value_to_remove = 2; // 削除したいユーザーのID $contact_list = explode(ʻ,ʼ, $contact_list); $key_to_remove = array_search($value_to_remove, $contact_list); unset($contact_list[$key_to_remove]); $contact_list = join(ʻ,ʼ, $contact_list); $stmt = $pdo->prepare( “UPDATE accounts SET user_id = ? WHERE id = 1” ); $stmt->excute(array($contact_list)); id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1 2 user_id: varchar(30) account: varchar(100) ⼝座テーブル (accounts) 3 1, 2 zzzzzzz-zzzzzzz Q. この⼝座をユーザー2が扱えないように設定しなおしたい A
ジェイウォーク - 信号無視 2 クエリの作成が難しくなる(例)ユーザーの共有解除 <?php $stmt = $pdo->query( “SELECT
user_id FROM accounts WHERE id = 3” ); $row = $stme->fetch(); $contact_list = $row[ʻuser_idʼ]; $value_to_remove = 2; // 削除したいユーザーのID $contact_list = explode(ʻ,ʼ, $contact_list); $key_to_remove = array_search($value_to_remove, $contact_list); unset($contact_list[$key_to_remove]); $contact_list = join(ʻ,ʼ, $contact_list); $stmt = $pdo->prepare( “UPDATE accounts SET user_id = ? WHERE id = 1” ); $stmt->excute(array($contact_list)); $contact_list = ʻ1, 2ʼ; $contact_list = [1, 2]; $contact_list = 1; id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1 2 user_id: varchar(30) account: varchar(100) ⼝座テーブル (accounts) 3 1, 2 zzzzzzz-zzzzzzz Q. この⼝座をユーザー2が扱えないように設定しなおしたい A 1. user_idのカラムのデータ を⽂字列で取得 2. explodeでカンマで分割した ⽂字列の配列を取得 3. 2で取得した配列から2を 削除したものを⽂字列で取得
ジェイウォーク - 信号無視 2 クエリの作成が難しくなる(例)ユーザーの共有解除 <?php $stmt = $pdo->query( “SELECT
user_id FROM accounts WHERE id = 3” ); $row = $stme->fetch(); $contact_list = $row[ʻuser_idʼ]; $value_to_remove = 2; // 削除したいユーザーのID $contact_list = explode(ʻ,ʼ, $contact_list); $key_to_remove = array_search($value_to_remove, $contact_list); unset($contact_list[$key_to_remove]); $contact_list = join(ʻ,ʼ, $contact_list); $stmt = $pdo->prepare( “UPDATE accounts SET user_id = ? WHERE id = 1” ); $stmt->excute(array($contact_list)); id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 1 2 user_id: varchar(30) account: varchar(100) ⼝座テーブル (accounts) 3 1, 2 zzzzzzz-zzzzzzz Q. この⼝座をユーザー2が扱えないように設定しなおしたい A SQL + PHP両⽅ゴリゴリ書かないといけない
ジェイウォーク - 信号無視 3 登録できるユーザーがuser_idの桁数によって変化してしまう id: int 1 ??? xxxxxxx-xxxxxx
xxxxxxx-yyyyyy 1 2 user_id: varchar(30) account: varchar(100) ⼝座テーブル (accounts) 3 2 zzzzzzz-zzzzzzz ʼ10,11,12,13,14,20,21,30,35,38ʼ 『10⼈』のユーザーが登録できる ʼ1020,1100,1210,1321,1433,2019,2100,3099…ʼ 『6⼈』のユーザーしか登録できない
ジェイウォーク - 信号無視 じゃあ、どうすればいいの? id: int 1 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy
1 2 user_id: int account: varchar(100) ⼝座テーブル (accounts) 3 2 zzzzzzz-zzzzzzz id: int 1 ⼭本ジェイ ⽥中ウォーク 2 name: varchar(30) ユーザーテーブル (users) 多 多
ジェイウォーク - 信号無視 じゃあ、どうすればいいの? id: int 1 xxxxxxx-xxxxxx xxxxxxx-yyyyyy 2
account: varchar(100) ⼝座テーブル (accounts) 3 zzzzzzz-zzzzzzz id: int 1 ⼭本ジェイ ⽥中ウォーク 2 name: varchar(30) ユーザーテーブル (users) user_id 1 3 1 2 account_id ⼝座とユーザーの中間テーブル (user_account_management) 2 3 1 1 多 多 中間テーブルを作る ※ 同じペアが複数存在する状況はおかしいので、 user_idとaccount_idのペアにUNIQUE制限をかけること!
ジェイウォーク - 信号無視 1 ユーザーIDカラムの型がvarcharになったことで数値以外も⼊ってしまう user_id: int 1 3 1
2 account_id: int ⼝座とユーザーの中間テーブル 2 3 数値しか⼊らなくなったので、 想定外の⽂字が⼊⼒されていないかを ⼼配する必要がなくなった!
ジェイウォーク - 信号無視 2 クエリの作成が難しくなる(例)ユーザーの共有解除 <?php $stmt = $pdo->query( “DELETE
FROM user_account_management WHERE account_id = 3 AND user_id = 2” ); $stmt->excute(array($contact_list)); user_id 1 3 1 2 account_id ⼝座とユーザーの中間テーブル (user_account_management) 2 3 このレコードを削除すると関連がなくなるので ユーザーを解除した扱いになる!
ジェイウォーク - 信号無視 2 クエリの作成が難しくなる(例)ユーザーの共有解除 <?php $stmt = $pdo->query( “DELETE
FROM user_account_management WHERE account_id = 3 AND user_id = 2” ); $stmt->excute(array($contact_list)); user_id 1 3 1 2 account_id ⼝座とユーザーの中間テーブル (user_account_management) 2 3 このレコードを削除すると関連がなくなるので ユーザーを解除した扱いになる! SQLオンリーで楽になった!
ジェイウォーク - 信号無視 3 登録できるユーザーがuser_idの桁数によって変化してしまう これに関しては、制限をかけないければ無限に登録ができるようになった! 逆に制限をかけたい場合は以下の⼿順で簡単に⾏える! (1)中間テーブルから特定の⼝座と紐づいているユーザー数を取得(COUNTなどで取れるよ!) (2)取得したユーザー数が制限数を上回っていないか確認 (3)追加で登録しても⼤丈夫なら中間テーブルに追加