Triển khai hệ thống phiên bản với MySQL


15

Tôi biết điều này đã được hỏi ở đâyở đây , nhưng tôi có cùng một ý tưởng với một triển khai có thể khác và tôi cần một số trợ giúp.

Ban đầu tôi có blogstoriesbảng của tôi với cấu trúc này:

| Column    | Type        | Description                                    |
|-----------|-------------|------------------------------------------------|
| uid       | varchar(15) | 15 characters unique generated id              |
| title     | varchar(60) | story title                                    |
| content   | longtext    | story content                                  |
| author    | varchar(10) | id of the user that originally wrote the story |
| timestamp | int         | integer generated with microtime()             |

Sau khi tôi quyết định tôi muốn triển khai một số hệ thống phiên bản cho mọi câu chuyện trên blog, điều đầu tiên tôi nghĩ đến là tạo một bảng khác để giữ các chỉnh sửa ; sau đó, tôi nghĩ rằng tôi có thể sửa đổi bảng hiện có để giữ các phiên bản thay vì chỉnh sửa . Đây là cấu trúc xuất hiện trong tâm trí của tôi:

| Column        | Type          | Description                                       |
|------------   |-------------  |------------------------------------------------   |
| story_id      | varchar(15)   | 15 characters unique generated id                 |
| version_id    | varchar(5)    | 5 characters unique generated id                  |
| editor_id     | varchar(10)   | id of the user that commited                      |
| author_id     | varchar(10)   | id of the user that originally wrote the story    |
| timestamp     | int           | integer generated with microtime()                |
| title         | varchar(60)   | current story title                               |
| content       | longtext      | current story text                                |
| coverimg      | varchar(20)   | cover image name                                  |

Những lý do tại sao tôi đến đây:

  • Các uidlĩnh vực của bảng ban đầu là UNIQUE trong bảng. Bây giờ, story_idkhông còn là duy nhất nữa. Làm thế nào tôi nên đối phó với điều đó? (Tôi nghĩ rằng tôi có thể giải quyết story_id = xvà sau đó tìm phiên bản mới nhất, nhưng điều đó có vẻ rất tốn tài nguyên, vì vậy vui lòng cho lời khuyên của bạn)
  • author_idgiá trị trường được lặp lại trong mỗi hàng của bảng. Tôi nên giữ nó ở đâu và như thế nào?

Biên tập

Quá trình tạo mã duy nhất là trong CreateUniqueCodechức năng:

trait UIDFactory {
  public function CryptoRand(int $min, int $max): int {
    $range = $max - $min;
    if ($range < 1) return $min;
    $log = ceil(log($range, 2));
    $bytes = (int) ($log / 8) + 1;
    $bits = (int) $log + 1;
    $filter = (int) (1 << $bits) - 1;
    do {
        $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
        $rnd = $rnd & $filter;
    } while ($rnd >= $range);
    return $min + $rnd;
  }
  public function CreateUID(int $length): string {
    $token = "";
    $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
    $codeAlphabet.= "0123456789";
    $max = strlen($codeAlphabet) - 1;
    for ($i=0; $i < $length; $i++) {
        $token .= $codeAlphabet[$this->CryptoRand(0, $max)];
    }
    return $token;
  }
}

Mã được viết bằng Hack và ban đầu được viết bằng PHP bởi @Scott trong câu trả lời của anh ấy .

Các trường author_ideditor_id có thể khác nhau, vì có những người dùng có đủ quyền để chỉnh sửa câu chuyện của bất kỳ ai.

Câu trả lời:


23

Phân tích kịch bản -which quà đặc điểm gắn liền với chủ đề được gọi là cơ sở dữ liệu theo thời gian - từ góc độ lý thuyết, người ta có thể xác định rằng: (a) “có mặt” Blog Câu chuyện bản và (b) một “quá khứ” Blog Câu chuyện bản , mặc dù rất tương tự, là các thực thể của các loại khác nhau.

