ぽんぽんぺいんでつらたんなので、おしごとおやすみしまーす

🗣: バイクに乗ったり、ものづくりしたり、ひたすら寝たり。

ユニバーサルデザインの機能についてまとめてみる。

前記事から引き続き、UIとユニバーサルデザインについて触れていきます。
こちらの本をベースに記事内容を展開していきますので、よろしくお願い致します。
(著者のユニバーサルデザインに対するこだわりは前記事からどうぞ)

www.amazon.co.jp

ユニバーサルデザインの7原則

前記事でUIやユニバーサルデザインにこだわる背景は紹介したと思うので、今回は本旨に移ります。
そもそもユニバーサルデザインはどのような法則性に則りデザインされているのか見ていきましょう。

wikipediaより

1. どんな人でも公平に使えること。(公平な利用)
Equitable use

2. 使う上での柔軟性があること。(利用における柔軟性)
Flexibility in use

3. 使い方が簡単で自明であること。(単純で直感的な利用)
Simple and intuitive

4. 必要な情報がすぐに分かること。(認知できる情報)
Perceptible information

5. うっかりミスを許容できること。(失敗に対する寛大さ)
Tolerance for error

6. 身体への過度な負担を必要としないこと。(少ない身体的な努力)
Low physical effort

7. アクセスや利用のための十分な大きさと空間が確保されていること
(接近や利用のためのサイズと空間)
Size and space for approach and us


The Center for Universal Design, NC State University による。

本書では12のテクニックがあげられていますが、今回はかいつまんでうち6つを紹介します。

発見性 到達性 判別性
秩序性 柔軟性 連想性
直感性 可読性 理解性
公平性 快適性 審美性

1. 発見性

f:id:letterneginr:20181213205334p:plain コントラストを強調する
・視認性の低い色調同士の配色では、文字が目立たずユーザーに認識してもらえる機会を逃してしまいます。
・あえて他のセクションと差別化させる為、色相ブロックの配置を転換することは有効です。

視認性のコントロールで優先順を明確にする
・上述したように、セクション毎で明度や色調を変える事でユーザーに伝えたい情報の優先順位づけが可能になります。

クリーム色の背景と青色の背景に白い文字
セクションのコントラスト比

視認性のコントロールで可読性を上げる
・しかし、セクション内での色の使い分けは重要です。
薄目にしてセクションを見比べてみると、文字が際立って見える方はどちらでしょうか。
視力が弱い人への配慮を想定する場合にはこのような明度や色調の使い方は重要です。


2. 到達性

f:id:letterneginr:20181213205334p:plain 伝えたいを絞り込む

煩雑さを避けて情報の要約に努める
・大量の情報を受け取った場合に、うまく処理ができず、「対応できない」と感じてしまう場合があります。
・特に高齢者や左脳的な文字面の処理が苦手な人などに対しては不安感や苦手感を与えてしまう可能性があります。
・大量の情報は項目化し、見出しを着色するなどして情報をしやすいようにパターンを決めてみましょう。

項目化された情報
・色を使用できない場合はピクトグラム化(絵にしてしまう)事も有用です。

3. 連想性

f:id:letterneginr:20181213205334p:plain 自然の造形からヒントを得る
・絵を使うと言っても、その意図が伝わるかどうかは文字よりも不確実であることは拭いきれません。
・これは文章にも言えることですが、必ずしもエンジニアの画力が高いとは言えないからです(笑)
ピクトグラムのデザインをする機会があれば、私たちにとって身近で、触れたことがある物に例える事が近道になるかもしれません。

フィッシュボーンで図解されたフロー図
フィッシュボーン
・この本では『フィッシュボーン』という形態図を例に取り上げています。要因を書き出す際にはエゴグラムのように情報を書き出してしまいがちですが、こちらは要素との相関が流れとして表現できるのが魅力です。
・本能的な喚起を促す為、ハチのイラストを取り入れて黄色と黒のコントラストで注意を引く事も有用なようです。
ユーザーにシンボルから連想させる事で、情報の整理を促します。

4. 直感性

f:id:letterneginr:20181213205334p:plain 簡潔な選択肢を提示する
情報の優先順位

・便利な機能を実装しすぎた結果、導線が複雑になり、ユーザーがその使用用途がわからずに迷ってしまうことがあります。
・人は似たような選択肢を見かけた場合に、消去法で情報を整理する場合があります。
・伝えたい内容を強調したり、大項目と小項目に分けて整理する事も重要です。

チャット画面が2パターンあって、吹き出しで説明がないものとあるもの
チャット画面のチュートリアルの例

5. 理解性

f:id:letterneginr:20181213205334p:plain 主体はユーザーである
・プロダクトを使用するのはユーザーです。情報の受け手であるユーザーは、私たちが表現する情報を意図通りに受け止め、
機能の使用の導線まで辿り着くことができない場合もあります。

・だからと言って情報を提示しすぎることは、ユーザーの考える余地を失くしてしまい、UX体験の低下にも繋がってしまうのではないでしょうか。

・ユーザーペルソナを設定し、プロダクトを使用するシナリオを描くことが有効です。
ユーザーがプロダクトを使用した時の経験を想起させる事もこのテクニックでは重要なポイントとなります。

プログラミングの広告例
プログラミングスクール勧誘のビラの例(プログラミングを学んでユーザーがどういう経験を経るのかがフロー立てて紹介されている)

6. 公平性

f:id:letterneginr:20181213205334p:plain 利用する人の気持ちを考える
配色への配慮
・アジア人の20人に1人が色覚異常の特性を発現しているようです。
私の同級生にも色盲の方がいましたが、桃色がグレーに見えるらしいです。
→このような見え方の人に対して柔らかい雰囲気の表現に暖色を使わないという配慮も取れるかと思います。
先の記事で示した通り、世界観は文字の形でも表現することは可能です(場合によるかもしれませんが)

