Ordersテーブルへの請求先住所のベストプラクティスの保存


10

誰かがCustomerLocationテーブルに対するこのユーザーの回答を理解するのを手伝ってくれませんか。注文テーブルに住所を保存するための優れた方法が本当に必要です。

私が探しているのは、住所を設定する方法です。そのため、住所を編集しても、顧客が住所を更新したり移転したりしても、注文は影響を受けません。

現状では、私のスキーマは次のようになります。

 Person           |EntityID|
 EntityAddress    |EntityID|AddressID|
 Address          |AddressID|AddressType|AddressLine1|AddressLine2|
 Order            |OrderID|BillingAddressID|

回答:


16

概念的に言えば、ビジネス環境ではOrderAddressは密接に関連しているアイデアですが、実際には2つの個別のエンティティタイプであり、それぞれに独自の適用可能なプロパティ(または属性)と制約があります。

したがって、前述のコメントで述べたように、@ Erikに同意します。データベースの論理レイアウトを編成して、他の要素を宣言する必要があります。

  • アドレス情報を保持する1つの個別のテーブル。
  • お客様固有の詳細を保持する1つのテーブル。
  • Orderデータポイントを囲む1つのテーブル。そして
  • Customer(s)Address(es)間の関連付けに関するファクトを含む1つのテーブル。

以下で例を示します。

解説IDEF1X図

写真は千の言葉に値するので、図1に示すIDEF1Xダイアグラムを作成して、私の提案によって開かれた可能性のいくつかを示します。

図1-顧客、注文、住所の説明IDEF1X図

顧客住所、およびそれらの関連付け

実例で示したように、エンティティタイプCustomer aAddressの間の多対多(M:N)カーディナリティ比との関連付けを描きました。ご存知のように、お客様は複数のアドレスを長期間または同時に保持でき、同じアドレスを複数のお客様が共有できるため、このアプローチは将来の柔軟性を提供します。

特定のアドレスは、1対多(1:M)のお客様がいくつかの方法で使用できます。たとえば、「物理」と定義したり、「配送」や「請求」に設定したりできます。おそらく、同じAddressインスタンスが前述の目的のそれぞれを同時に果たすことができます。または、異なるAddressオカレンスが残りの1つをカバーする一方で、2つの用途をカバーすることもできます。

a一部のビジネス環境では、お客様個人または組織(スーパータイプとサブタイプの構造に関するこの回答で詳細示されているように、わずかに異なる配置を意味する状況)のいずれかになりますが、簡単な例を提供することを目的として、ここにはその可能性を含めないでください。データベースでその状況をカバーする必要がある場合は、前のリンクの投稿に、上記の要件を解決する方法が示されています。

OrderAddressCustomerAddressAddress Roles

通常、注文に必要な住所は発送用と請求用の2種類のみです。このようにして、同じAddressインスタンスが個々のOrderの両方のロールを満たすことができますが、各ロールはそれぞれのプロパティ、つまりShippingAddressIdまたはBillingAddressIdによって示されます。

Orderは、2つのマルチプロパティFOREIGN KEY を使用して、CustomerAddress連想エンティティタイプを介してAddressに関連付けられます。

  • CustomerNumberShippingAddressId)、および(CustomerNumberBillingAddressId)、

どちらもCustomerAddressマルチプロパティPRIMARY KEYをポイントしています。

  • CustomerNumberAddressId

...規定ということは()というビジネスルールを表すのに役立ちます注文インスタンスが(B)で独占的にリンクしなければならないアドレスの発生は、以前に特定の関連付けられたカスタマー作られたその秩序、そして決してランダム非(C)との顧客 -関連住所

(1)住所および(2)CustomerAddress関連付けの履歴

アドレス情報を変更する可能性を提供する場合は、すべてのデータ変更を追跡する必要があります。このようにして、Addressを、独自のAddressHistoryを維持する「監査可能な」エンティティタイプとして表現しました

CustomerAddressの間の接続の性質も1つ以上の変更の影響を受ける可能性があるため、CustomerAddressHistoryエンティティタイプを使用して、このような関連付けを「監査可能な」関連付けとして扱う可能性も示しました。

この点で、Q&Aで扱われたさまざまな要因はありません。1Q&Aいいえ。2、— データベースで一時的な機能を有効にすることの両方について— は本当に関連しています。

