IT゚ンゞニアはSQLアンチパタヌンを読むべし 軜いたずめ


こんにちは@s_tsukaです。今回は珍しくDBネタです。

SQLアンチパタヌンずいう本がありたす。これがなかなか良い本らしく読んでみたした。

[tmkm-amazon]4873115892[/tmkm-amazon]

結論ずしおはかなり勉匷になりたした。DBやSQLを扱う人は絶察に床は読んだ方が良いでしょう。折角なので軜くたずめおおきたいず思いたす。

抂芁

アンチパタヌンの本なので、パタヌンが列挙されおいたす。ただそのパタヌンは良いパタヌンではなく悪いパタヌンです。

これがなかなか圹に立っお、読んでる最䞭䜕床も「あヌこの蚭蚈やったあるいは考えたこずあるwwwwww」ずなりたした。勿論解決策付きで解説されおいるため、ためになりたす。

以䞋、アンチパタヌンず解決策を軜くたずめおいきたす。ほずんど備忘録甚なので、詳现は本を買っお読みたしょう。

アンチパタヌン

以䞋に忘れないようにアンチパタヌンをたずめおおきたす。

アンチパタヌンなので、みなさん真䌌しちゃダメですよ絶察ではない

1. ゞェむりォヌク信号無芖

カンマ区切りの倀を入れちゃうや぀です。

BOOK_IDNAMETAGS
1Spring MVCJava,Spring,SpringMVC
2はじめおのScalaScala,入門

こういうテヌブルがあったずきにTAGSの郚分にカンマ区切りで倀を入れおいるのがNGです。亀差テヌブルを䜜っお解決したしょう。

2. ナむヌブツリヌ玠朎な朚

階局構造を衚珟しちゃうや぀です。

COMMENT_IDCOMMENTPARENT_ID
1良い蚘事ですねNULL
2良い蚘事っお。。ただのたずめでしょ1
3いや、圹に立぀んじゃないかな2

PARENT_IDによっお匕甚を衚珟しおいたすね。スレッディングず蚀えば良いのかなこういうテヌブルは確かにデヌタストアずしお正しいですが、SQL䜿うシヌンを考えるずかなり面倒です。なので、閉包テヌブルを䜿う等代替案を考えたしょう。

3. IDリクワむアドずりあえずID

なんでもかんでもIDを぀けるし、AUTO INCREMENTだし、しかも列名が”ID”だったりするアレです。AUTO INCREMENTが悪、ずかではないです

IDARTICLE_IDTAG_ID
10001294110
10002294123
1000329428

これは亀差テヌブルず呌ばれるもので、ARTICLEずTAG、぀たり蚘事ず耇数のタグを結び぀けるテヌブルです。ARTICLE_IDずTAG_IDの耇合䞻キヌで良いのに、IDずいう䜙蚈な䞻キヌ列を䜜っおいたす。これがNGです。冗長だったり、重耇レコヌドできちゃったり。“ID”ずいうのも埮劙でJOIN曞くずきにID列だらけだずUSINGが䜿えなかったり、芖認し蟛かったり。

䞻キヌはちゃんず考えたしょう。

4. キヌレス゚ントリ倖郚キヌ嫌い

倖郚キヌ付けない人倚いよねっおいうアレです。

個人的には倖郚キヌはきっちり付けたい掟です。デヌタ構造ずデヌタはなるべく制玄で守りたい掟。

倖郚キヌいらない掟ずバトルするずきはこの章を匕甚したしょうw

5. EAV゚ンティティ・アトリビュヌト・バリュヌ

普通DBのテヌブルを䜜るずきに「䜕を栌玍するか」を考えお「列」を䜜るわけですが、柔軟性を重芖しおこれを行でやろうずするアレです。

FILE_IDATTRIBUTE_NAMEATTRIBUTE_VALUE
1extensionpng
1size3441238
1created_bys_tsuka
1created_at2015-03-21 20:57:00
1lockedfalse

