Lưu trữ một địa chỉ thanh toán Thực tiễn tốt nhất trong bảng đơn hàng


10

Ai đó có thể giúp tôi hiểu câu trả lời của người dùng này cho bảng Khách hàng không . Tôi thực sự muốn một phương pháp tốt để lưu trữ địa chỉ trong bảng đơn hàng.

Điều tôi đang tìm kiếm là làm thế nào tôi có thể thiết lập địa chỉ của mình để khi tôi chỉnh sửa chúng, đơn hàng không bị ảnh hưởng bởi thực tế là khách hàng cập nhật địa chỉ của mình hoặc chuyển địa điểm.

Vì nó đứng lược đồ của tôi trông tương tự như:

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

Câu trả lời:


16

Về mặt khái niệm, mặc dù trong môi trường kinh doanh của bạn, Thứ tựĐịa chỉ là những ý tưởng được liên kết chặt chẽ, chúng có hiệu lực với hai loại thực thể riêng biệt, mỗi loại có các thuộc tính (hoặc thuộc tính) riêng và các ràng buộc.

Do đó, như đã nêu trong các nhận xét, tôi đồng ý với @Erik và bạn nên tổ chức bố cục logic của khai báo cơ sở dữ liệu của mình trong số các yếu tố khác:

  • một bảng riêng biệt để giữ các mẩu thông tin Địa chỉ ;
  • một bảng để giữ lại các chi tiết cụ thể của khách hàng ;
  • một bảng để bao gồm các điểm dữ liệu thứ tự ; và
  • một bảng để chứa thông tin về mối liên hệ giữa (các) Khách hàngĐịa chỉ ;

như tôi sẽ làm gương dưới đây

Sơ đồ IDEF1X tiếp xúc

Một bức tranh đáng giá cả ngàn từ, vì vậy tôi đã tạo sơ đồ IDEF1X được hiển thị trong Hình 1 để minh họa một số khả năng được mở theo đề xuất của tôi:

Hình 1 - Khách hàng, đơn đặt hàng và địa chỉ Sơ đồ IDEF1X tiếp xúc

Khách hàng , địa chỉ và các hiệp hội của họ

Như đã chứng minh, tôi đã mô tả một liên kết với tỷ lệ số lượng nhiều-nhiều (M: N) giữa các loại thực thể Khách hàng aĐịa chỉ ; Cách tiếp cận này sẽ mang lại sự linh hoạt trong tương lai bởi vì, như bạn biết, Khách hàng có thể giữ nhiều Địa chỉ theo thời gian hoặc thậm chí đồng thời và cùng một Địa chỉ có thể được chia sẻ bởi nhiều Khách hàng .

Một đặc biệt Địa chỉ có thể được sử dụng theo nhiều cách bởi một-nhiều (1: M) Khách hàng ; ví dụ: nó có thể được định nghĩa là Vật lý và / hoặc có thể được đặt cho Giao hàng và / hoặc cho Thanh toán . Có lẽ, cùng một địa chỉ Địa chỉ có thể phục vụ từng mục đích đã nói ở trên cùng một lúc hoặc có thể bao gồm hai mục đích sử dụng trong khi một địa chỉ khác nhau xuất hiện trong các mục đích còn lại.

a Trong một số môi trường kinh doanh, Khách hàng có thể là Cá nhân hoặc Tổ chức (tình huống ngụ ý một sự sắp xếp hơi khác biệt, như chi tiết trong câu trả lời này về cấu trúc siêu kiểu phụ) nhưng với mục tiêu cung cấp một ví dụ đơn giản, tôi đã quyết định không bao gồm khả năng đó ở đây. Trong trường hợp bạn cần bao gồm tình huống đó trong cơ sở dữ liệu của mình, bài đăng của liên kết trước đó hiển thị phương pháp để giải quyết yêu cầu đã nói.

Đặt hàng , địa chỉ , vai trò khách hàngđịa chỉ

Thông thường, một Đơn hàng chỉ yêu cầu hai loại Địa chỉ , một cho Giao hàng và một cho Thanh toán . Theo cách này, cùng một thể hiện Địa chỉ có thể điền vào cả hai Vai trò cho một Đơn hàng riêng lẻ , nhưng mỗi Vai trò được hình dung bởi thuộc tính tương ứng, tức là ShippingAddressId hoặc BillingAddressId .

Đơn hàng được kết nối với Địa chỉ thông qua loại thực thể liên kết CustomerAddress với sự trợ giúp của hai khóa NGOẠI TỆ đa tài sản, nghĩa là,

  • (Số khách hàng , ShippingAddressId ) và (Số khách hàng , BillingAddressId ),

