Mẫu thiết kế - Một trong nhiều bàn của phụ huynh


13

Tôi bắt gặp một tình huống trong cơ sở dữ liệu khá thường xuyên khi một bảng đã cho có thể FK đến một trong số các bảng cha khác nhau. Tôi đã thấy hai giải pháp cho vấn đề này, nhưng cả hai đều không thỏa mãn cá nhân. Tôi tò mò không biết những mẫu khác mà bạn đã thấy ngoài kia? Có cách nào tốt hơn để làm điều đó?

Một ví dụ giả định
Hãy nói rằng hệ thống của tôi có Alerts. Cảnh báo có thể được nhận cho nhiều đối tượng - Khách hàng, Tin tức và Sản phẩm. Một cảnh báo nhất định có thể dành cho một và chỉ một mục. Vì bất kỳ lý do gì, Khách hàng, Bài viết và Sản phẩm đều di chuyển nhanh (hoặc được bản địa hóa) để văn bản / dữ liệu cần thiết có thể được kéo vào Cảnh báo khi tạo Thông báo. Đưa ra thiết lập này, tôi đã thấy hai giải pháp.

Lưu ý: Dưới đây DDL dành cho SQL Server nhưng câu hỏi của tôi nên được áp dụng cho bất kỳ DBMS nào.

Giải pháp 1 - Nhiều khóa Fullable

Trong giải pháp này, bảng liên kết đến một trong nhiều bảng có nhiều Cột FK (vì đơn giản là DDL dưới đây không hiển thị việc tạo FK). TỐT - Trong giải pháp này, thật tuyệt khi tôi có khóa ngoại. Tính không tối ưu của FK giúp cho việc thêm dữ liệu chính xác trở nên thuận tiện và tương đối dễ dàng. Truy vấn BAD không tuyệt vời vì nó yêu cầu các câu lệnh N LEFT THAM GIA hoặc N UNION để có được dữ liệu liên quan. Trong SQL Server, cụ thể là LEFT THAM GIA ngăn chặn việc tạo chế độ xem được lập chỉ mục.

CREATE TABLE Product (
    ProductID    int identity(1,1) not null,
    CreateUTC    datetime2(7) not null,
     Name        varchar(100) not null
    CONSTRAINT   PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
    CustomerID  int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
     Name       varchar(100) not null
    CONSTRAINT  PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
    NewsID      int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    Name        varchar(100) not null
    CONSTRAINT  PK_News Primary Key CLUSTERED (NewsID)
)

CREATE TABLE Alert (
    AlertID     int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    ProductID   int null,
    NewsID      int null,
    CustomerID  int null,
    CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertID)
)

ALTER TABLE Alert WITH CHECK ADD CONSTRAINT CK_OnlyOneFKAllowed 
CHECK ( 
    (ProductID is not null AND NewsID is     null and CustomerID is     null) OR 
    (ProductID is     null AND NewsID is not null and CustomerID is     null) OR 
    (ProductID is     null AND NewsID is     null and CustomerID is not null) 
)

Giải pháp 2 - Một FK trong mỗi Bảng cha
Trong giải pháp này, mỗi bảng 'cha mẹ' có một FK cho bảng Thông báo. Nó giúp dễ dàng truy xuất các cảnh báo liên quan đến cha mẹ. Về phía bên dưới, không có chuỗi thực sự từ Thông báo đến người tham chiếu. Hơn nữa, mô hình dữ liệu cho phép Cảnh báo mồ côi - nơi cảnh báo không được liên kết với Sản phẩm, Tin tức hoặc Khách hàng. Một lần nữa, nhiều LEFT THAM GIA để tìm ra sự liên kết.

CREATE TABLE Product (
    ProductID    int identity(1,1) not null,
    CreateUTC    datetime2(7) not null,
     Name        varchar(100) not null
    AlertID     int null,
    CONSTRAINT   PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
    CustomerID  int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
     Name       varchar(100) not null
    AlertID     int null,
    CONSTRAINT  PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
    NewsID      int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    Name        varchar(100) not null
    AlertID     int null,
    CONSTRAINT  PK_News Primary Key CLUSTERED (NewsID)
)

CREATE TABLE Alert (
    AlertID     int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertID)
)

Đây chỉ là cuộc sống trong một cơ sở dữ liệu quan hệ? Có giải pháp thay thế nào bạn thấy thỏa mãn hơn không?


1
Bạn có thể tạo một bảng cha, Alertable, với các cột sau: ID, CreateDate, Name, Type. Bạn có thể có ba bảng con FK cho nó và ALert của bạn sẽ FK chỉ một bảng, Alertable.
AK

Đối với một số trường hợp, bạn thực hiện một điểm tốt - giải pháp hiệu quả # 3. Tuy nhiên, bất cứ khi nào dữ liệu di chuyển nhanh hoặc cục bộ, tuy nhiên, nó sẽ không hoạt động. Ví dụ, mỗi sản phẩm, Khách hàng và Tin tức đều có bảng "Lang" tương ứng để hỗ trợ Tên bằng nhiều ngôn ngữ. Nếu tôi phải cung cấp 'tên' bằng ngôn ngữ bản địa của người dùng, tôi không thể lưu trữ nó Alertable. Điều đó có ý nghĩa gì?
EBarr