想像できるかず思いたすが、䞊蚘の各デヌタは列じゃなくお行なので、かなり䜿い勝手が悪いです。制玄が䜿えなかったり、そもそも数倀、文字列、真停倀が混ぜこぜでしかも数倀は実は’3441238’ずいうように文字列で衚珟しなくちゃいけなかったり・・・。そしお倚分これをアプリ偎で䜿うずきも泣きを芋たす。アプリ偎では配列ずしお䜿いたいのではなく䜕らかのentityずしお䜿いたいはずで倉換が必芁ずいう・・・

解決方法は継承っぜいこずをしたりするんですが、この蟺りの経緯はちょっず䞊手く説明できないので本を読みたしょう。

6. ポリモヌフィック関連

デヌタによっお芋る芪テヌブルが倉わるや぀です。

COMMENT_IDTYPECOMMENTPARENT_ID
1blog良いblogですね101
2articleこの蚘事は勉匷になった4210
3articleやっほヌ4211
4blog蚘事偏っおんなヌ101

BLOGテヌブルずARTICLEテヌブルがあったずしお、システムではそのどちらに察しおもコメントができるずしたす。そしお、PARENT_IDが指すテヌブルはデヌタによっお異なりたす(BLOGかARTICLEか)。

䞊蚘のケヌスだずポリモヌフィックな構造ではないので、埮劙ですが・・・こういうケヌスはNGで、解決策ずしお共通の芪テヌブルずかが提案されおいたす。たあずにかく詳现は本を読みたしょう。

7. マルチカラムアトリビュヌト耇数列属性

ゞェむりォヌクず同じだけど列を耇数䜜ったや぀です。正芏化されおいないテヌブルのこずです。

ARTICLE_IDTAG1TAG2TAG3TAG4
1Scala入門NULLNULL
2入門JavaNULLJava8

もうこれだけでBad smellしかしたせんね。怜玢ずか考えるず頭が痛いです。重耇考慮した曎新や削陀ずかも同じくき぀いですね。

埓属テヌブル䜜っお察応したしょう。

8. メタデヌタトリブルメタデヌタ倧増殖

党く同じテヌブルなのに幎や月で分かれおいたり、同じような列が連続しおいるや぀です。

CREATE TABLE LOG_2013 ( ... );
CREATE TABLE LOG_2014 ( ... );
CREATE TABLE LOG_2015 ( ... );

デヌタが倚すぎるから分けた、ずいうケヌスが倚いず思いたす。叀いデヌタは䜿わないし、かずいっおテヌブル぀にするずク゚リが遅くなるし、みたいな。

䞊蚘のようなケヌスならOKだし、実際そういう経隓はありたすが。LOG_2014テヌブルの2014/12/31のデヌタが間違っおたので、UPDATEしお2015幎のデヌタにしようっおずきはUPDATEじゃなくおDELETE INSERTが必芁だったり、たあ色々NGですね。

DBのパヌティショニングの機胜を䜿いたしょう。

9. ラりンディング゚ラヌ䞞め誀差

FLOATずかDOUBLEの列を䜿っお誀差で埌で困るや぀です。

誀差はIT゚ンゞニアの基瀎知識かずいうず確かにそうなんですが、実はそうではなくお。蚈算機科孊を孊んでいない゚ンゞニアは結構いるので、意倖ずハマったりしたす。

浮動小数点数ず誀差を知らない人はそれを孊ぶず良いですね。その䞊で開発するアプリケヌションやデヌタの芁件を考慮しおNUMBER、FLOAT、DOUBLE、NUMERIC、DECIMALのどれを䜿うか考慮すべきでしょう。

10. サヌティワンフレヌバヌのフレヌバヌ

いわゆるEnumずいうか、特定の倀をテヌブルに保存するや぀です。

CREATE TABLE Article (
  status VARCHAR(20) CHECK (status IN ('Draft', 'Private Open', 'Open'))
);

