Nếu ý định là xây dựng một cơ sở dữ liệu quan hệ , thì thực sự hữu ích trước tiên khi thực hiện (a) phân tích bối cảnh kinh doanh của mối quan tâm để đặt ra một lược đồ khái niệm về các loại thực thể , kiểm tra các thuộc tính và liên kết của chúng trước đó (b) suy nghĩ về các bảng, cột và các ràng buộc. Các phần tử tương ứng với mức logic logic . Theo tiến trình hành động này, việc nắm bắt ý nghĩa của miền doanh nghiệp với độ chính xác sẽ đơn giản hơn nhiều và sau đó phản ánh nó trong một thiết kế SQL-DDL thực tế, bị ràng buộc tốt.
Một trong nhiều lợi thế được cung cấp bởi mô hình quan hệ là nó cho phép quản lý dữ liệu theo cấu trúc tự nhiên của nó; do đó người ta phải tìm kiếm cấu trúc như vậy trước khi sử dụng các công cụ quan hệ để quản lý nó. Không có vấn đề gì nếu kịch bản có vấn đề liên quan đến một dự án cá nhân (như bạn đã chỉ ra qua các bình luận): bạn càng xác định nó thực tế hơn, bạn sẽ càng học được nhiều hơn từ sự phát triển của nó (nếu đó là mục đích của nỗ lực này). Tất nhiên, một dự án cá nhân thực tế có thể phát triển thành một dự án thương mại với những điều chỉnh tương đối nhỏ.
Quy tắc kinh doanh
Để trình bày một tiến trình đầu tiên mà bạn có thể muốn sử dụng làm tài liệu tham khảo, tôi đã xây dựng một số quy tắc kinh doanh ở cấp độ khái niệm nằm trong số những quy tắc quan trọng nhất và chúng được liệt kê như sau:
- Một người sở hữu tài khoản không có một hoặc nhiều
- Một người chủ yếu được phân biệt bởi Id của nó
- Một người được luân phiên phân biệt bởi anh / cô FirstName , LastName , ngày sinh và giới tính
- Một tổ chức sở hữu các tài khoản không có một hoặc nhiều
- Một tổ chức chủ yếu được phân biệt bởi Id của nó
- Một tổ chức được phân biệt xen kẽ bởi Tên của nó
- Một tổ chức bắt đầu hoạt động tại FoundingDate
- Một tài khoản là Giao trong zero-một-hoặc-nhiều Transfers
- Một tài khoản là nhận chuyển giao trong zero-một-hoặc-nhiều Transfers
- Một tài khoản chủ yếu được xác định bởi nó Số
- Một tài khoản được cấp tại một chính xác CreatedDate
- Trong một chuyển , các Bên Giao phải khác với những nhận chuyển giao
- Một người có thể đăng nhập thông qua UserProfile 0 hoặc 1
Kể từ khi các hiệp hội -Hoặc relationships- (1) giữa người và tài khoản và (2) giữa tổ chức và tài khoản là rất giống nhau, thực tế này cho thấy Người và tài khoản là tổ chức phân nhóm của Đảng (về cơ bản, hoặc là một cá nhân hoặc một nhóm cá nhân) , đó là lần lượt siêu thực thể của họ . Đây là một cấu trúc thông tin cổ điển phát sinh rất thường xuyên trong nhiều mô hình khái niệm thuộc nhiều loại khác nhau. Theo cách này, hai quy tắc mới có thể được khẳng định:
- Một PartyType phân loại zero-one-hoặc-nhiều Bên
- Một bên là một cá nhân hoặc một tổ chức
Và hai trong số các quy tắc kinh doanh trước đây có thể được hợp nhất thành một quy tắc duy nhất:
- Một Bên sở hữu Tài khoản không có một hoặc nhiều
Điều này cũng có thể được nêu theo quan điểm của loại thực thể Tài khoản :
- Một tài khoản được sở hữu bởi chính xác-one Đảng
Sơ đồ IDEF1X tiếp xúc
Do đó, tôi đã tạo ra một bình luận (giản thể) IDEF1X † sơ đồ tổng hợp các quy tắc xây dựng ở trên, và nó được hiển thị trong Hình 1 :
Đảng, Người và Tổ chức: Cấu trúc siêu kiểu phụ
Như đã trình bày, Person
và Organization
được mô tả như loại trừ lẫn nhau phân nhóm của Party
.
Các Party
siêu kiểu giữ một phân biệt (ví dụ PartyTypeCode
) và tất cả các thuộc tính (hoặc các thuộc tính) mà là chung cho các phân nhóm của mình, trong đó, đến lượt nó, có các tính chất áp dụng cho mỗi người trong số họ.
Tài khoản
Các Account
loại thực thể được kết nối trực tiếp với Party
, cung cấp một kết nối tiếp theo giữa (i) Account
và Person
, và giữa (ii) Account
và Organization
.
Vì có thể, trong thế giới thực, (a) một ngân hàng Account
không thể chuyển nhượng được, nghĩa là, ngân hàng không Owner
thể thay đổi và (b) Account
không thể bắt đầu hiện tại hoặc được kích hoạt mà không có Owner
, PRIMary KEY của loại thực thể này có thể bao gồm các thuộc tính PartyId
và AccountNumber
, vì vậy bạn nên phân tích kịch bản kỹ lưỡng hơn nữa để xác định điểm này với độ chính xác cao.
chuyển khoản
Mặt khác, Transfer
những món quà loại thực thể một PRIMARY KEY tổng hợp tạo thành từ ba tài sản, ví dụ TransferorAccountNumber
, TransfereeAccountNumber
( tên vai trò tôi được giao để phân biệt mỗi một trong hai Account
thuộc tính liên quan đến mỗi Transfer
chẳng hạn) và TransferDateTime
(mà kể exaxct tức thì khi Transfer
xảy ra là thực hiện).
Các yếu tố về AccountNumbers
Cũng cần lưu ý rằng, trong các hệ thống ngân hàng thực tế, định dạng của một AccountNumber
điểm dữ liệu thường phức tạp hơn một giá trị số nguyên đơn giản. Có nhiều cách sắp xếp định dạng khác nhau, ví dụ: cách sắp xếp tương ứng với Số tài khoản ngân hàng quốc tế (IBAN), được xác định theo tiêu chuẩn ISO 13616 . Rõ ràng khía cạnh này ngụ ý rằng phân tích khái niệm (1) và các định nghĩa logic sau (2) đòi hỏi một cách tiếp cận toàn diện hơn nhiều.
Các khai báo SQL-DDL logic minh họa
Sau đó, như một dẫn xuất từ phân tích trước đó, tôi đã tuyên bố một thiết kế logic trong đó
- mỗi bảng đại diện cho một loại thực thể,
- mỗi cột là viết tắt của một thuộc tính của loại thực thể tương ứng và
- nhiều ràng buộc được thiết lập (khai báo) để đảm bảo rằng các xác nhận dưới dạng hàng được giữ lại trong tất cả các bảng tuân thủ các quy tắc kinh doanh được xác định ở tầng khái niệm.
Tôi đã cung cấp các ghi chú dưới dạng các nhận xét giải thích một số tính năng mà tôi đánh giá cao đặc biệt quan trọng đối với cấu trúc nói trên, được hiển thị bên dưới:
-- You have to determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on the business context characteristics.
-- Also, you should make accurate tests to define the
-- most convenient physical implementation settings; e.g.,
-- a good INDEXing strategy based on query tendencies.
-- As one would expect, you are free to make use of
-- your preferred (or required) naming conventions.
CREATE TABLE PartyType (
PartyTypeCode CHAR(1) NOT NULL, -- This one is meant to contain the meaningful values 'P', for 'Person', and 'O' for 'Organization'.
Name CHAR(30) NOT NULL,
--
CONSTRAINT PartyType_PK PRIMARY KEY (PartyTypeCode)
);
CREATE TABLE Party ( -- Represents the supertype.
PartyId INT NOT NULL,
PartyTypeCode CHAR(1) NOT NULL, -- Denotes the subtype discriminator.
CreatedDateTime TIMESTAMP NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Party_PK PRIMARY KEY (PartyId),
CONSTRAINT PartyToPartyType_FK FOREIGN KEY (PartyTypeCode)
REFERENCES PartyType (PartyTypeCode)
);
CREATE TABLE Person ( -- Stands for a subtype.
PersonId INT NOT NULL, -- To be CONSTRAINed as PRIMARY KEY and FOREIGN KEY at the same time, enforcing an association cardinality of one-to-zero-or-one from Party to Person.
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
GenderCode CHAR(3) NOT NULL,
BirthDate DATE NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Person_PK PRIMARY KEY (PersonId),
CONSTRAINT Person_AK UNIQUE ( -- Composite ALTERNATE KEY.
FirstName,
LastName,
GenderCode,
BirthDate
),
CONSTRAINT PersonToParty_FK FOREIGN KEY (PersonId)
REFERENCES Party (PartyId)
);
CREATE TABLE Organization ( -- Represents the other subtype.
OrganizationId INT NOT NULL, -- To be CONSTRAINed as PRIMARY KEY and FOREIGN KEY simultaneously, enforcing a association cardinality of one-to-zero-or-one from Party to Organization.
Name CHAR(30) NOT NULL,
FoundingDate DATE NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Organization_PK PRIMARY KEY (OrganizationId),
CONSTRAINT Organization_AK UNIQUE (Name), -- ALTERNATE KEY.
CONSTRAINT OrganizationToParty_FK FOREIGN KEY (OrganizationId)
REFERENCES Party (PartyId)
);
CREATE TABLE UserProfile (
UserId INT NOT NULL, -- To be CONSTRAINed as PRIMARY KEY and FOREIGN KEY at the same time, enforcing an association cardinality of one-to-zero-or-one from Person to UserProfile.
UserName CHAR(30) NOT NULL,
CreatedDateTime TIMESTAMP NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
CONSTRAINT UserProfile_AK UNIQUE (Username),
CONSTRAINT UserProfileToPerson_FK FOREIGN KEY (UserId)
REFERENCES Person (PersonId)
);
CREATE TABLE Account (
AccountNumber INT NOT NULL,
OwnerPartyId INT NOT NULL, -- A role name assigned to PartyId in order to depict the meaning it carries in the context of an Account.
CreatedDateTime TIMESTAMP NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Account_PK PRIMARY KEY (AccountNumber),
CONSTRAINT AccountToParty_FK FOREIGN KEY (OwnerPartyId)
REFERENCES Party (PartyId)
);
CREATE TABLE Transfer (
TransferorAccountNumber INT NOT NULL, -- Role name assigned to AccountNumber.
TransfereeAccountNumber INT NOT NULL, -- Role name assigned to AccountNumber
TransferDateTime TIMESTAMP NOT NULL,
Amount INT NOT NULL, -- Retains the Amount in Cents, but there are other possibilities.
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Transfer_PK PRIMARY KEY (TransferorAccountNumber, TransfereeAccountNumber, TransferDateTime), -- Composite PRIMARY KEY.
CONSTRAINT TransferToTransferor_FK FOREIGN KEY (TransferorAccountNumber)
REFERENCES Account (AccountNumber),
CONSTRAINT TransferToTransferee_FK FOREIGN KEY (TransfereeAccountNumber)
REFERENCES Account (AccountNumber),
CONSTRAINT AccountsAreDistinct_CK CHECK (TransferorAccountNumber <> TransfereeAccountNumber),
CONSTRAINT AmountIsValid_CK CHECK (Amount > 0)
);
Như đã lưu ý, không cần giữ lại các dấu NULL mơ hồ và có vấn đề trong các cột của bất kỳ bảng cơ sở nào.
Nếu bạn muốn biết nếu một tài khoản tham gia vào một số chuyển được sở hữu bởi một tổ chức hoặc một người , bạn có thể lấy được những thông tin đó trong một câu lệnh SELECT đơn qua, ví dụ như, các Transfer.TrasnferorAccountNumber
, các Account.PartyId
, và các Party.PartyTypeCode
cột.
Để bạn có thể đảm bảo rằng một Bên có thể sở hữu tối đa một Tài khoản (như đã nêu trong các nhận xét), thì bạn nên sửa một ràng buộc KHÔNG GIỚI HẠN cho Account.PartyId
cột. Tuy nhiên, trong các tình huống trong thế giới thực, ví dụ: trong một ngân hàng, một Người có thể sở hữu các Tài khoản không có một hoặc nhiều , do đó tôi đánh giá cao rằng một hiệp hội một-không-hoặc-một-không có vẻ thực tế.
Như đã đề cập trước đây, cách tiếp cận được đề xuất trong câu trả lời này được cho là sẽ được sử dụng như một tài liệu tham khảo mà bạn có thể tự mình mở rộng và điều chỉnh. Đương nhiên, các phần mở rộng và thích ứng được thực hiện ở cấp độ khái niệm nên được phản ánh trong mô hình logic.
Tôi đã thử nghiệm khai báo cấu trúc này trong (i) db <> fiddle này và trong (ii) SQL Fiddle này , cả hai đều chạy trên PostgreQuery 9.6 (ban đầu bạn đã gắn thẻ của hệ thống quản lý cơ sở dữ liệu này).
Cân nhắc tính toàn vẹn và nhất quán đối với các bảng Đảng, Người và Tổ chức
Với cách bố trí mô tả ở trên, người ta phải đảm bảo rằng mỗi “siêu kiểu” hàng là bất cứ lúc nào bổ sung bởi “Loại” đối tác tương ứng của nó và, đến lượt nó, đảm bảo những gì đã nói “Loại” hàng là phù hợp với giá trị chứa trong các siêu kiểu “phân biệt " cột.
Sẽ rất tiện lợi và tao nhã khi thực thi các trường hợp như vậy một cách khai báo, nhưng thật không may, không có nền tảng SQL chính nào cung cấp các cơ chế phù hợp để làm như vậy (theo như tôi biết). Do đó, khá thuận tiện khi sử dụng GIAO DỊCH ACID để các điều kiện này luôn được đáp ứng một cách tự tin trong cơ sở dữ liệu.
Kịch bản tương tự
Trong trường hợp bạn quan tâm đến các lĩnh vực kinh doanh khác trong đó các cấu trúc siêu kiểu phụ xuất hiện, bạn có thể muốn xem câu trả lời của tôi về
Tài nguyên liên quan
- Các bài đăng Stack Overflow này bao gồm các điểm rất phù hợp liên quan đến kiểu dữ liệu của một cột chứa dữ liệu tiền tệ , như
Transfer.Amount
, trong PostgreQuery.
Chú thích
† Integration Definition Thông tin Modeling ( IDEF1X ) là một dữ liệu cao recommendable mô hình kỹ thuật mà đã được thành lập như là một tiêu chuẩn trong tháng 12 năm 1993 của Hoa Kỳ Viện Tiêu chuẩn và Công nghệ (NIST). Nó hoàn toàn dựa trên (a) một số tác phẩm lý thuyết ban đầu được tác giả bởi người khởi tạo mô hình quan hệ, tức là, Tiến sĩ EF Codd ; trên (b) quan điểm Thực thể-Mối quan hệ , được phát triển bởi Tiến sĩ PP Chen ; và cũng trên (c) Kỹ thuật thiết kế cơ sở dữ liệu logic, được tạo bởi Robert G. Brown.