Ngoài ra, khi làm việc ở mức độ trừu tượng logic, các sự kiện (được biểu thị bằng các hàng) của các loại riêng biệt phải được giữ lại trong các bảng riêng biệt. Trong trường hợp đang được xem xét, ngay cả khi khá giống nhau, (i) các sự kiện về Phiên bản hiện tại của Khác biệt với (ii) sự thật về Phiên bản trước đây của Phiên bản .

Do đó tôi khuyên bạn nên quản lý tình huống bằng hai bảng:

  • một phiên bản dành riêng cho các phiên bản hiện tại và các phiên bản hiện tại của Wikipedia

  • một trong đó là riêng biệt, mà còn liên kết với người khác, cho tất cả các hoặc “quá khứ” “trước” phiên bản ;

mỗi cột có (1) một số cột hơi khác biệt và (2) một nhóm các ràng buộc khác nhau.

Quay lại lớp khái niệm, Tôi cho rằng -in kinh doanh với môi trường của bạn Tác giảbiên tập viên là khái niệm có thể được mô tả như vai trò có thể được chơi bởi một tài khoản , và những khía cạnh quan trọng phụ thuộc vào dữ liệu nguồn gốc (thông qua các hoạt động thao tác logic cấp) và giải thích (được thực hiện bởi các độc giả và nhà văn Blog Stories , ở cấp độ bên ngoài của hệ thống thông tin máy tính, với sự hỗ trợ của một hoặc nhiều chương trình ứng dụng).

Tôi sẽ chi tiết tất cả các yếu tố này và các điểm liên quan khác như sau.

Quy tắc kinh doanh

Theo hiểu biết của tôi về các yêu cầu của bạn, các công thức quy tắc kinh doanh sau đây (được kết hợp theo các loại thực thể có liên quan và các loại mối quan hệ của chúng) đặc biệt hữu ích trong việc thiết lập lược đồ khái niệm tương ứng :

  • Một người dùng viết không có một hoặc nhiều BlogStories
  • Một BlogStory giữ zero-một-hoặc-nhiều BlogStoryVersions
  • Một người dùng đã viết không một hoặc nhiều BlogStoryVersions

Sơ đồ IDEF1X tiếp xúc

Do đó, để thể hiện sự gợi ý của tôi nhờ thiết bị đồ họa, tôi đã tạo ra một IDEF1X mẫu, một sơ đồ bắt nguồn từ các quy tắc kinh doanh được xây dựng ở trên và các tính năng khác có vẻ phù hợp. Nó được hiển thị trong Hình 1 :

Hình 1 - Các phiên bản Blog Story IDEF1X

Tại sao BlogStoryBlogStoryVersion được khái niệm hóa thành hai loại thực thể khác nhau?

Bởi vì:

  • Một phiên bản BlogStoryVersion (ví dụ, một quá khứ trước đây) luôn giữ một giá trị cho một thuộc tính UpdateDateTime , trong khi sự xuất hiện của BlogStory (tức là một hiện tại của một ngôi sao) không bao giờ giữ nó.

  • Bên cạnh đó, các đơn vị của những loại được xác định duy nhất bởi các giá trị của hai bộ riêng biệt của thuộc tính: BlogStoryNumber (trong trường hợp của BlogStory lần xuất hiện), và BlogStoryNumber cộng CreatedDateTime (trong trường hợp của BlogStoryVersion trường hợp).


một 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ó dựa trên các tài liệu lý thuyết ban đầu được tác giả bởi người khởi tạo duy nhất của mô hình quan hệ , tức là Tiến sĩ EF Codd ; trên quan điểm Thực thể-Mối quan hệ của dữ liệu, được phát triển bởi Tiến sĩ PP Chen ; và cũng trên Kỹ thuật thiết kế cơ sở dữ liệu logic, được tạo bởi Robert G. Brown.


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

Sau đó, dựa trên phân tích khái niệm được trình bày trước đây, tôi đã tuyên bố thiết kế mức logic dưới đây:

-- 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 at the physical level.

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

CREATE TABLE UserProfile (
    UserId          INT      NOT NULL,
    FirstName       CHAR(30) NOT NULL,
    LastName        CHAR(30) NOT NULL,
    BirthDate       DATETIME NOT NULL,
    GenderCode      CHAR(3)  NOT NULL,
    UserName        CHAR(20) NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT UserProfile_PK  PRIMARY KEY (UserId),
    CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
        FirstName,
        LastName,
        BirthDate,
        GenderCode
    ), 
    CONSTRAINT UserProfile_AK2 UNIQUE (UserName) -- ALTERNATE KEY.
);

CREATE TABLE BlogStory (
    BlogStoryNumber INT      NOT NULL,
    Title           CHAR(60) NOT NULL,
    Content         TEXT     NOT NULL,
    CoverImageName  CHAR(30) NOT NULL,
    IsActive        BIT(1)   NOT NULL,
    AuthorId        INT      NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT BlogStory_PK              PRIMARY KEY (BlogStoryNumber),
    CONSTRAINT BlogStory_AK              UNIQUE      (Title), -- ALTERNATE KEY.
    CONSTRAINT BlogStoryToUserProfile_FK FOREIGN KEY (AuthorId)
        REFERENCES UserProfile (UserId)
);

CREATE TABLE BlogStoryVersion  (
    BlogStoryNumber INT      NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    Title           CHAR(60) NOT NULL,
    Content         TEXT     NOT NULL,
    CoverImageName  CHAR(30) NOT NULL,
    IsActive        BIT(1)   NOT NULL,
    AuthorId        INT      NOT NULL,
    UpdatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT BlogStoryVersion_PK              PRIMARY KEY (BlogStoryNumber, CreatedDateTime), -- Composite PK.
    CONSTRAINT BlogStoryVersionToBlogStory_FK   FOREIGN KEY (BlogStoryNumber)
        REFERENCES BlogStory (BlogStoryNumber),
    CONSTRAINT BlogStoryVersionToUserProfile_FK FOREIGN KEY (AuthorId)
        REFERENCES UserProfile (UserId),
    CONSTRAINT DatesSuccession_CK               CHECK       (UpdatedDateTime > CreatedDateTime) --Let us hope that MySQL will finally enforce CHECK constraints in a near future version.
);

Đã thử nghiệm trong SQL Fiddle này chạy trên MySQL 5.6.

Cái BlogStorybàn

Như bạn có thể thấy trong thiết kế demo, tôi đã định nghĩa cột BlogStoryPRIMARY KEY (PK for brevity) với kiểu dữ liệu INT. Về vấn đề này, bạn có thể muốn sửa một quy trình tự động tích hợp để tạo và gán một giá trị số cho một cột như vậy trong mỗi lần chèn hàng. Nếu thỉnh thoảng bạn không để lại các khoảng trống trong bộ giá trị này, thì bạn có thể sử dụng thuộc tính AUTO_INCREMENT , thường được sử dụng trong môi trường MySQL.

Khi nhập tất cả các BlogStory.CreatedDateTimeđiểm dữ liệu riêng lẻ của bạn , bạn có thể sử dụng hàm NOW () , trả về các giá trị Ngày và Giờ hiện tại trong máy chủ cơ sở dữ liệu tại thời điểm thao tác INSERT chính xác. Đối với tôi, cách làm này được quyết định là phù hợp hơn và ít bị lỗi hơn so với việc sử dụng các thói quen bên ngoài.

Với điều kiện, như đã thảo luận trong các bình luận (hiện đã bị xóa), bạn muốn tránh khả năng duy trì BlogStory.Titlecác giá trị trùng lặp, bạn phải thiết lập một ràng buộc ĐỘC ĐÁO cho cột này. Do thực tế là một Tiêu đề nhất định có thể được chia sẻ bởi một số (hoặc thậm chí là tất cả) trong quá khứ BlogStoryVersions , nên không nên thiết lập một ràng buộc KHÔNG GIỚI HẠN cho BlogStoryVersion.Titlecột.