cả hai đều trỏ đến KEY PRIMARY đa tài sản của khách hàng được hiển thị dưới dạng

  • ( CustomerNumber , AddressID )

Sự giúp đỡ thể hiện một quy tắc kinh doanh quy định rằng (a) một thể hiện Đơn hàng phải được liên kết riêng với (b) Địa chỉ xuất hiện trước đây được liên kết với Khách hàng cụ thể đã thực hiện Đơn hàng đó và không bao giờ với (c) một Khách hàng ngẫu nhiên - Địa chỉ liên quan .

Lịch sử cho (1) Địa chỉ và cho (2) hiệp hội Khách hàng Địa chỉ

Nếu bạn muốn cung cấp khả năng sửa đổi các mẩu thông tin Địa chỉ , thì bạn phải theo dõi tất cả các thay đổi dữ liệu. Theo cách này, tôi mô tả Địa chỉ như một “có thể kiểm tra” loại thực thể duy trì riêng của mình AddressHistory .

Vì bản chất của một kết nối giữa một Khách hàngĐịa chỉ cũng có thể chịu một hoặc nhiều sửa đổi, tôi cũng đã mô tả khả năng xử lý của một hiệp hội như một đối tượng có thể kiểm tra được bởi một loại hình thực thể của khách hàng .

Về mặt này, các yếu tố khác nhau xử lý trong Q & A không. 1hỏi đáp không. 2 , Wapboth về việc kích hoạt các khả năng tạm thời trong cơ sở dữ liệu.

Bố cục logic SQL-DDL minh họa

Do đó, về mặt sơ đồ được hiển thị và giải thích ở trên, tôi đã khai báo cách sắp xếp mức logic sau (bạn có thể điều chỉnh để đáp ứng chính xác nhu cầu của mình):

-- 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)
);

Nếu bạn muốn xem, tôi đã thử nó trong db <> fiddle này chạy trên SQL Server 2017.

Các History bảng

Đoạn trích sau từ câu hỏi của bạn là rất quan trọng:

Điều tôi đang tìm kiếm là làm thế nào tôi có thể thiết lập địa chỉ của mình để khi tôi chỉnh sửa chúng, đơn hàng không bị ảnh hưởng bởi thực tế là khách hàng cập nhật địa chỉ của mình hoặc chuyển địa điểm.

Các bảng AddressHistoryCustomerAddressHistorytrợ giúp trong việc đảm bảo Đơn hàng không bị ảnh hưởng bởi Địa chỉ thay đổi , vì tất cả các hàng trước đó của LÊN nên được giữ lại trong Historybảng tương ứng và có thể được truy vấn khi cần thiết. Các hoạt động CẬP NHẬT và XÓA trên hai bảng này nên bị cấm (cố gắng thay đổi lịch sử thậm chí có thể có ý nghĩa pháp lý tiêu cực).

Các Interval bao trùm giữa các giá trị kèm theo trong AddressHistory.CreatedDateTimeAddressHistory.AuditedDateTimeđại diện cho toàn bộ giai đoạn trong đó một “quá khứ” nhất địnhAddress hàng được coi là “có mặt”, “hiện tại” hoặc “hiệu quả”. Cân nhắc tương tự áp dụng cho các CustomerAddressHistoryhàng.

Cột CustomerAddress.IsActiveBIT (boolean) có nghĩa là chỉ ra liệu một Addresshàng nhất định có phải là có thể sử dụng được bởi một Customerhàng hay không; ví dụ: nếu được đặt thành 'sai', nó sẽ truyền đạt thực tế rằng Khách hàng sẽ không sử dụng Địa chỉ đó nữa và do đó không thể sử dụng cho Đơn hàng mới .


Lưu ý : Mặt khác, tôi đã thấy một số hệ thống trong đó mỗi lần Đơn hàng mới được kích hoạt, thông tin Địa chỉ phải được nhập (một số lần lặp lại) và (các) Địa chỉ được sử dụng cho Đơn hàng trước đây không bao giờ bị xóa (do đó các đơn đặt hàng không bị ảnh hưởng bởi Địa chỉ thay đổi).

Quá trình hành động này có thể quyết định liên quan đến khối lượng dư thừa đáng kể, nhưng có khả năng là việc phụ thuộc vào các yêu cầu thông tin chính xác của lĩnh vực kinh doanh của bạn có thể hoạt động, vì vậy bạn cũng có thể đánh giá ưu và nhược điểm của nó.


Phục hồi dữ liệu