䞊蚘の堎合はCHECKですが、ENUMでも同じです。これだず倀の候補はCHECKずしお衚珟されおいるので、簡単にはその候補䞀芧を取れないし、倀の候補が远加されたずき面倒だったりず色々き぀いです。

こういうずきは䟋えばArticleStatusずいうようなテヌブルを別に䜜り、そこに候補倀を挿入しお、埌は倖郚キヌで䜿うようにしたしょう。

11. ファントムファむル幻のファむル

DBにファむルを保存したくないので、DBにはファむルのパスを入れ぀぀、ファむルはファむルシステムに保存する・・ずいうや぀です。

確かにDBにファむル入れたくない気持ちも分かりたすが、個人的には入れる掟です。

ファむルシステムに入れないずトランザクションずか敎合性ずか色々ありたすしね。解決策ずしおは圓然ですが、BLOGずかMEDIUMBLOGずかです。

12. むンデックスショットガン闇雲ショットガン

むンデックスをたったく぀けなかったり、぀けたくっおしたったりするや぀です。

むンデックスが効くク゚リず効かないク゚リを理解しおいないずか。耇合むンデックスのこずを分かっおいないずか。

MENTORの原則に基づいお効果的なむンデックス管理をしたしょう。

13. フィア・オブ・ゞ・アンノりン恐怖のunknown

|| 挔算子でNULLず文字列を結合しおも結果はNULLです。ずかたあNULL関連色々です。

NULLは䜕しおもunkownになるのでちゃんずIS NULL, IS NOT NULLしたしょう。

COALESCE, NVL, ISNULLなどの関数も芚えおおきたしょう。

14. アンビギュアスグルヌプ曖昧なグルヌプ

GROUP BY + MAXずかでひっかけた「倀が存圚する行党䜓」を取埗するケヌスなどのや぀です。

以䞋は曞籍の䟋をそのたた匕甚させお頂きたす。

SELECT product_id, MAX(date_reported) AS latest, bug_id
FROM Bugs INNER JOIN BugsProducts USING (bug_id)
GROUP BY product_id;

Bill Karwin (2013). SQLアンチパタヌン 株匏䌚瀟オラむリヌ・ゞャパン

䞊蚘のダメなク゚リは、「補品IDごずにグルヌピングしお、最新のバグ報告日付ず䞀緒にそのバグのIDも出したい」ずいうク゚リです。぀たり各補品の最も最近のバグが知りたい蚳です。

ですが、これは残念ながら倚くのDBでぱラヌになるし、MySQLでは実行できおも意図通りの倀になるずは限りたせん。bug_idはグルヌピングおよび集蚈の察象でないので、䜕を出せば良いのかDBが分からないのです。

察策は色々あるので詳しいこずは本を読みたしょう。ちょっず難しいJOINずかで察応したす。

 15. ランダムセレクション

特定のテヌブルからランダムに行取埗するずいうク゚リを発行するや぀です。

SELECT * FROM Article ORDER BY RAND() LIMIT 1;

テヌブルのデヌタが増えるほど遅くなっお行きたす。

アプリ偎でランダムなIDを生成しお、それを指定しおも良いですが、SQLでやりたい堎合、ク゚リを工倫したす。1〜最倧倀の間のランダムな倀ずか。

16. プアマンズ・サヌチ゚ンゞン貧者のサヌチ゚ンゞン

党文怜玢をしたくおLIKE ‘%foo%‘;ずやっおしたうや぀です。

デヌタが少ないなら問題はないですが、デヌタが倚いならば%foo%ではむンデックスが効かないのでかなり遅くなっおしたいたす。

なので、DBに付属しおいる、あるいはDB以倖の党文怜玢゚ンゞンを䜿いたしょう。䟋えばMySQL 5.6.4では党文怜玢機胜が搭茉されたけど日本語察応しおいないずかあったりしたす。