Tôi đã bao gồm BlogStory.IsActivecột loại BIT (1) (mặc dù TINYINT cũng có thể được sử dụng) trong trường hợp bạn cần cung cấp chức năng DEL mềm mềm hoặc hoặc logic logic DEL DEL.

Chi tiết về BlogStoryVersionbảng

Mặt khác, PK của BlogStoryVersionbảng bao gồm (a) BlogStoryNumbervà (b) một cột có tên CreatedDateTime, tất nhiên, đánh dấu thời điểm chính xác trong đó một BlogStoryhàng trải qua một CHỨNG MINH.

BlogStoryVersion.BlogStoryNumber, ngoài việc là một phần của PK, còn bị ràng buộc như là một NGOẠI TỆ (FK) tham chiếu BlogStory.BlogStoryNumber, một cấu hình thực thi tính toàn vẹn tham chiếu giữa các hàng của hai bảng này. Về mặt này, việc thực hiện một thế hệ tự động của a BlogStoryVersion.BlogStoryNumberlà không cần thiết bởi vì, được đặt là FK, các giá trị được CHỈ vào cột này phải được rút ra từ các giá trị được đặt trong BlogStory.BlogStoryNumberđối tác liên quan .

Các BlogStoryVersion.UpdatedDateTimecột nên giữ lại, như mong đợi, điểm kịp thời khi một BlogStoryhàng đã được sửa đổi và, do đó, bổ sung vào BlogStoryVersionbảng. Do đó, bạn cũng có thể sử dụng hàm NOW () trong tình huống này.

Các Interval thấu hiểu giữa BlogStoryVersion.CreatedDateTimeBlogStoryVersion.UpdatedDateTimethể hiện toàn bộ giai đoạn trong đó một BlogStoryhàng là “có mặt” hay “hiện tại”.

Cân nhắc cho một Versioncột

Có thể hữu ích khi nghĩ về BlogStoryVersion.CreatedDateTimecột giữ giá trị đại diện cho một Phiên bản trước đây của một phiên bản cá nhân của BlogStory . Tôi cho rằng điều này có lợi hơn nhiều so với một VersionIdhoặc VersionCode, vì nó thân thiện với người dùng hơn theo nghĩa mọi người có xu hướng quen thuộc hơn với các khái niệm thời gian . Chẳng hạn, các tác giả hoặc độc giả blog có thể tham khảo BlogStoryVersion theo cách tương tự như sau:

  • Tôi muốn xem Phiên bản cụ thể của BlogStory được xác định bởi Số 1750 được tạo trên 26 August 2015tại 9:30.

Vai trò của tác giảbiên tập viên : Xuất phát và giải thích dữ liệu

Với cách tiếp cận này, bạn có thể dễ dàng phân biệt ai là người nắm giữ bản gốc AuthorIdcủa một BlogStory cụ thể LỰA CHỌN Phiên bản sớm nhất của một BlogStoryIdTỪ một BlogStoryVersionbảng nhất định nhờ áp dụng hàm MIN () cho BlogStoryVersion.CreatedDateTime.

Theo cách này, mỗi BlogStoryVersion.AuthorIdgiá trị được chứa trong tất cả các hàng Phiên bản sau này hoặc các phiên bản thành công của các phiên bản , sau đó, chỉ ra, định danh Tác giả của Phiên bản tương ứng , nhưng người ta cũng có thể nói rằng giá trị đó đồng thời là biểu thị các vai trò chơi bởi tham gia tài như biên tập viên của “bản gốc” Version của một BlogStory .

Có, một AuthorIdgiá trị nhất định có thể được chia sẻ bởi nhiều BlogStoryVersionhàng, nhưng đây thực sự là một thông tin cho biết điều gì đó rất có ý nghĩa về mỗi Phiên bản , vì vậy việc lặp lại dữ liệu đã nói không phải là vấn đề.