Phiên bản hiện tại, hiện tại, phiên bản hiện tại và hiệu quả của một sự xuất hiện của Địa chỉ phải được chứa dưới dạng một hàng trong Addressbảng, nhưng CHỌN các trạng thái trước đó của một địa chỉ TỪ AddressHistory(hoặc từCustomerAddressHistory ) là dễ dàng và có thể là một bài tập thú vị để nâng cao kỹ năng mã hóa SQL của bạn.

Đối với một trong những tình huống mà bạn đã đề cập trong các nhận xét, nếu bạn muốn truy xuất phiên bản thứ hai đến phiên bản cuối cùng của một Addresshàng riêng lẻ TỪ AddressHistory, bạn phải tính đến MAX(AddressHistory.AuditedDateTime)AddressHistory.AddressId phù hợp với đặc biệt Address.AddressIdgiá trị trong tầm tay.

Về vấn đề này, ít nhất là khi xây dựng cơ sở dữ liệu quan hệ , rất thuận tiện để xác định sơ đồ khái niệm tương ứng (dựa trên các quy tắc kinh doanh áp dụng ) và sau đó tuyên bố sắp xếp DDL hợp lý tiếp theo . Khi bạn có được các phiên bản ổn định và đáng tin cậy của các yếu tố cơ bản này (tất nhiên, có thể phát triển theo thời gian), đã đến lúc phân tích và xác định các cách tốt nhất để thao tác (thông qua các hoạt động INSERT, UPDATE, DELETE và SELECT hoặc kết hợp chúng) liên quan đến dữ liệu.

Nhận thức, quan điểm và hỗ trợ (các) chương trình ứng dụng của người dùng cuối

Rõ ràng, ở mức độ trừu tượng bên ngoài , thông tin Địa chỉ được người dùng cuối coi là một phần của Đơn hàng và không có gì sai với điều đó, nhưng điều đó không có nghĩa là các nhà lập mô hình phải thiết kế các phần quan trọng của cơ sở dữ liệu trong câu hỏi như thế. Vào thời điểm này, nếu có nhu cầu, ví dụ, in Đơn hàng đầy đủ (rất khả thi), bạn có thể sao chép lại theo yêu cầu với sự trợ giúp của một vài toán tử THAM GIA và các mệnh đề WHERE (xem xét thời hạn hiệu lực liên quan , v.v.) có thể cố định trong lượt xem để tiêu thụ trong tương lai, gửi tập kết quả thích hợp đến (các) chương trình ứng dụng liên quan, đến lượt nó, có thể nâng cao định dạng của nó khi cần thiết.

Tất nhiên, (các) chương trình ứng dụng cũng sẽ rất hữu ích khi Đơn hàng đang được thực hiện; ví dụ: cửa sổ ứng dụng dành cho máy tính để bàn / thiết bị di động hoặc trang web có thể:

  • chỉ hiển thị (các) Địa chỉKhách hàng có liên quan đã thiết lập là có thể sử dụng được (thông qua CustomerAddress.IsActive);
  • liệt kê tất cả các Địa chỉKhách hàng đã kích hoạt cho dịch vụ thanh toán (thông qua CustomerAddress.IsBilling); và
  • nhóm tất cả các Địa chỉKhách hàng đã xác định cho dịch vụ vận chuyển (thông qua CustomerAddress.IsShipping);

tạo điều kiện theo cách này tất cả các quy trình liên quan tại GUI (nghĩa là mức độ trừu tượng bên ngoài của một hệ thống máy tính).


Cách đọc được đề nghị

Bạn đã yêu cầu (hiện đã xóa các bình luận) một số gợi ý về tài liệu cơ sở dữ liệu âm thanh; do đó, đối với tài liệu lý thuyết , tôi khuyên bạn nên đọc tất cả các tác phẩm được viết bởi Tiến sĩ EF Codd , người nhận Giải thưởng Turing và, tất nhiên, người khởi tạo duy nhất của mô hình dữ liệu quan hệ (có thể phù hợp hơn bao giờ hết). Danh sách này bao gồm một số bài báo và giấy tờ có ảnh hưởng rất lớn của ông.

Chính xác, hai tác phẩm quan trọng không có trong danh sách nói trên là Bài giảng Giải thưởng ACM Turing của ông có tên Cơ sở dữ liệu quan hệ: Một nền tảng thực tiễn cho năng suất , từ năm 1981, và cuốn sách của ông có tên là Mô hình quan hệ để quản lý cơ sở dữ liệu: Phiên bản 2 , đã được xuất bản vào năm 1990.