例示的なSQL-DDL論理レイアウト

したがって、上で表示および説明した図の観点から、次の論理レベルの配置を宣言しました(正確にニーズに合わせて調整できます)。

-- You should determine which are the most fitting 
-- data types and sizes for all your table columns 
-- depending on your business context characteristics.

-- Also, you should make accurate tests to define the 
-- most convenient INDEX strategies based on the exact 
-- data manipulation tendencies of your business domain.

-- As one would expect, you are free to utilize 
-- your preferred (or required) naming conventions. 

CREATE TABLE Customer (
    CustomerNumber      INT      NOT NULL,
    SpecificAttribute   CHAR(30) NOT NULL,
    ParticularAttribute CHAR(30) NOT NULL,  
    CreatedDateTime     DATETIME NOT NULL,
    -- 
    CONSTRAINT Customer_PK PRIMARY KEY (CustomerNumber)
);

CREATE TABLE Address (
    AddressId           INT      NOT NULL,
    SpecificAttribute   CHAR(30) NOT NULL,
    ParticularAttribute CHAR(30) NOT NULL,  
    CreatedDateTime     DATETIME NOT NULL,  
    -- 
    CONSTRAINT Address_PK PRIMARY KEY (AddressId)
);

CREATE TABLE CustomerAddress (
    CustomerNumber  INT      NOT NULL,  
    AddressId       INT      NOT NULL,
    IsPhysical      BIT      NOT NULL,
    IsShipping      BIT      NOT NULL,  
    IsBilling       BIT      NOT NULL,
    IsActive        BIT      NOT NULL,
    CreatedDateTime DATETIME NOT NULL,  
    -- 
    CONSTRAINT CustomerAddress_PK           PRIMARY KEY (CustomerNumber, AddressId),
    CONSTRAINT CustomerAddressToCustomer_FK FOREIGN KEY (CustomerNumber)
        REFERENCES Customer (CustomerNumber),
    CONSTRAINT CustomerAddressToAddress_FK  FOREIGN KEY (AddressId)
        REFERENCES Address  (AddressId)  
);

CREATE TABLE MyOrder (
    CustomerNumber      INT      NOT NULL,  
    OrderNumber         INT      NOT NULL,
    ShippingAddressId   INT      NOT NULL,
    BillingAddressId    INT      NOT NULL,    
    SpecificAttribute   CHAR(30) NOT NULL,
    ParticularAttribute CHAR(30) NOT NULL,  
    OrderDate           DATE     NOT NULL,
    CreatedDateTime     DATETIME NOT NULL,  
    -- 
    CONSTRAINT Order_PK                  PRIMARY KEY (CustomerNumber, OrderNumber),
    CONSTRAINT OrderToCustomer_FK        FOREIGN KEY (CustomerNumber)
        REFERENCES Customer        (CustomerNumber),
    CONSTRAINT OrderToShippingAddress_FK FOREIGN KEY (CustomerNumber, ShippingAddressId)
        REFERENCES CustomerAddress (CustomerNumber, AddressId),
    CONSTRAINT OrderToBillingAddress_FK  FOREIGN KEY (CustomerNumber, BillingAddressId)
        REFERENCES CustomerAddress (CustomerNumber, AddressId)          
);

CREATE TABLE AddressHistory (
    AddressId           INT      NOT NULL,
    AuditedDateTime     DATETIME NOT NULL,
    SpecificAttribute   CHAR(30) NOT NULL,
    ParticularAttribute CHAR(30) NOT NULL,  
    CreatedDateTime     DATETIME NOT NULL,  
    -- 
    CONSTRAINT AddressHistory_PK          PRIMARY KEY (AddressId, AuditedDateTime),
    CONSTRAINT AddressHistoryToAddress_FK FOREIGN KEY (AddressId)
        REFERENCES Address  (AddressId)    
);

CREATE TABLE CustomerAddressHistory (
    CustomerNumber  INT      NOT NULL,  
    AddressId       INT      NOT NULL,
    AuditedDateTime DATETIME NOT NULL,    
    IsPhysical      BIT      NOT NULL,
    IsShipping      BIT      NOT NULL,  
    IsBilling       BIT      NOT NULL,
    IsActive        BIT      NOT NULL,
    CreatedDateTime DATETIME NOT NULL,  
    -- 
    CONSTRAINT CustomerAddressHistory_PK                  PRIMARY KEY (CustomerNumber, AddressId, AuditedDateTime),
    CONSTRAINT CustomerAddressHistoryToCustomerAddress_FK FOREIGN KEY (CustomerNumber, AddressId)
        REFERENCES CustomerAddress (CustomerNumber, AddressId)
);

