Làm cách nào để ánh xạ mối quan hệ IS-A vào cơ sở dữ liệu?


26

Hãy xem xét những điều sau đây:

entity User
{
    autoincrement uid;
    string(20) name;
    int privilegeLevel;
}

entity DirectLoginUser
{
    inherits User;
    string(20) username;
    string(16) passwordHash;
}

entity OpenIdUser
{
    inherits User;
    //Whatever attributes OpenID needs... I don't know; this is hypothetical
}

Các loại người dùng khác nhau (người dùng Đăng nhập trực tiếp và người dùng OpenID) hiển thị mối quan hệ IS-A; cụ thể là cả hai loại người dùng đều là người dùng. Bây giờ, có một số cách có thể được biểu diễn trong RDBMS:

Cách một

CREATE TABLE Users
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    type ENUM("DirectLogin", "OpenID") NOT NULL,
    username VARCHAR(20) NULL,
    passwordHash VARCHAR(20) NULL,
    //OpenID Attributes
    PRIMARY_KEY(uid)
)

Cách hai

CREATE TABLE Users
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privilegeLevel INTEGER NOT NULL,
    type ENUM("DirectLogin", "OpenID") NOT NULL,
    PRIMARY_KEY(uid)
)

CREATE TABLE DirectLogins
(
    uid INTEGER NOT_NULL,
    username VARCHAR(20) NOT NULL,
    passwordHash VARCHAR(20) NOT NULL,
    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid) REFERENCES Users.uid
)

CREATE TABLE OpenIDLogins
(
    uid INTEGER NOT_NULL,
    // ...
    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid) REFERENCES Users.uid
)

Cách ba

CREATE TABLE DirectLoginUsers
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    username VARCHAR(20) NOT NULL,
    passwordHash VARCHAR(20) NOT NULL,
    PRIMARY_KEY(uid)
)

CREATE TABLE OpenIDUsers
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    //OpenID Attributes
    PRIMARY_KEY(uid)
)

Tôi gần như chắc chắn cách thứ ba là sai cách, vì không thể thực hiện một phép nối đơn giản với người dùng ở nơi khác trong cơ sở dữ liệu.

Ví dụ trong thế giới thực của tôi không phải là người dùng có ví dụ đăng nhập khác nhau; Tôi quan tâm đến cách mô hình hóa mối quan hệ này trong trường hợp chung.


Tôi đã chỉnh sửa câu trả lời của mình để đưa vào cách tiếp cận được đề xuất trong các bình luận của Joel Brown. Nó sẽ làm việc cho bạn. Nếu bạn đang tìm kiếm thêm gợi ý, tôi sẽ gắn thẻ lại câu hỏi của bạn để cho biết bạn đang tìm câu trả lời dành riêng cho MySQL.
Nick Chammas

Lưu ý rằng nó thực sự phụ thuộc vào cách các mối quan hệ được sử dụng trong phần còn lại của cơ sở dữ liệu. Mối quan hệ liên quan đến các thực thể con yêu cầu các khóa ngoại đối với các bảng đó và cấm ánh xạ vào một bảng thống nhất duy nhất mà không có một chút hack.
beldaz

Trong các cơ sở dữ liệu quan hệ đối tượng như PostgreSQL, thực sự có một cách thứ tư: bạn có thể khai báo một bảng INHERITS từ một bảng khác. postgresql.org/docs/civerse/static/tutorial-inherribution.html
MarkusSchaber

Câu trả lời:


16

Cách hai là cách chính xác.

Lớp cơ sở của bạn nhận được một bảng và sau đó các lớp con có các bảng riêng của chúng chỉ với các trường bổ sung mà chúng giới thiệu, cộng với các tham chiếu khóa ngoài vào bảng cơ sở.

Như Joel đề xuất trong nhận xét của mình về câu trả lời này, bạn có thể đảm bảo rằng người dùng sẽ có thông tin đăng nhập trực tiếp hoặc đăng nhập OpenID, nhưng không phải cả hai (và cũng có thể không) bằng cách thêm một cột loại vào mỗi bảng loại phụ mà khóa lại đến bảng gốc. Cột loại trong mỗi bảng loại phụ bị hạn chế có một giá trị duy nhất đại diện cho loại bảng đó. Vì cột này được khóa ngoài vào bảng gốc, mỗi lần chỉ có một hàng loại phụ có thể liên kết với cùng một hàng gốc.