Định dạng của các cột DATETIME

Đối với kiểu dữ liệu của DATETIME, vâng, bạn đã đúng, và MySQL truy xuất và hiển thị các giá trị của DATETIME theo YYYY-MM-DD HH:MM:SSđịnh dạng '' , nhưng bạn có thể tự tin nhập dữ liệu thích hợp theo cách này và khi bạn phải thực hiện một truy vấn bạn chỉ cần thực hiện sử dụng các hàm DATE và TIME tích hợp để, trong số những thứ khác, hiển thị các giá trị liên quan ở định dạng phù hợp cho người dùng của bạn. Hoặc bạn chắc chắn có thể thực hiện loại định dạng dữ liệu này thông qua mã chương trình ứng dụng của bạn.

Ý nghĩa của các BlogStoryhoạt động CẬP NHẬT

Mỗi khi một BlogStoryhàng bị CẬP NHẬT, bạn phải đảm bảo rằng các giá trị tương ứng là hiện tại của bản thân cho đến khi sửa đổi diễn ra sau đó được XÁC NHẬN vào BlogStoryVersionbảng. Do đó, tôi đặc biệt khuyên bạn nên hoàn thành các hoạt động này trong một GIAO DỊCH ACID duy nhất để đảm bảo rằng chúng được coi là một Đơn vị công việc không thể chia cắt. Bạn cũng có thể sử dụng TRIGGERS, nhưng họ có xu hướng làm cho mọi thứ không gọn gàng, để nói.

Giới thiệu một VersionIdhoặc VersionCodecột

Nếu bạn chọn (vì hoàn cảnh kinh doanh hoặc sở thích cá nhân) để kết hợp một BlogStory.VersionIdhoặc BlogStory.VersionCodecột để phân biệt BlogStoryVersions , bạn nên suy nghĩ về các khả năng sau:

  1. A VersionCodecó thể được yêu cầu là ĐỘC ĐÁO trong (i) toàn bộ BlogStorybảng và cả trong (ii) BlogStoryVersion.

    Do đó, bạn phải thực hiện một phương pháp được kiểm tra cẩn thận và hoàn toàn đáng tin cậy để tạo và gán từng Codegiá trị.

  2. Có thể, các VersionCodegiá trị có thể được lặp lại trong các BlogStoryhàng khác nhau , nhưng không bao giờ được nhân đôi cùng BlogStoryNumber. Ví dụ: bạn có thể có:

    • một BlogStoryNumber 3- Phiên bản83o7c5c và, đồng thời,
    • một BlogStoryNumber 86- Phiên bản83o7c5c
    • một BlogStoryNumber 958- Phiên bản83o7c5c .

Khả năng sau này sẽ mở ra một phương án khác:

  1. Giữ một VersionNumbercho BlogStories, vì vậy có thể có:

    • BlogStoryNumber 23- Phiên bản1, 2, 3… ;
    • BlogStoryNumber 650- Phiên bản1, 2, 3… ;
    • BlogStoryNumber 2254- Phiên bản1, 2, 3… ;
    • Vân vân.

Tổ chức các phiên bản gốc và các phiên bản tiếp theo của Nhật Bản trong một bảng duy nhất

Mặc dù duy trì tất cả các BlogStoryVersions trong cùng một bảng cơ sở riêng lẻ , tôi khuyên bạn không nên làm điều đó bởi vì bạn sẽ trộn lẫn hai loại sự kiện (khái niệm) riêng biệt, do đó có tác dụng phụ không mong muốn đối với

  • ràng buộc dữ liệu và thao tác (ở mức logic), cùng với
  • việc xử lý và lưu trữ liên quan (ở tầng vật lý).