確認したい場合は、SQL Server 2017で実行されるこのdb <> fiddleでテストしました

Historyテーブル

次の質問からの抜粋は非常に重要です。

私が探しているのは、住所を設定する方法です。そのため、住所を編集しても、顧客が住所を更新したり移転したりしても、注文は影響を受けません。

することを確保する上でのテーブル援助注文は、に影響されないアドレスのすべての「前」行はそれぞれに保持する必要があるとして、変更テーブルと、必要なときに照会することができます。これらの2つのテーブルに対するUPDATEおよびDELETE操作は禁止する必要があります(履歴を変更しようとすると、法的にマイナスの影響が及ぶこともあります)。AddressHistoryCustomerAddressHistoryHistory

で囲まれた値の間に含まれる間隔は、特定の「過去」の行が「存在」、「現在」、または「有効」と見なされた期間全体AddressHistory.CreatedDateTimeAddressHistory.AuditedDateTime表します。同様の考慮事項が行に適用されます。AddressCustomerAddressHistory

CustomerAddress.IsActiveBIT(ブーリアン)カラムを特定するかどうかを指摘することを意図されているAddress行はによって「使用可能」であるCustomer行またはありません。たとえば、「false」に設定されている場合、顧客がそのアドレスを使用していないため、新しい注文に使用できないという事実を伝えます。


:一方、新しい注文が有効になるたびに住所情報を入力する必要があるシステムがいくつかあります(何度も繰り返して)、過去の注文に使用された住所は決して消去されません(したがって)注文はによって影響されないアドレス)の変更。

この一連の行動には、かなりの量の冗長性が明らかに伴う可能性がありますが、ビジネスドメインの正確な情報要件によっては機能する可能性があるため、その長所と短所も評価することをお勧めします。


データ検索

「現在」、「現在」または「有効な」バージョンアドレス発生の行として含まれなければならないAddressテーブルが、以前の「状態」を選択するアドレスを FROM AddressHistory(又はからCustomerAddressHistory)テーブルは簡単であり、それはよいですあなたのSQLコーディングスキルを強化するための興味深い演習になります。

コメントで言及した状況の1つに関して、個々のAddress行の「最後から2番目のバージョン」をそのAddressHistoryから取得する場合は、手元の特定の値と一致するMAX(AddressHistory.AuditedDateTime)とを考慮する必要があります。AddressHistory.AddressIdAddress.AddressId

この点で、少なくともリレーショナルデータベースを構築する場合は、最初に(該当するビジネスルールに基づいて)対応する概念スキーマを定義し、その後で後続の論理 DDL配置を宣言すると非常に便利です。これらの基本的な要素の安定した信頼できるバージョン(もちろん、時間とともに進化する可能性があります)を入手したら、操作(INSERT、UPDATE、DELETE、およびSELECT操作またはそれらの組み合わせを介して)を行うための最良の方法を分析および決定するときが来ました。データについて。

エンドユーザーの認識、見解、アプリケーションプログラムの支援

明らかに、抽象化の外部レベルでは、住所情報は(エンドユーザーによって)Orderの一部として認識されますが、これには何の問題もありませんが、モデル作成者がそのような問題のデータベース。この点で、たとえば「完全な」注文を印刷する(非常に実現可能)必要がある場合は、いくつかのJOIN演算子とWHERE句(有効期間を考慮して)を使用してオンデマンドで「再現」できます。など)将来の消費のためにビューで修正され、関連する結果セットが関連するアプリケーションプログラムに送信され、必要に応じてフォーマットが拡張されます。

もちろん、注文が発効しているときは、アプリケーションプログラムも非常に役立ちます。たとえば、デスクトップ/モバイルアプリウィンドウまたはウェブページでは次のことができます。

  • 関連するお客様が「使用可能」として(を介して)確立したアドレスのみを表示します。CustomerAddress.IsActive
  • お客様が(を介して)課金サービスを有効にしたすべてのアドレスを一緒にリストします。そしてCustomerAddress.IsBilling
  • お客様が配送サービス用に(を介して)定義したすべての住所をグループ化します。CustomerAddress.IsShipping