Về mặt thiết kế khái niệm , Định nghĩa tích hợp cho mô hình hóa thông tin (IDEF1X) là một kỹ thuật được khuyến nghị nghiêm túc, được định nghĩa là một tiêu chuẩn vào tháng 12 năm 1993 bởi Viện Tiêu chuẩn và Công nghệ Quốc gia Hoa Kỳ (NIST).


1
Xin lỗi, tôi biết bài đăng cũ hơn, nhưng tại sao bạn lại tham khảo (ví dụ: TÀI LIỆU THAM KHẢO (Địa chỉId)) trong MyOrder? Tại sao không phải là Khách hàng?
Shadrix

1
Không phải lo lắng, và nắm bắt tốt đẹp: Như một vấn đề của thực tế, cả hai MyOrder.ShippingAddressIdMyOrder.BillingAddressIdphải làm cho một tham chiếu đến CustomerAddress.AddressId(và không phải Address.AddressId); bằng cách này, người ta đảm bảo rằng một Đơn hàng có thể được liên kết độc quyền với (các) Địa chỉ được kết nối trước đó với Khách hàng đã thực hiện Đơn hàng đó . Sơ đồ cho thấy sự sắp xếp này, vì vậy DDL sẽ chính xác hơn. Cảm ơn đã yêu cầu làm rõ.
MDCCL

2
@Shadrix Tôi vừa chỉnh sửa bài, trong trường hợp bạn muốn xem.
MDCCL

@MDCCL Khi bạn nói không CẬP NHẬT và XÓA trên Historybảng, nó có giống với Addressbảng không? Điều gì sẽ xảy ra nếu Khách hàng đặt thứ gì đó và sau đó chỉ thay đổi mã bưu điện hoặc thành phố chỉ một trường. Chúng ta nên chèn địa chỉ hiện có vào Historyvà sau đó thực hiện chèn mới vào Addressbảng, phải không?
Mike Ross

1
OTOH, nếu Khách hàng muốn thay đổi một hoặc nhiều mẩu thông tin về một Địa chỉ nhất định , người ta phải đảm bảo rằng (a) Addresshàng tương ứng là hiện tại của Cho đến khi sửa đổi được XÁC NHẬN vào AddressHistorybảng và cả (b) ) Addresshàng trong câu hỏi là UPDATEd với (các) giá trị mới. Sẽ thuận lợi khi thực hiện quy trình này như một đơn vị công việc duy nhất trong một giao dịch.
MDCCL

3

Câu trả lời này được tổng hợp từ ý kiến ​​cho câu hỏi.

Một giải pháp sẽ là sử dụng FK vào bảng địa chỉ trong bảng thứ tự. Điều đó sẽ cho phép bạn xem các địa chỉ đã được sử dụng cho đơn đặt hàng và tách địa chỉ khỏi địa chỉ hiện tại của Người dùng.

Để thực hiện công việc này, bạn sẽ phải chèn một địa chỉ mới và liên kết địa chỉ mới đó với bảng Người dùng. Điều này có nghĩa là các địa chỉ được ghi một lần và chỉnh sửa là một ảo ảnh cho người dùng cuối. Bạn có thể lưu trữ lịch sử của tất cả các địa chỉ mà người dùng được liên kết một cách hiệu quả bằng cách di chuyển liên kết từ bảng Người dùng sang bảng kết hợp có dấu thời gian. Điều đó sẽ cung cấp cho bạn lịch sử chỉnh sửa / địa chỉ và duy trì dữ liệu không thay đổi trong bảng Địa chỉ.

@MDCCL nêu:

[bạn nên] tổ chức cấu trúc cơ sở dữ liệu của mình có một bảng để lưu dữ liệu liên quan đến Đơn hàng và một bảng khác để giữ thông tin Địa chỉ. Và, vâng, bạn chắc chắn có thể có một bảng biểu thị mối quan hệ nhiều-nhiều giữa hai loại thực thể này. Nếu Người dùng có thể thay đổi các thuộc tính Địa chỉ của mình, thì bạn phải theo dõi các sửa đổi đó, do đó bạn nên kích hoạt tương ứng AddressHistory. Bài này có liên quan đến khía cạnh sau.

MDCCL cũng đã giới thiệu tổng quan về cách tìm địa chỉ hiện tại cho người dùng tại đây:

Để lấy phiên bản mới nhất của bảng Lịch sử mà bạn có, bạn phải tính đến MAX(AuditedDateTime)tương ứng AddressId. Bước đầu tiên là mô hình hóa / thiết kế các sắp xếp logic và khái niệm tốt nhất có thể của bạn, bước thứ hai là tìm ra các cách thích hợp để XÁC NHẬN, CẬP NHẬT, XÓA và CHỌN dữ liệu của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.