Nhưng, với điều kiện bạn chọn tuân theo tiến trình hành động đó, bạn vẫn có thể tận dụng nhiều ý tưởng chi tiết ở trên, ví dụ:

  • một PK tổng hợp bao gồm một cột INT ( BlogStoryNumber) và cột DATETIME ( CreatedDateTime);
  • việc sử dụng các chức năng của máy chủ để tối ưu hóa các quy trình thích hợp và
  • các tác giảbiên tập viên derivable Roles .

Thấy rằng, bằng cách tiếp tục với cách tiếp cận như vậy, một BlogStoryNumbergiá trị sẽ được nhân đôi ngay sau khi thêm phiên bản mới hơn của Phiên bản , một tùy chọn mà bạn có thể đánh giá (rất giống với những gì được đề cập trong phần trước) đang thiết lập BlogStoryPK bao gồm các cột BlogStoryNumberVersionCode, theo cách này, bạn sẽ có thể xác định duy nhất mỗi Phiên bản của BlogStory . Và bạn có thể thử với sự kết hợp BlogStoryNumberVersionNumberquá.

Kịch bản tương tự

Bạn có thể tìm thấy câu trả lời của tôi cho câu hỏi trợ giúp này, vì tôi cũng đề xuất cho phép các khả năng tạm thời trong cơ sở dữ liệu liên quan để giải quyết một kịch bản có thể so sánh.


2

Một tùy chọn là sử dụng Phiên bản bình thường (vnf). Những lợi thế bao gồm:

  • Dữ liệu hiện tại và tất cả dữ liệu trong quá khứ nằm trong cùng một bảng.
  • Truy vấn tương tự được sử dụng để truy xuất dữ liệu hiện tại hoặc dữ liệu hiện tại kể từ bất kỳ ngày cụ thể nào.
  • Tham chiếu khóa ngoài đến dữ liệu được phiên bản hoạt động tương tự như đối với dữ liệu chưa được đảo ngược.

Một lợi ích bổ sung trong trường hợp của bạn, vì dữ liệu được phiên bản được xác định duy nhất bằng cách làm cho ngày có hiệu lực (ngày thay đổi được thực hiện) một phần của khóa, không yêu cầu trường version_id riêng.

Dưới đây là một lời giải thích cho một loại thực thể rất giống nhau.

Thông tin chi tiết có thể được tìm thấy trong một bản trình bày ở đây và một tài liệu chưa hoàn thành ở đây


1

Mối quan hệ của bạn

(Story_id, version_id, Editor_id, Author_id, dấu thời gian, tiêu đề, nội dung, coverimg)

không ở dạng thứ 3 bình thường. Đối với mọi phiên bản câu chuyện của bạn, Author_id đều giống nhau. Vì vậy, bạn cần hai mối quan hệ để vượt qua điều này

(truyện_id, tác giả_id)
(Story_id, version_id, Editor_id, dấu thời gian, tiêu đề, nội dung, coverimg)

Khóa của quan hệ thứ nhất là story_id, khóa của quan hệ thứ hai là khóa kết hợp (story_id, version_id). Nếu bạn không thích khóa kết hợp thì bạn chỉ có thể sử dụng version_idlàm khóa


2
Điều này dường như không giải quyết được vấn đề của tôi, nó chỉ nhấn mạnh chúng
Victor

Vì vậy, nó thậm chí không trả lời author_id giá trị trường của truy vấn đang lặp lại trong mỗi hàng của bảng. Tôi nên giữ nó ở đâu và như thế nào ?
phép lạ173

2
Tôi không thực sự hiểu câu trả lời của bạn nói gì. Có thể là do tôi không phải là người nói tiếng Anh bản địa, vì vậy bạn có thể cố gắng giải thích nó bằng những từ đơn giản hơn không?
Victor

Điều đó có nghĩa là bạn nên tránh sự lặp lại của số Author_id (nếu Story_id bằng hai hàng, thì Author_id của chúng cũng bằng nhau) và chia bảng của bạn thành hai bảng như được mô tả trong bài viết của tôi. Vì vậy, bạn có thể tránh sự lặp lại của Author_id.
phép lạ173
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.