ちなみに、日本人は世界中で一番緑の色調さを見分けることのできる種族のようです。
このような見え方を参考にしてもいいかもしれません。

デザインはロジカルで楽しい

デザインは美しければそれでいいと思う時期があって、素材がきれいでないとダメ、
配色センスがないとダメ、みたいな考え方でいたのですが、表現したいもの、それを伝えたい相手のことを具体的にすることで
自然とデザインはロジカルになることに気がつきました。

こちらの書籍も大変為になるので、興味ある方はぜひ読んでみてください。

エンジニアのための理論でわかるデザイン入門 ThinkIT Books Kindle版

kindle unlimited契約で無料とな👀✨


引用
ユニバーサルデザイン - Wikipedia
Material Designからアイコンをお借りしました
navi.dropbox.jp


(飲食店ネタは尽きている)

UI・ユニバーサルデザインについて思う事をメモしておく。

こんにちは!
記事毎にテンションのムラがある人です。無理しないがモットーなので、極力品質を落とさず見に来てくれる方に新たな知見を見出していただけるような記事を書くことにします🐶笑



今回は緩めな内容ですが、個人的に気になっているUIについてのメモです。

グラフィックデザイナーの為のユニバーサルデザイン実践テクニック51
上記の本を読んだので、共感したポイントや、実際に現場で活用していきたいテクニックをまとめます。

UIを気にしている背景

個人的なポイントは主に2つあります。
①その機能の意図は正しくユーザーに伝わっているか
②そのUIは競合よりもユーザーに受け入れられるものか


①その機能の意図は正しくユーザーに伝わっているか


あるシステム会社で目にした案件の例です。卸業者と顧客がチャット機能を介し、注文が完結するようなシステムの改修に携わったことがありました。
UIはFBのmessengerに似ています。下の見た目に設定や注文ボタンがくっついているようなイメージです。

FacebookのチャットUI
FacebookのチャットUI(5年前)
しかし、チャットから注文フローに行く為に少し癖のあるポイントがあって、注文方法が2パターン存在するものになっていました。注文のステータスによってボタンの文言も変わるので、ユーザーは今自分が何をしていて、このフローのゴールまでのどの位置にいるのかがわかりづらかったようでした。
この機能のリリース後、CSチームはユーザーからの仕様説明に追われる事になり、CS側が機能の把握のキャッチアップに追われたり、開発側でもそもそもこの機能は誰の為、何の為にリリースされたの?という話し合いが持たれたりなどしました。


この事実を重く見た上司は、(チャットルームに入室かつ一度もメッセージを送っていない)ユーザーに対してチュートリアルの展開を実施し、視覚的に用途の説明を促したのでした。
これが功を奏してCSチームに別添えの説明書に載っているような問い合わせは激減したわけです。
文字面ベースの説明ではなく、インタラクティブに動くギミックであることが重要です。
それでも直感的な操作に慣れていない人や、ブラウザによって見方が変わってしまうユーザーに対しては別途の対策を取る必要があります。

②そのUIは競合よりもユーザーに受け入れられるものか

この世にはいろんなアプリケーションが溢れかえり、同じ指向性を持った多数のプロダクトが存在しています。
よくレッドオーシャンなどと例えられますが、その中で差別化を図るポイントは、機能性と同格に位置付けられる見た目にあると思います。

①で説明したように、人はわからんものを敬遠してしまうところがあります。年齢を経ると、予測できないものに対する左記の性質がさらに強化されるとも言われています。
書いてあることが同じでも、分厚い辞書と章毎に分けられた英単語の冊子であれば後者の方が手に取りやすいですし、学習の予測も立てやすいです、個人的には。


さらに言えば、ユーザー層によって好まれる装飾や世界観があります。

飲食店のメディアサイトであれば暖色系のUIが好まれるかと思います。一般的に寒色系の色はビジネスや清涼さを表現するものだからです。また赤に近い色は人の食欲を増進すると言われています。食べログさんやRettyさんは基調色がオレンジ色のUIですよね。


上記の例で言えば、飲食店の情報を求めてやってくるユーザーは掲示されている情報の値踏みをします。
webページの見た目の情報はテキストと画像で構成されていて、ユーザーは目視で到達意欲をそそる飲食店の情報を抽出し、これらの内容を素早く直感的に判別し、必要な情報のみ精査する動きをするかと思います。


下記にあげてある画像は世界観に関しての比較画像です。イメージはタイトルの通りですが、文字・画像・付加要素(罫線、スタンプ)により情報が整理されているのがわかります。
やわらかい印象を与えるフォントを使用する事で、幼さや可愛らしさを表現することができます。
ゴシック調のフォントを使用すればリッチな印象、情報の確実さを助長するような表現ができます。

これはこの記事を目にするユーザー層によって使い分ける事で、プロダクトを選定する基準にもなります。


週刊誌の連載グルメコーナーのイメージ
週刊誌の連載グルメコーナーのイメージ

女子向け雑誌のおすすめグルメコーナーのイメージ
女子向け雑誌のおすすめグルメコーナーのイメージ

ユニバーサルデザインを大事だと思う理由

私はもともと福祉系の仕事をしていました。障害のある方と内職をこなしていく仕事でありましたが、情報伝達は言語だけではなく、視覚や聴覚から得られる情報は彼らにとって非常に有用なものであります。(なんなら聴覚ベースよりも箇条書きの文書を見せただけで仕事の工程を一瞬で記憶する人もいます。)


この部分に私は面白さを感じました。自分の思う相手の感じ方をいくつも想像して、その人に合った集中法を考え、実践するのはとてもやりがいがありました。相手の感じ方にうまく寄り添うことができれば、相手にとって苦手意識の克服や、自己強化の良い思い出になりますし、新しい事に挑戦する自信をつけてもらうことができるのです。