Ví dụ, MySQL DDL sẽ trông giống như:

CREATE TABLE Users
(
      uid               INTEGER AUTO_INCREMENT NOT NULL
    , type              ENUM("DirectLogin", "OpenID") NOT NULL
    // ...

    , PRIMARY_KEY(uid)
);

CREATE TABLE DirectLogins
(
      uid               INTEGER NOT_NULL
    , type              ENUM("DirectLogin") NOT NULL
    // ...

    , PRIMARY_KEY(uid)
    , FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);

CREATE TABLE OpenIDLogins
(
      uid               INTEGER NOT_NULL
    , type              ENUM("OpenID") NOT NULL
    // ...

    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);

(Trên các nền tảng khác, bạn sẽ sử dụng một CHECKràng buộc thay vì ENUM.) MySQL hỗ trợ các khóa ngoại tổng hợp để việc này phù hợp với bạn.

Cách một là hợp lệ, mặc dù bạn đang lãng phí không gian trong các NULLcột có thể sử dụng được vì việc sử dụng chúng phụ thuộc vào loại người dùng. Ưu điểm là nếu bạn chọn mở rộng loại loại người dùng nào để lưu trữ và những loại đó không yêu cầu các cột bổ sung, bạn chỉ có thể mở rộng tên miền của mình ENUMvà sử dụng cùng một bảng.

Cách ba buộc bất kỳ truy vấn nào tham chiếu người dùng để kiểm tra đối với cả hai bảng. Điều này cũng ngăn bạn tham khảo một bảng người dùng thông qua khóa ngoại.


1
Làm thế nào để tôi đối phó với thực tế là sử dụng cách 2, không có cách nào để thực thi rằng có chính xác một hàng tương ứng trong hai bảng khác?
Billy ONeal

2
@Billy - Phản đối tốt. Nếu người dùng của bạn chỉ có thể có cái này hoặc cái kia, bạn có thể thực thi điều này thông qua lớp Proc hoặc trình kích hoạt. Tôi tự hỏi nếu có một cách cấp DDL để thực thi ràng buộc này. (Alas, quan điểm lập chỉ mục không cho phép UNION , hoặc tôi đã gợi ý một cái nhìn được lập chỉ mục với một chỉ số duy nhất so với UNION ALLcác uidtừ hai bảng.)
Nick Chammas

Tất nhiên, giả định rằng RDBMS của bạn hỗ trợ các khung nhìn được lập chỉ mục ở vị trí đầu tiên.
Billy ONeal

1
Một cách thuận tiện để bao hàm loại ràng buộc bảng chéo này sẽ bao gồm một thuộc tính phân vùng trong bảng siêu loại. Sau đó, mỗi loại phụ có thể kiểm tra để đảm bảo rằng nó chỉ liên quan đến các siêu loại có giá trị thuộc tính phân vùng thích hợp. Điều này tiết kiệm việc phải thực hiện kiểm tra va chạm bằng cách xem xét một hoặc nhiều bảng loại phụ khác.
Joel Brown

1
@Joel - Vì vậy, ví dụ, chúng tôi thêm một typecột vào mỗi bảng loại phụ bị hạn chế thông qua CHECKràng buộc để có chính xác một giá trị (loại của bảng đó). Sau đó, chúng ta biến các khóa ngoại của bảng phụ thành siêu bảng thành các khóa tổng hợp trên cả hai uidtype. Thật khéo léo.
Nick Chammas

5

Họ sẽ được đặt tên

  1. Kế thừa bảng đơn
  2. Kế thừa bảng lớp
  3. Kế thừa bảng bê tông .

và tất cả đều có cách sử dụng hợp pháp của họ và được hỗ trợ bởi một số thư viện. Bạn phải tìm ra cái nào phù hợp nhất.

Có nhiều bảng sẽ giúp quản lý dữ liệu nhiều hơn vào mã ứng dụng của bạn nhưng sẽ giảm dung lượng không sử dụng.


2
Có một kỹ thuật bổ sung gọi là "Khóa chính được chia sẻ". Trong kỹ thuật này, các bảng lớp con không có id khóa chính được gán độc lập. Thay vào đó, PK của các bảng lớp con là FK tham chiếu bảng siêu lớp. Điều này cung cấp một số lợi ích, chủ yếu là nó thực thi bản chất một đối một của các mối quan hệ IS-A. Kỹ thuật này là một bổ sung vào Kế thừa bảng lớp.
Walter Mitty
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.