1
@EBarr: "Nếu tôi phải cung cấp 'tên' bằng ngôn ngữ bản địa của người dùng, tôi không thể lưu trữ nó trong Alertable. Điều đó có ý nghĩa gì không?" Không, nó không. Với lược đồ hiện tại của bạn, nếu bạn phải cung cấp 'tên' bằng ngôn ngữ bản địa của người dùng, bạn có thể lưu trữ nó trong bảng Sản phẩm, Khách hàng hoặc Tin tức không?
ypercubeᵀᴹ

@ypercube Tôi đã thêm rằng mỗi bảng có một bảng ngôn ngữ liên quan. Tôi đã cố gắng tạo một kịch bản trong đó văn bản "tên" có thể thay đổi theo yêu cầu và do đó không thể được lưu trữ trong Alertable.
EBarr

Trừ khi nó đã có tên được chấp nhận, tôi đề xuất thuật ngữ "tham gia bạch tuộc" cho truy vấn bạn sẽ làm để xem tất cả các cảnh báo và cha mẹ liên quan của chúng. :)
Nathan Long

Câu trả lời:


4

Tôi hiểu giải pháp thứ hai là không áp dụng vì nó không cung cấp một mối quan hệ (đối tượng)-nhiều (cảnh báo).

Bạn bị mắc kẹt chỉ với hai giải pháp vì tuân thủ nghiêm ngặt 3NF.

Tôi sẽ thiết kế một lược đồ khớp nối ít hơn:

CREATE TABLE Product  (ProductID  int identity(1,1) not null, ...)
CREATE TABLE Customer (CustomerID int identity(1,1) not null, ...)
CREATE TABLE News     (NewsID     int identity(1,1) not null, ...)

CREATE TABLE Alert (
  -- See (1)
  -- AlertID     int identity(1,1) not null,

  AlertClass char(1) not null, -- 'P' - Product, 'C' - Customer, 'N' - News
  ForeignKey int not null,
  CreateUTC  datetime2(7) not null,

  -- See (2)
  CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertClass, ForeignKey)
)

-- (1) you don't need to specify an ID 'just because'. If it's meaningless, just don't.
-- (2) I do believe in composite keys

Hoặc, nếu mối quan hệ toàn vẹn là bắt buộc, tôi có thể thiết kế:

CREATE TABLE Product  (ProductID  int identity(1,1) not null, ...)
CREATE TABLE Customer (CustomerID int identity(1,1) not null, ...)
CREATE TABLE News     (NewsID     int identity(1,1) not null, ...)

CREATE TABLE Alert (
  AlertID     int identity(1,1) not null,
  AlertClass char(1) not null, /* 'P' - Product, 'C' - Customer, 'N' - News */
  CreateUTC  datetime2(7) not null,
  CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertID)
)

CREATE TABLE AlertProduct  (AlertID..., ProductID...,  CONSTRAINT FK_AlertProduct_X_Product(ProductID)    REFERENCES Product)
CREATE TABLE AlertCustomer (AlertID..., CustomerID..., CONSTRAINT FK_AlertCustomer_X_Customer(CustomerID) REFERENCES Customer)
CREATE TABLE AlertNews     (AlertID..., NewsID...,     CONSTRAINT FK_AlertNews_X_News(NewsID)             REFERENCES News)

Dù sao...

Ba giải pháp hợp lệ cộng với một giải pháp khác được xem xét cho nhiều mối quan hệ (đối tượng)-một (cảnh báo) ...

Những điều này được trình bày, những gì là đạo đức?

Chúng khác nhau một cách tinh tế và trọng lượng như nhau trên các tiêu chí:

  • hiệu suất trên chèn và cập nhật
  • phức tạp về truy vấn
  • không gian lưu trữ

Vì vậy, chọn comfier cho bạn.


1
Cảm ơn các đầu vào; Tôi đồng ý với hầu hết các ý kiến ​​của bạn. Tôi có thể mô tả một cách khéo léo mối quan hệ bắt buộc (không bao gồm giải pháp 2 trong mắt bạn), vì đó là một ví dụ được tạo ra. fn1 - đã hiểu, tôi chỉ đơn giản hóa các bảng rất nhiều để tập trung vào vấn đề. fn2 - phím tổng hợp và tôi quay trở lại! Sơ đồ khớp nối ít hơn Tôi hiểu sự đơn giản, nhưng cá nhân tôi cố gắng thiết kế với DRI bất cứ nơi nào có thể.
EBarr

Xem lại điều này, tôi bắt đầu nghi ngờ tính đúng đắn của giải pháp của mình ... dù sao, nó đã được bình chọn hai lần, tôi cảm ơn. Mặc dù tôi tin rằng thiết kế của tôi là hợp lệ nhưng không phù hợp với vấn đề được đưa ra, vì các hiệp hội / liên kết không được giải quyết ...
Marcus Vinicius Pompeu

Từ viết tắt DRI có tôi. Đối với tất cả các bạn, nó là viết tắt của tính toàn vẹn tham chiếu khai báo, kỹ thuật đằng sau tính toàn vẹn dữ liệu tham chiếu thường được triển khai dưới dạng ... (trống cuộn) ... các câu lệnh DDL FOREIGN KEY. Thông tin thêm về en.wikipedia.org/wiki/Declarative_Referential_Integritymsdn.microsoft.com/en-us/l Library / trộm
Marcus Vinicius Pompeu

1

Tôi đã sử dụng bảng tham gia duy trì kích hoạt. giải pháp hoạt động khá tốt như là phương sách cuối cùng nếu tái cấu trúc db là không thể hoặc không mong muốn. Ý tưởng là bạn có một bảng chỉ để xử lý các vấn đề RI và tất cả DRI đều chống lại 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.