少し話が逸れてしまいましたが、専門学校ではユニバーサルデザインというものを学びました。

wikipediaより

世界初のユニバーサルデザインの提唱は、米ノースカロライナ州立大学デザイン学部・デザイン学研究科(College of Design)のロナルド・メイスによるものである。カリフォルニアにあるユニバーサルデザインセンターの長でもあった彼が1985年に公式に提唱した概念[1]とされる。

「できるだけ多くの人が利用可能であるようなデザインにすること」が基本コンセプトである。デザイン対象を障害者に限定していない点が「バリアフリー」とは異なる。

小学校の時に鉛筆の握り方を矯正する器具を使ったことがありますか?あれは治具というものですが、あれもユニバーサルな設計が取り入れられています。指先の発達が未熟な為に、鉛筆の握りが困難な人の為に作られた製品です。

鉛筆を握られなければ昔は文字を書くことができませんでした。しかし、今では発声から文字入力を手伝うデバイスもありますし、パネルから指で書いた文字を推測し、変換してくれるミドルウェアもあるでしょう。


表現方法は多様化し、普及しつつあります。
それでもその高速な進化を遂げたメソッドの扱い方に戸惑いを覚え、人の利益の発展や幸せの為に設計されたプロダクトを使用するところまで行き着かず、
一線を引かれてしまう事例もこれから出てくるのではないかと考えます。


私個人としてはこれが残念に思えて仕方がないのです。
見方や使い方に工夫をする事でアプリをシェアすることが出来、そこから得られる素晴らしい体験を出来ない人たちがいるというのは寂しいことです。

抵抗や偏見を排し、安全に使用できるプロダクトを生み出していくのはエンジニアの使命だと感じる部分があります。

抜粋実践テクニックは次の記事から始めます🙇‍♂️🙇‍♂️🙇‍♂️


引用
www.recode.net

SQLアンチパターンを読んだのだが、論理設計編のまとめがようやく終わる。

サボっていたらだいぶ日が空きました、DB論理設計編のまとめです。
結構散文化としてしまっていますが、よろしければ引き続きご覧くださいませ🐶

ようやく完結、DB論理設計編。

1. ジェイウォーク(信号無視)
2. ナイーブツリー(素朴な木)
3. IDリクワイアド
4. キーレスエントリ
5. EAV(エンティティアトリビュートバリュー)
6. ポリモーフィック関連