このようにして、GUIで関連するすべてのプロセス(つまり、コンピュータ化されたシステムの外部レベルの抽象化)を容易にします。


推奨読書

あなたは(今は削除されたコメントで)健全なデータベース文献についてのいくつかのポインタを要求しました。したがって、理論的な資料については、チューリング賞の受賞者であり、もちろん、データリレーショナルモデルの唯一の創始者であるEF Codd博士が執筆したすべての研究を読むことを強くお勧めします(おそらく、これまで以上に関連性が高まっています)。このリストには、彼の途方もなく影響力のある記事や論文のいくつかが含まれています。

前述のリストに含まれていない2つの重要な作品は、正確には、彼のACM Turing Award Lectureと題するRelational Database:A Practical Foundation for Productivity、1981年から出版されたThe Relational Model for Database Management:Version 2です。 1990年に。

上の概念設計のフロント、インフォメーションモデリングのための統合された定義(IDEF1X)は、米国の1993年12月における標準として定義された真剣に推奨技術である米国国立標準技術研究所(NIST)。


1
申し訳ありませんが、投稿が古いことを知っていますが、なぜMyOrderで参照しているのですか(例:REFERENCESアドレス(AddressId))?なぜCustomerAddressではないのですか?
Shadrix 2018年

1
心配ない、と素敵なキャッチ:実際のところ、両方MyOrder.ShippingAddressIdMyOrder.BillingAddressIdへの参照をしなければならないCustomerAddress.AddressId(としないようにAddress.AddressId)。このように1つの性を保証をすることをご注文は、排他的に関連付けることができますアドレス(複数可)以前に接続されているお客様と判断注文。図はこの配置を示しているため、DDLはより正確になります。説明をリクエストしていただきありがとうございます。
MDCCL 2018年

2
@Shadrixご覧になりたい場合に備えて、投稿を編集しました。
MDCCL 2018年

@MDCCL Historyテーブルに対してUPDATEおよびDELETEを実行しないと言ったとき、それはAddressテーブルに対して同じである必要がありますか?顧客が何かを注文し、郵便番号または都市のみを1つのフィールドだけ変更した場合はどうなるでしょうか。既存のアドレスをにHistory挿入してから、Addressテーブルに新しい挿入を行う必要がありますよね?
マイク・ロス

1
OTOH、お客様が特定のAddressに関する1つ以上の情報を変更する場合は、(a)Address変更が行われるまで「存在」していた対応する行がAddressHistoryテーブルに挿入され、さらに(b) )Address問題の行が新しい値で更新されます。このプロセスは、トランザクション内の単一の作業単位として実行すると便利です。
MDCCL

3

この回答は、コメントに対する質問から編集されました。

1つの解決策は、注文テーブルのアドレステーブルにFKを使用することです。これにより、注文に使用された住所が表示され、ユーザーの現在の住所から住所が分離されます。

この作業を行うには、新しいアドレスを挿入し、その新しいアドレスをユーザーテーブルにリンクする必要があります。つまり、アドレスは1回だけ書き込まれ、編集はエンドユーザーにとっては幻想です。関連付けをUserテーブルからタイムスタンプ付きの関連付けテーブルに移動することにより、ユーザーが関連付けられたすべてのアドレスの履歴を効果的に保存できます。これにより、編集/住所の履歴が得られ、住所テーブルに不変のデータが保持されます。

@MDCCLは述べました:

注文関連のデータを保持するための1つのテーブルと、住所情報を保持するための別のテーブルを持つデータベース構造を編成します。そして、はい、これら2つのエンティティタイプ間の多対多の関係を表すテーブルを確実に作成できます。ユーザーがアドレス属性を変更できる場合は、そのような変更を追跡する必要があるため、対応するを有効にする必要がありますAddressHistoryこの投稿は後者の側面に関連しています。

MDCCLは、ここでユーザーの現在のアドレスを見つける方法の概要も示しました。

お持ちの履歴テーブルの最新バージョンを取得するにMAX(AuditedDateTime)は、対応するのを考慮する必要がありますAddressId。最初のステップは、可能な限り最高の概念的および論理的な配置をモデル化/設計することです。次のステップは、データを挿入、更新、削除、および選択する適切な方法を見つけることです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.