私たちが構築しているソフトウェアには「お客様」がいます。顧客は個人または組織のいずれかです。
私は本当にこれに最適なスキーマを作成したいです。
私はこれらの考慮事項を持っています。
アプリを適切にスケーリングしたいので、スキーマはこの選択に合うはずです。
私は次のようなことを達成しようとしています。
SELECT * FROM Customers + a few joins.
1. Person | NULL | John Doe | Primary Organisation Contact | Primary Address
2. Organisation | Acme Ltd | Jane Doe | Primary Organisation Contact | Primary Address
上記を関連付けるための最適なスキーマを作成するにはどうすればよいですか?
大まかなビジュアルスキーマ階層をアタッチしました。そして、間違いなく私は間違いを犯しています。
個人か組織かに応じて、結婚した一連の結果を取得することは可能ですか?
個人および連絡先/住所を顧客に参加させるのは簡単ですが、組織の主要な連絡先/住所に参加するにはどうすればよいですか?
スパイダーダイアグラムのようなスキーマを実現する簡単な方法はありますか?
連絡先と会社でいくつかのアプリを実際に構築したことからのいくつかの洞察。
まず、アウトラインにいくつかのユースケースがありません。あなたが必ずしもカバーしているわけではない、私が長年にわたって遭遇したカラフルなものの中で:
第二に、あなたの概要は顧客に言及しているので、顧客は会社でも(肉体的)人物でもよいことに注意してください。ダイアグラムに基づいてその部分がカバーされているようですので、次に進みましょう。
次のステップが「注文」テーブルなどの会計に関連するものを追加することである場合、会計に関連するすべてが会社、連絡先、製品、価格などに関連付けられていることに注意してください時間。これらの詳細はあらゆる種類のカラフルな方法で進化する可能性があり、頻繁に起こる設計ミスは、完全な正規化された設計を作成し、必要に応じてテーブルを更新するだけであると想定することです。だめだ。税務署員から請求書を印刷するように依頼され、会社ABCが価格Qで会計されたときに、会社XYZが価格PであるとITが言った場合、あなたは非常にうんざりしています。また、同様の設計ミスにより変更された、年に1回のアーカイブされたアカウントとクローズされたアカウントから始めないでください。
第3に、正規化しすぎると発生する可能性があるUI/UXの問題に非常に注意してください。一般的なユーザーが手に持っているアプリ(Outlook:Outlook)のように機能しない場合、Joeが新しい仕事に就いたときに、XYZのすべての従業員の会社をABCに変更することから離れて、十分に訓練されていない秘書になります。私は実際にこれが起こるのを見ました、そしてそれはかなりの光景ではありませんでした。 (あなたが疑問に思っているかもしれないので、彼女は彼女の仕事を続けました。)
最後に、避けられない重複する会社、連絡先、その他の多くのカラフルなものをマージすることから始めないでください。これらを念頭に置き、スキーマがvery、veryであることを確認してくださいwillが発生するためです。
今...実際に...
個人的に、私はこの場合、正規化に実際に目を向けるようになりました。連絡先/会社は、学校で教えられたようにDB設計と美しく正規化されたデータが、リソースの浪費、過度に複雑なクエリ、および途方もなく複雑なUIの形で問題を引き起こすレシピの1つです。とにかく、それは私見ではありません。
まず、連絡先テーブルを作成し、氏名、必要に応じてdisplay_name、company_name、address、phone、cell_phone、email、email2、secretary_name、secretary_phoneなどのフィールドを入力します。Outlookで連絡先を作成するときに使用できる場合は、それはおそらくそこに属しています。
私は会社の表に言及しなかったことに気付くでしょう。これは、通常、連絡先テーブルと会社のテーブルの間の強い結びつきを望まないためです。必要な場合は1つ追加し、company_nameに加えてcopmany_idを追加します。ただし、削除セットのnullの外部キーにします。また、データベースレベルで、company_nameに非常に緩く関連付けられていることを確認してください。それをフロントエンドレベルで維持します。これにより、秘書が誤って複数の連絡先の会社を変更することを防ぎます。
物事を正気に保つ。リストに合理的に表示されるもの(つまり、select * from contact where ... limit 10)、クエリの対象、または表に頻繁にあるはずの情報。参加しません、nada。クエリ、結果の反復、完了。
本当ににゴミを追加したい場合、2つのオプションがあります。
1つは、extra_contact_detailsテーブルを作成することです。これは、EAVテーブル、company_name、address、phoneなどのフィールドの完全なロード、または正規化されたビットと断片のホグウォッシュにすることができます。どちらのオプションを選択しても、やりすぎないでください。どちらの場合も、追加の(潜在的に複雑な)クエリが作成され、UIを作成すると、厄介なプログラミングの問題が山積みになります。ここで絶対に重要なのは、この方法を使用する場合、常にOutlookで作業してきた秘書がそれを理解する必要があるということです。
もう1つは、実際に前もってお勧めしますが、連絡先テーブルに「extra_contact_details」というテキストフィールドを追加して、そのまま使用することです。正規化はまったくありません。 1組の主要な詳細。頻繁に使用される二次的な詳細。プレーンテキストとして余分なもの。秘書はそれを取得します。エンドユーザーはそれを手に入れます。できました。
最後に、特定の時点でデータのバージョンを保存する必要がある場合は、キーの詳細の値を必ず複製してください。読み取り:挿入したときとまったく同じように印刷するために必要なもの。
男、私はデータベースの本が最初の章でパーティーモデルをカバーすることを望みます。
時間にかなりの価値がある場合は、HayのEnterprise Model PatternsまたはSilverstonのData Model Resource Bookを読んでください。サファリで安く見つけることができます。
テーブル継承についても読んでください:
http://martinfowler.com/eaaCatalog/singleTableInheritance.html
Nullが気に入らない場合は、クラステーブルの継承を使用してください。
パーティーは人、組織、自動エージェントを表します:
Party
id
name
Individual : Party
Organization : Party
AutomatedAgent : Party
顧客は、パーティーが演じる役割です。たとえば、誰かがあなたのサプライヤーになる可能性があります。あなたは彼らを従業員として雇い、解雇すると、彼らはあなたの顧客になります。
あなたは彼らがあなたと一緒に販売注文をしたかどうかから彼らの「顧客」を引き出すか、PartyRoleを使って顧客として彼らを宣言することができます。
PartyRelationshipは、パーティー間の関係を表します。
PartyRelationship
fromParty
toParty
fromDate
toDate (nullable)
OrganizationContact : PartyRelationship
Employment : PartyRelationship
アドレスは、物理アドレスと仮想アドレスを表します。
Address
id
EmailAddress : Address
emailAddress
WebAddress : Address
url
TelephoneNumber : Address
telephoneNumber
MailingAddress : Address
suiteOrApartment
street
city
Zip
人々は多くの連絡方法を持っています:
ContactMethod
party
address
fromDate
toDate (nullable)
telephoneExtension (nullable)
Hibernateのようなツールを使用することを強くお勧めします。
パーティーデータモデルを見てください。これは要件を満たしているはずです。
このようなものはどうですか?
addresses
id unsigned int(P)
street1 varchar(50)
street2 varchar(50)
city_id unsigned int(F cities.id)
Zip varchar(6)
lat double
lon double
cities
id unsigned int(P)
state_id unsigned int(F states.id)
name varchar(50)
countries
id char(2)(P)
iso3 char(3)(U)
iso_num char(3)(U)
name varchar(45(U)
customers
id unsigned int(P)
parent_id unsigned int(F customers.id) (Allow NULL)
name varchar(50) // John Doe, ABC Company, etc.
customers_addresses
id unsigned int(P)
customer_id unsigned int(F customers.id)
address_id unsigned int(F addresses.id)
customers_email_addresses
id unsigned int(P)
customer_id unsigned int(F customers.id)
email_address_id unsigned int(F email_addresses.id)
customers_phone_numbers
id unsigned int(P)
customer_id unsigned int(F customers.id)
phone_number_id unsigned int(F phone_numbers.id)
email_addresses
id unsigned int(P)
address varchar(255)
phone_numbers
id unsigned int(P)
area_code char(3)
exchange char(3)
station char(4)
extension varchar(10)
states
id unsigned int(P)
country_id char(2)(F countries.id)
code char(2)
電話番号と郵便番号は北アメリカ諸国向けであり、他の国では変更する必要があります。
customers
テーブルのサンプルデータを次に示します。
+----+-----------+---------------+
| id | parent_id | name |
+----+-----------+---------------+
| 1 | NULL | ABC Company |
| 2 | NULL | XYZ Company |
| 3 | 1 | John Doe |
| 4 | 1 | Jane Doe |
| 5 | 2 | John Q Public |
| 6 | NULL | JKL Company |
+----+-----------+---------------+
組織には常にNULL
parent_idがあります。個人は、NULL
parent_id(組織に関連付けられていない場合)または非NULL
parent_id(組織に関連付けられている場合)を持つことができます。特定のニーズには他の変更が必要になる場合があります...
SQLの例:
SELECT * FROM customers c
LEFT JOIN customers_addresses ca ON c.id = ca.customer_id
LEFT JOIN customers_email_address ce ON c.id = ce.customer_id
LEFT JOIN customers_phone_numbers cp ON c.id = cp.customer_id
LEFT JOIN cities ON ca.city_id = cities.id
LEFT JOIN states ON cities.state_id = states.id
LEFT JOIN countries ON states.country_id = countries.id
WHERE c.id = 1