EAV(エンティティアトリビュートバリュー

select date_reported, COUNT(*)
from Bugs
group by date_reported;

これはバグテーブルからdate_reported毎に集計した数を出力しています。
ただし、日付のフォーマットが異なる為にこれを整備できないとしたらどのような方法を取るのがベターなのでしょうか?


可変属性をサポートしたい場合

オブジェクト指向設計になぞらえるならば、基底のモデルを拡張してサブクラスを立てることができます。
インスタンスは同じ基底クラスを持ち、サブタイプのインスタンスでもありますが、
あるサブタイプの持つ属性は基底型や他のサブタイプに存在するとは限りません。
このような例を1行のレコードに格納したい場合、自ずとカラムの定義が揺らぐことになります。

アンチパターンの実現例

属性テーブルの用意
1つのモデルに対し、属性(カラム)を内包したテーブルを作成します。
これが実現するパターンは、カラムの存在意味と名前がロジックに基づき固定されている場合であり、
無闇に列を増やしていけば管理がさらに複雑になることが想定されます。

投げるクエリが複雑になる

単純にデータのidと報告日が見たい!
select issue_id, date_reported from issues;
EAV設計の場合
select issue_id, attr_value as date_reported
from IssueAttributes
where attr_name = 'date_reported';

データ整合性の保ち方

date_reportedが設定されていない場合の運用が決定されていない
報告書がまとめられない…
date_reportedが設定されていないデータのためにクエリを投げて検査結果をメールで寄越す?適当な日付を入れてしまう?

SQLのデータ型扱いづらい問題

このattr_nameはStringで定義されているため、日付型でないデータも許容します。
計算結果を挿入するデータをBIGINTなどで定義してから、うっかりDECIMALやUNSINGEDなデータが投げられてしまったら、
このテーブルはデータ更新を拒否するかもしれません。

参照整合性を強制できない

薄々感づくところもあるかもしれませんが、attr_nameが外部キーであった場合に、
他のテーブルでも同じ型でこのデータを参照する必要があり、かつこのデータに一致することが強制されます。
attr_dateが文字列型であるのに、外のテーブルからTIMESTAMP型としてこれを参照することは出来ません。

(データを参照するためにjoinの手間がかかる…)

ソリューション

サブタイプのモデリングを行う

シングルテーブル継承(Single Table Inheritance)
 基底クラスと関連データを一つのテーブルにまとめ、タイプでサブクラス識別できるように設計する。
 タイプ毎に使用するカラムを定める。Ruby on RailsActiveRecordがこの設計を採用している。

基底クラスとそのサブクラスをSTIに則って設計する図

具象テーブル継承(Concrete Table Inheritance)

 サブタイプ毎にテーブルを設計する。サブタイプを判別するカラムを入れずに設計できる。
 基底型に設定されているデータかの判別がしづらく、共通部に新たなカラムを加えたい場合に共通テーブルの変更が必要になる。
 サブタイプに関わらずデータを取得したい場合、UNION句の使用によりビューを作成することで実現できる。

クラステーブル継承(Class Table Inheritance)

 オブジェクト指向に則り、一つのテーブルを一つのクラスとみなして設計する。
 サブクラスは基底クラスのidを参照し、外部キーで関連性が保たれる。
 基底クラスからサブクラスへは1対1のリレーションが強制される。

半構造化データ

 頻繁に新しい属性が追加され、基底クラスの定義が揺らぐ場合やサブクラスの数が多い場合はLargeObjectを追加し、 
 属性名と値をjsonxmlでtext型に格納する。
 拡張性が高く、各行に異なるサブタイプを格納することも可能。

 ※ SQLがLOBにアクセスできる術がない為、アプリ側でデータをselectした後に分解する必要がある。

CloudDataStoreでこんなの見たな。ここからインスピレーション受けたのかしら…


ポリモーフィック関連

サブクラスそれぞれに紐つくCommentsテーブルが欲しいけれども、どう実装しよう?

同じ基底クラスを持つBugs, FeatureRequestsですが、新たなCommentsテーブルの実装にはひとひねりが必要そうです。
同じ外部キーを通して関連性を持つことはできない為、ポリモーフィック関連(Polymorphic Associations)というテクニックを使います。
プロミスキャス・アソシエーションという別名は、複数のテーブルを参照できる為に無差別な関連という意味で使用されます。


ポリモーフィック関連の定義

Commentsテーブルには、サブクラスを判別するissue_typeが定義されます。
その他の外部キーはaccounts_id一種のみです。
ポリモーフィック関連の特徴は、参照整合性制約を定義できない部分にあり、
言い換えればissue_typeに定義されている種類も、親のissue_idも、本当に親テーブルで定義されているものかはわからないのです。

-- データを抜くときの注意

issue_idはBugs, FeatureRequestsの両テーブルに同じ値の主キーを持つ可能性を孕んでいます。
正確に紐つくデータを抜き取るには、issue_typeも同時に使用することが重要です。
両方のテーブルを参照したい場合は外部結合を使います。


まとめてみてどうだったか

事例まで挙げるとひたすらに長い。読者が疲れない程度に情報を分割することも重要。
この辺は自分の知識不足もあるので、SQL意外で置き換えられるように知識を反復して吸収することも大事。
 -> 👿👿👿

発行するSQLはぼやっとしている
 -> SQLのバリエが少ない。HavingとかSQLの関数についてもっと知れるとアプリ側の実装のバリエも広がりそう。

効率的にテーブルを舐める設計がうまくいかなかった
 -> 設計(経験)あるのみなんじゃないか

これらが実際にSQLを書きながらなんとなくでも前に進めた実感はある
 -> ◎

SQLアンチパターンを読んだので、少しずつまとめてみる。

こんばんは!
最近、我が家の豆苗のテンションが低めです

しなだれる豆苗

▼ 『寒いんじゃ…』

私の食料用に育成しているこの人ですが、日陰+常に気温は10℃前後という過酷な環境でも育ってくれています。
もともと温室育ちなはずなのに… 私もどんな辛いプロジェクトに放り込まれても、根を張って耐え抜く強さを持ち続けていたいです笑


さて!寒さにめげずにアンチパターンの続きをまとめていきます!

前回はSQLアンチパターンを読んで、論理設計編をまとめるという内容で展開しました。
残りが消化できていないので、こちらも見ていきましょう🐕


1. ジェイウォーク(信号無視)

2. ナイーブツリー(素朴な木)

3. IDリクワイアド
4. キーレスエントリ

5. EAV(エンティティアトリビュートバリュー)

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

今回は太字になっているIDリクワイアド, キーレスエントリについて振り返りをしていきます!


IDリクワイアド

前置き長いので、内容は本旨からどうぞ。

私は最近までRuby on Railsというフレームワークを使う現場で仕事をしていました。
RailsはORマッパーを採用していて、テーブルのデータをひとつのモデルとみなし、必要なデータの設計を高速に実施できます。
SQLで抽出したデータはActiveRecord::Relationというクラスでサポートされており、データの整形にキャスト(型変換)を意識せず素早く使うことができます。
経験や学びが浅く、エンジニアとして自信を無くしかけていた私にとって、このフレームワークの存在は本当にありがたかったです。昔では怖くて触れることができなかったロジックで色んな機能を実装することが出来、人の役に立つことが出来たので…。


その前はお客さん先でJavaOracleを使ってDB周りのことをしていました。
A5M2というツールで眺めた証券系システムを成すテーブル群はゆうに300を超えていて、当時1:nもよくわからなかった私は衝撃を受けました。

研修で作ったアプリについても、「テーブルなんて実行する回数少なければひとつだけあればいいじゃない」みたいなぬるい感想を持った素人にとっては、あの光景はなかなかに破壊力があるものです。マジで過呼吸になるかと思った。
あ、A5M2SQLパーな私にとって強力な調査の相棒となったので、WindowsOS使っている人に広めたい気持ちがあります。

マーケティングの人がSQLと仲良くなるきっかけとして。開発者と仲良くなるきっかけとして。
こちらもまた使ってみてまとめ記事あげたいなあ。

本旨

こちらがIDリクワイアドの内容の本旨になります。


私も研修の際に覚えたことですが、
各テーブルにおわすidはprimary key(主キー)として、そのデータが一意であることを示し、かつ他のテーブルのforeign key(外部キー)から参照され、テーブルの関連性を持つことが出来ます。

しかし、一意である=ダブらないといっても、本当にダブらないのかはちょっと疑問なところではあります。
下記は記事の詳細データを扱うテーブルです。

id reference_url article_id tag_id
1 423429 2 3
2 423429 2 3
3 524520 3 6

一度article_id=2の記事を消したものの、関連テーブルのデータは消えていませんでした。
このテーブルから記事に紐つけられている引用ページの数を知りたくて、以下のSQLを発行します。

select * article_image_mapping_id, count(*) as uniq_image_id from article_detail where tag_id = 3

すると、idが1と2のreference_urlが取れました。
idは一意であるものの、挿入されるデータは別物とは限りません。
この場合はid以外のカラムにユニーク制約(一意制約)をかけるべきだったのですが、ここで各テーブルのidは必要なのか?という疑問が浮かび上がります。

id列があって困るパターン

①単純に紛らわしい
articleテーブルに同名のid列があったらどうでしょう。
同じidのユニークな列が出来た際に、上のような出来事は避けられるものの、idでもarticle_idでもレコードは取れてしまいます。


②重複行を許可してしまう
上の例ではレコードを挿入する際に、テーブルのidはserial primary keyを許可されているものの、
他の2カラムについては重複を許可してしまっています。
新たに同じtag_id, article_idでレコードが更新されてしまうのです。


③キーの意味がプログラムにとってわかりづらい

select b.id, a.id
from tags b
inner join articles a on b.assign_to = a.id
where b.name = 'coffee';

上のクエリは記事とタグのテーブルを結合し、コーヒーのタグがついた記事を抽出するものです。
データ名で内容を識別するとき、article_id, tag_idのように識別されない為に、列で内容が上書きされてしまうケースもあるようです。
Railsを使用していれば起こりにくい問題ですが、一から設計した時には起こりうる問題です。


④using句の使いどころが減ってしまう
もしもidカラムにテーブル名を表す情報が付加されていた場合、以下のクエリを簡単に書き直すことが出来ます。

select * from tags as a
inner join articles as b
on a.id = b.tag_id;

select * from tags as a
inner join articles as using (tag_id);

using句便利!idカラムを実装するよりも、tablename_idの命名規則に沿うと楽そうですね♪


参考

idリクワイアドを適用してもいい場合

行で重複しても良い
クエリで個別の行を参照しない
外部キー参照をサポートしない

擬似キーをサポートしているデータベース
DB種類 機能名 参考url
mysql AUTO_INCREMENT MySQL :: MySQL 5.6 リファレンスマニュアル :: 3.6.9 AUTO_INCREMENT の使用
mysql, postgres SERIAL https://www.postgresql.jp/document/7.3/user/datatype.html
sqlite ROWID Rowid Tables
Oracle SEQUENCE CREATE SEQUENCE


mysql_insert_id()は最後に生成されたAUTO_INCREMENTの値を取得してくれるそう。
SQLにも関数がある、これ応用できたら便利やろな…。


キーレスエントリ(外部キー嫌い)

冒頭のケースで語られるのは、外部キー参照のないDBで設計をした為に、データ重複や孤児(親データが削除された後も置き去りとなった関連データ)の扱いについてです。
不整合なデータを定期的に検出し、結果をメールで通知させる設計を追加したものの、テーブルの数とデータそのものが増えてきてクエリの実行に時間がかかるようになってしまったのでした。
本来であれば、ユーザーが無効なデータを入力した時にエラーを返せる設計にすればよかったとありますが、
では無効なデータとはどんなものを指すのでしょうか?

外部キーを参照したくないパターン
  • テーブルの設計がしょっちゅう更新される
  • データの更新が外部参照と衝突する
  • 本テーブルのデータを更新する為に外部テーブルも更新しなければならない-
  • クエリを調べるのに時間を割きたくない(笑)
  • 外部キー参照を実装していないDBを使用している(本文ではMyIsam)
完璧なクエリを発行してデータを保持していかなければならない

1:nのarticle, tagがあった場合に、tagを更新する際は親であるarticleが存在するかselectで確認する必要がある。
1:nのarticle, commentがあった場合に、articleを削除する際は子のcommentが存在していないか、selectで確認する必要がある。
(さらに、削除作業を行う際には新たなcommentが作成されているかもしれないことを念頭に置くこと。)

孤児データの検索が必要になる

select a.article_id, t.tag_name
from articles left outer join tags t
on a.tag_id = t.tag_id
where t.tag_id is null;

これを毎度発行して、定期的にデータの管理をする事を考えただけでも、気が遠くなりそうですよね…。

削除、更新の際は親のカラムを更新してから子のカラムを更新していかなければならないのです。。

ソリューション

外部キー制約を宣言しよう


複数テーブルの更新をサポートするカスケード更新

create table Bugs (
reported_by BIGINT UNSIGNED NOT NULL,
-- 正の数のみ, NULL許容しない

status VARCHAR(20) NOT NULL DEFAULT 'NEW',
-- デフォルト値はNEWで20文字までの制約あり, NULL許容しない

FOREIGN KEY (reported_by) REFERENCES Accounts(account_id)
ON UPDATE CASCADE
ON DELETE RESTIRCT,
-- カスケード処理を定義する, Bugsテーブルを参照しているaccount_idは削除時にエラーを返す

FOREIGN KEY (status) REFERENCES BugStatus(status)
ON UPDATE CASCADE
ON DELETE RESTIRCT
-- statusをNULLにした場合には、set defaultが作用してNEWが設定されるようになる 孤児の心配はない
);

テーブル作成時にカスケード更新を設定すれば、参照している子の行も更新が反映される…おったまげたぜ…

これで削除、更新前にselectを投げる必要がない
複数のテーブルの更新を気にして排他的ロックをかける必要がない
孤児を検索するスクリプトを書く必要がない

👏👏👏


ご参考

www.dbonline.jp
qiita.com



寒い日には美味しいラーメンが食べたい!

という事で、ぼっちラーメン散策して参りました👟
地元にあるこのラーメン屋さんですが、皿の上に乗っているものが全て美味しかったです。
特製煮干しラーメン(塩)
特製煮干しラーメンの塩を頼んできました。
スープつやっつやですよね!?一度口をつけたら飲み干すまで止まらないですよ…😈
体が欲しがる栄養スープって感じです!
体に優しいお味で、チャーシューの代わりに半ナマのローストビーフとチキンが乗っていました🐃🐓
優しい脂が、噛み締めるたびに口の中を満たします🤤
味玉はとろりとした半熟、海苔も噛み締めると濃いめの味が広がって、それでもスープを邪魔しない程度の主張。
麺は自家製みたいです。もくもくと噛み締めて、私は好きです。

特製とあってお値段少し張りますが、通常メニューのラーメンは880~からいただけるので、今度はこっちも食べにいきます!
家系よりもあっさりした方が好きな方は是非!


関連ランキング:ラーメン | 高円寺駅東高円寺駅新高円寺駅

http://tabelog.com/btb/0bbbdcd7680f03ca3988b4c586a62e53ea73e731111cc24a272de3e1bad7eba3/

DBの設計してきました

バックエンド(サーバサイド)エンジニアです!と名乗りだして半年が経ちました。
成長が亀の歩みなのですが、なかなか体験できなかったDB設計について実際にやってみよう!という
イベントがあったので、参加をしてきました🐶

参加背景

Railsでロジックを書き始めた頃、よく思うことがありました。
「このロジックはモデルに書くべきなの?
コントローラに書くべき?」
「スコープ(モデルからデータを引っこ抜くORマッパーが書いてあるメソッド)が欲しかったら、モデルに書くのかな
…あれ?似た処理がコントローラにある気がする(幻覚)」
といった具合に。

私は歴が浅いこともあり、言われるがままの要件を満たす実装をしがちでした。
cssを書いている時も感じていましたが、
「これ、同じ部品あるんじゃ?」「このバリデーション、viewにいちいち書くのか…」
と感じつつも、要件が投げられるからという理由でバックエンドのリファクタを実施できなかった後悔があります。


メモリの使用率などを計算に入れてコーディングしたためしはないですが、
false, nil以外は真というRubyの性質上、判定文で使用されなかった変数もメモリを取ったまま、処理が実行されるという事に気付きました。

データ抽出もORマッパーに頼りっぱなし、いまだに疎い部分はたくさんありますが、自分の書いたスコープがテーブルの中を何往復しているかという想像も出来ていませんでした。

気がつけばmackaelがアラートを出し、CPU負荷率の調査の為にログを眺めていてもSQLの意味がわからない…


上記に後悔ばかりが連ねてありますが、そもそもどこから手をつけるべきなのかが全く見えていませんでした。
そんなもやもやを抱えている時にSQLアンチパターンと出会い、Rubyの問題にしろ、Railsの問題にしろ、ちょっとずつ知ることから始める事にしました。

いらないものは実装しない

YAGNIの原則というものがあります。
私はプライマリキーはどのテーブルにもある!という謎の宗教観の人間だったのですが、
実はそうでなくて、これはアンチパターンの一種であるIDリクワイアドに見事にハマってしまっている状態だという事に気づかされました。
無意識にガーッと書いたER図の中にid, user_id...などが軒を連ね…
参照される必要のないカラムは'もしかしたら'プロダクトが成長した暁に、DBの容量を逼迫するかもしれません。
ついつい入れてしまった『参加者をカウントする数字を格納するカラム』もきっとアプリ側で解決する問題でしょう…


他の参加者の方の設計を見て考えさせられたのは、上手にテーブルを分割して、要件を実現するだけでなく、後々の運用までをうっすらとでも考えている見通しの意図でした。

DB設計はシンプルさが重要で、頼まれた要件を渡すように作りこむのかがDB・アプリ技術者の腕にかかっていて、それはシームレスに行われるものだと実感しました…。

それからどうする?

最近はサーバレスというものが流行っていますが、私はそれにしばらく着手せずに、上にあげた後悔と向き合うだろうと思います…
後本当はプリンシパルオブプログラミングもちゃんと読み込みたいです。
速読術が欲しいなあ…

SQLアンチパターンを読んだので、論理設計編を個人的なアンチパターンと共にまとめてみる。

論理設計の章ではデータの構成、関連付けなどをまとめています。

具体的にはDBのテーブル、列、関連性の設計があります。

物理設計の章は格納データタイプの決定後、テーブルやインデックスの定義、データ型の決定となります。具体的なDBの中身の決定ですね。

 

今回は論理設計の章についてまとめていきます。

個人的な所感が多く含まれますので、リアクションいただけると嬉しいです。

 

1. ジェイウォーク(信号無視)

2. ナイーブツリー(素朴な木)

3. IDリクワイアド

4. キーレスエントリ

5. EAV(エンティティアトリビュートバリュー)

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

 

 

1. ジェイウォーク(信号無視)

これ、まさしくこのアンチパターンを頭の中で考えたことがありました。

(適当な絵面が浮かばなかった…)

f:id:letterneginr:20181121163146j:plain

文字面で説明しておくと、Product(製品)とUser(ユーザー)が1: nの関係です。製品は複数のユーザーが使用しているので、連絡先を参照できるようにしたいという要件が投げられたとします。

 

今回のアンチパターンはカンマ区切りでaccount_idを列挙するカラムをProduct側に定義するというものです。

この場合だとカラムを1個定義するだけで終了なのですが、

如何せん運用中にどんなSQLを発行すればいいか想像つかないですよね…

後該当データがダブりそう。

事例ではユーザーから製品を参照したいときに正規表現が必要だと言っています。

account_id = 3のユーザーのidが登録されている製品と、その製品詳細が欲しい… となるとテーブルを結合してViewを作りたい気持ちなのですが、

ユーザーidで製品抜いて!該当の製品idでデータ抜いて!その作業だけでもややこしいSQLが発行されるのが想像に容易くないですよね…?(さらに最近アクティブな製品だけ抜きたい…Productのupdated_atでソートをかけたいなど、要件追加されたらちょっと面倒臭いなあ…

それが100行近くに肥えた暗黒クエリとして勉強会のスライドショーに晒されるのも遠くない未来でしょう。

 

* ソリューション

中間テーブルを作って相互参照が容易なものにする。

外部キーをカラムに持つテーブルを作るんですね。 

これだったらNULLの心配もないですし、更新・削除がお手軽になりそうです。

f:id:letterneginr:20181121165421j:plain

中間テーブルはRailsを現場で使い始めてから初めて知りました。

何でもかんでも適用していいという訳ではないと思いますが、命名が重要ですよね。

連絡先だけじゃなくて、関連性だけに注視するならProductUserMappingって名前でもいいのかなあと思います。

 

2. ナイーブツリー(素朴な木)

 

f:id:letterneginr:20181121175036j:plain

これもJavaのアプリケーション作成時に苦しみました。

記事に結びつくコメントのテーブル設計ですが、投稿された後にどんな運用が考えられるかというパターンを出しきれなかったのです。

id = 1のコメントに対する返信のコメントを書いて、返信文だけ消したいのに

親記事まで消してしまったり…

発行するクエリが悪い場合もあるんですけどね…💧

 

今回のお題では親記事に連なるコメントを取得したい場合。

親のidが動的に変わるので、親記事内でparentとして扱われているコメントを取得しなければいけないので、左外部結合をひたすら行わなければいけないようです。

 

<<アンチパターン>>

f:id:letterneginr:20181121175104j:plainLEFT OUTER JOIN …左軸に検索元のテーブル1の外部キーを提示し、抜きたい関連テーブルを右側に置くことでNULLを混入せずにデータが取れる。

 

* ソリューション1 - 経路列挙

 

該当データまでのパスをcomment_idで表現し、格納します。

上の図でいう『いってみたい』のコメントを抽出したい場合に、親記事のcomment_id = 1, 該当のcomment_id = 2となる為、1/2/というパスをしまっておきます。

隣にもう一列できた場合も1/5/のように表現できるので、元記事に連なるデータは、ワイルドカードを使って1/%のように取得できます。

 

* ソリューション2 - 入れ子集合

親記事を起点にして、今回は便宜上その距離をrightとleftで表します。

こんな深さの記事があったとしてf:id:letterneginr:20181121180012j:plain

親記事は1番上の高さにいるのでleftには1

データの起点から数えて12番目の距離にいるので、rightは12を代入します。

親記事から1階層下であれば左側からleftの値が1ずつ増えるのです。

f:id:letterneginr:20181122123250j:plain

 この起点となるデータ(親記事)からのパスを辿ることで、目的のデータが抜けるという手続きを想定しています。

画像の『こんな姉妹店が…』以下のデータを抜きたい場合は、該当階層のleftの最小値とrightの最大値を検索して抜く事ができます。

元記事に連なるデータはleftの最小値が2、rightの最大値は11までのデータを指定する事で取得が可能です。

 

* ソリューション3 - 閉包テーブル

各データまでのパスを格納するテーブルを作ります。

親記事は子孫のデータ分、自身を含めたパスデータを持ちます。

各子孫も、自分よりも下にデータを持っている場合にそのパスを格納します。

f:id:letterneginr:20181122132824j:plain

 

先祖 子孫 先祖 子孫 先祖 子孫
1 1 1 5 3 3
1 2 1 6 3 4
1 3 2 2 4 4
1 4 2 3 5 5

パスは記事の持っているcomment_idで表現します。

コメント元記事に連なるデータは、記事とパスのテーブルを結合し、先祖と子孫の最大距離を指定してクエリで抜く事ができます。

 

 

 

少し長くなったので、3~6は次記事以降にまとめます🐕 

Rubyのクラス、特異クラスの話。

本日も寒いですね。

突然甘くてほかほかなたい焼きが恋しくなって、四ツ谷わかばさんに並んでしまいました。

甘さ控えめ、あずき本来の味と塩気を生かした粒あん、尾までぎっしりあんこ + 注文を受けてから一丁焼きで生誕するたい焼きたち。

Ruby Goldの対策をしながら並んでいたら、なんとかあり着くまでに並び続ける事が出来ました。笑(平日の14時ごろ並んで、20分強でした。参考までに。)

 

個人的にはくりこ庵のカスタードも大好きです🐟

 

今日はクラスとモジュールと特異クラスの話をします。

だいたいオブジェクト指向の話を知っていて、Javaなんかを触っている人は聞くまでもない話かもしれません。ふんわりオブジェクト指向についてご存知の方が今回の記事の読者の対象です。

 

Rubyのクラスは定数であり、再代入が可能です。

従って、クラス自体の定義を再オープンしてメソッドの追加をする事が可能です。

 

他の言語とは違って単一継承する仕様ですが、moduleという部品を輸入したり、クラス内に展開する事でいろんな事ができるようになります。

 

クラス内にはinitializeというメソッドが存在し、クラスが評価される(初期化される)ときに呼ばれます。私はJavaでいうコンストラクタのように捉えています。

コンストラクタはattr_accessorというメソッドで定義する事ができ、インスタンス変数を使って表現することもできます。

 

Class Taiyaki
PRICE = 150

def nakami @nakami end def nakami=(str) @nakami = str end
end

このコードはTaiyakiクラスのnakamiを設定しています。

上からnakamiを保存しておくメソッド(ゲッターと呼びます)

nakamiを設定しているメソッド(この方はセッター)です。 

 

また名前空間という特性があり、クラスのネスト状態を定義する事が出来ます。

 

Class Taiyaki::ShiroiTaiyaki
PRICE = 200
end

 こいつはたい焼きの亜種の白いたい焼きです。

ぱっと見Taiyakiを継承しているかのように見えますが、違います。別物です。

こいつは便宜上Taiyaki内でネストして宣言され、定数PRICEが200で初期化されている他人です。親子関係にはありません。

 

 

Class Taiyaki
PRICE = 150

def nakami @nakami end def nakami=(str) @nakami = str end
end

# あっ、中身詰めるの忘れてた。

Class Taiyaki
PRICE = 150

 def nakami=(str)
  @nakami = "tsubuann"
 end
end

 これをクラスの再オープンと言います。

これからTaiyakiのnakamiはこのプログラムが動作している上では初期値が全てtsubuannです。

何が言いたいかというと、クラスを再オープンして定義を変更した場合には、基本的にはその設定が全体に適用されます。 

また、Nikumann < Manjyuのようにスーパークラスを継承している場合は、きちんと同じお名前で開いて中身を詰めてあげないとTypeErrorを起こしてしまうので気をつけましょう。

標準の中身の設定はあんこでいいけれど、クラスを操作せずにカスタード味も作ってみたいなあ…なんて思った人は、下で説明する特異クラスを使ってみてもいいかもしれませんね。

 

特異クラスのお話 

 

インスタンスについてですが、これはクラスを分身させ、汎化(出生所よりもちょっとキャラを薄くした感じ)したものたちのことです。

Rubyのインスタンスは限られたスコープの中でしか生存できません。インスタンス変数しかり。宣言された箇所から~endまでが生きれる範囲です。儚い。

さて、特異クラスですが、これは指定したインスタンスに対して適用されるクラスです。

クラスメソッド、クラス変数は一切噛みません。

 

def object_name.method_name

end

これでこっそりと邪道の白いたい焼きカスタード味を作成するんです。 へっへっへ。

 

Class Taiyaki
PRICE = 150

 def nakami=(str)
  @nakami = "tsubuann"
 end

def self.change_shape_to_white
@nakami = "custard"
p "白い#{self}、#{@nakami}味。"
end
end

taiyaki = Taiyaki.new
taiyaki.change_shape_to_white #=> 白いTaiyaki、custard味。"

無理矢理感はあるんですが、白いたい焼きができましたよっと。

あ、家族にお土産買っていきたいなあ。お母さんはあんこが苦手なので、

家族別に注文を分けておきましょー。

 

my_taiyaki = Taiyaki.new("zunda")

mother_taiyaki = Taiyaki.new

def mother_taiyaki.change_shape_to_white
@nakami = "custard"
p "白い#{self}、#{@nakami}味。"
end

father_taiyaki = Taiyaki.new

father_taiyaki.change_shape_to_white #=> NoMethodError

おぅ、間違ってお父さんのたい焼きを白いやつにしてしまいました。

この注文はお母さん用なので、お母さん以外を指定すると "白いたい焼き欲しがっとるお母さん存在せんやないけ" という苦情を申し立てられます。

 

ちょっとプログラムよりな話をすると、一時的にmother_taiyakiというインスタンス自体が特異クラス(親クラス)となっているので、change_shape_to_whiteはレシーバから本来の実行対象であるmother_taiyakiを探しに行くのですね。

しかし、father_taiyakiのsuper classはTaiyakiであるものの、Taiyakiの中にはmother_taiyakiは存在しないんです。なので、NoMethodErrorが返ってきます。

 

上の例で白いたい焼きの注文が成功しているのは、レシーバがselfだからであり、

クラス内で定義されているselfはクラス自身を指します。

 

でも…ベースのあんこにカスタードを投入したいなあ…なんていう邪道も邪道なたい焼きも下のように実現させることが可能です。

 

<<~Taiyakiクラスの中~>>

class << self
def half_anko_with_custard
@nakami << " half_custard"
end
end
end

<<~Taiyakiクラスの外側~>>

my_taiyaki = Taiyaki.new
p my_taiyaki.half_anko_with_custard #=> "tsubuann half_custard"

 はい。これであんことカスタードの魔融合を楽しみたい他の人も注文できるようになりました。

これはさっきのように特定のインスタンスではなくて、Taiyaki自体に注文を追加しています。Taiyakiクラス内のselfはTaiyaki自身なので、これからもインスタンスとして新メニューの注文は可能です!

しかし、Taiyakiクラスの本来の中身はankoのままなので、うっかりTaiyaki.half_anko_with_custardなんてクラスからの呼出をすると、職人に怒られてしまいます。

half_anko_with_custardはTaiyaki本来の味を乱してしまわぬよう、特異クラスに追加してあるからです。

 

このように、クラスの外側でインスタンスに対してメソッドを作ることができてしまうのはちょっと恐ろしいですね。

Taiyaki職人のあずかり知らぬところで、ピザ味のたい焼きや、あまつさえ米の詰まったたい焼きも登場してしまうかもしれない…

 

それを防ぐためにもたい焼きのカテゴリー毎にレシピブックを作っておいたり(クラス設計)、instance_methodsなどのメソッドたちを活用することは重要ですね。

 

 

Taiyakiのゲシュタルト崩壊

 

たい焼きもいいのですが、あったかくてしょっぱいものも食べたくなってきますね〜。

おでんなんてどうでしょうか?しっかり赤だしが染み込んだ大きな大根…

お酒が進むと尚いいですよね!

渋谷の居心地もコスパも良い居酒屋さんをご紹介しまーす🍢 

tabelog.com

土日来店時予約は必須です!半個室・カウンターがあって適度に薄暗く、賑やかですよ🐶

私は居酒屋に行くとポテサラをすぐに頼むポテサラ廃人なのですが、

こちらのポテサラはフライドオニオンやサラミが細かく投入されていて、歯ごたえが楽しいやつでした!

刺し盛りが2000円弱で5点くらいいけたはずですが、遅いと売り切れてしまうので行くなら早い方がいいですよー!私はハマチが食べたかった…