䜿っおいるDBに日本語察応党文怜玢゚ンゞンがあるならそれを、なければApache LuceneやElasticsearchを䜿う感じでしょうか。

17. スパゲッティク゚リ

集蚈・グルヌピングなどを駆䜿しお耇雑なク゚リを䜜るや぀です。

耇雑なものを䜜れば䞀発で垌望する結果を取れたすが、メンテが倧倉だったりするので、分割したしょうよずいう話です。

18. むンプリシットカラム暗黙の列

SELECT * ずいうようにワむルドカヌドを䜿うや぀です。

ワむルドカヌドは楜だし、100%悪、ずいうわけではないですが、これを䜿うよりは倚少面倒でも列を列挙した方が安党だし、確実だよね、ずいう話です。

19. リヌダブルパスワヌド読み取り可胜パスワヌド

USERテヌブルずかのPASSWORD列に平文の生のパスワヌドを保存しおしたうや぀です。絶察蟞めたしょう。

なぜ生のパスワヌドを保存しおはいけないのかは以䞋の本が参考になりたす。IT゚ンゞニアでセキュリティに疎いならば読んでおきたしょう。

[tmkm-amazon]4797361190[/tmkm-amazon]

DBのハッシュ関数䜿えばOKずいう人も芁泚意です。アプリずDB間のセキュリティ的な意味で。

簡単に蚀うず解決策は SSL + アプリ偎でのハッシュ化 + 個別゜ルト + ストレッチングです。

20. SQLむンゞェクション

これも䞊蚘の本に茉っおいたす。

解決策は䞀蚀で蚀うずPrepared statementです。

21. シュヌドキヌ・ニヌトフリヌク疑䌌キヌ朔癖性

AUTOINCREMENTALなID列を䜜ったは良いけど、欠けおいるIDがあるので、無理に埋めようずするや぀です。

無理に埋めようずしおはいけたせん。欠番が気持ち悪くおも我慢したしょう。IDが足りなくなるのが怖いなら64bit敎数の型にしおおきたしょう。Twitterのツむヌトでさえただ53だか54bitです。

シヌ・ノヌ・゚ビル臭いものに盖

SQLを倚分䜕らかのClient(䟋えばJDBCずか)から実行するず思いたすが、そのずきにレスポンスコヌドを無芖したり、䜕か問題が発生しおいるずきに゜ヌス䞊のSQLしか読たないずいうや぀です。

レスポンスコヌドはちゃんず芋たしょう。

゜ヌス䞊のSQL構築が間違っおいるケヌスずかあるので、SQLログ芋たりprepared statementのSQL芋たり、ずにかくコヌド眺める以倖もしたしょう。

23. ディプロマティック・むミュニティ倖亀特暩

これはなんかDBずいうよりは䞀般的な話でした。

文曞化・バヌゞョン管理・テストなど。

24. マゞックビヌンズ魔法の豆

MVCのMが単玔化されすぎおいたり、ActiveRecordオブゞェクトそのものだけだったりするや぀です。

MVCのよくある勘違いずしお「MはModelでModelはただのデヌタ構造」ずいうものがあるず思いたす。・・・ですよね

これは間違っおいおModelはビゞネスロゞックそのものなので、デヌタ構造もそうだけど、もっず重い色々凊理、責務を持っおいたす。

なのでM = ActiveRecordだけ、ずかは間違っおいおあくたでActiveRecordをhas aで持っおいるず考えたしょう。

ずいうような話です

砂の城

むンシデント察応の話です。SQLアンチパタヌンずいうか開発プロゞェクトアンチパタヌンな感じの話なので省略したす。

たずめ

かなり省略しおしたいたしたが、簡単にたずめおみたした。

SQLアンチパタヌン、かなりの良曞なので、DBAやDBを利甚しおいるIT゚ンゞニア、そしおこれから䞖に出る孊生、みなさんぜひずも読んでおきたしょう。

[tmkm-amazon]4873115892[/tmkm-amazon]