Впровадження системи версій з MySQL


15

Я знаю, що це запитували тут і тут , але я маю ту саму ідею з іншою можливою реалізацією, і мені потрібна допомога.

Спочатку я мав свою blogstoriesтаблицю з такою структурою:

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

Після того як я вирішив, що хочу впровадити певну систему версій для кожної історії в блозі, перше, що мені прийшло в голову, - це створити іншу таблицю для редагування змін ; після цього я подумав, що можу змінити існуючу таблицю для розміщення версій замість правок . Це структура, яка мені прийшла в голову:

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

Причини, чому я прийшов сюди:

  • uidПоле вихідної таблиці було унікальним в таблиці. Тепер це story_idвже не унікальне. Як мені з цим боротися? (Я думав, я можу звернутися, story_id = xа потім знайти останню версію, але це здається дуже трудомістким, тому, будь ласка, дайте пораду)
  • author_idзначення поля повторюється в кожному рядку таблиці. Де і як я повинен його зберігати?

Редагувати

Процес генерації унікальних кодів CreateUniqueCodeфункціонує:

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

Код написаний на Hack , і спочатку був написаний на PHP @Scott у своїй відповіді .

Поля author_idі editor_id можуть бути різними, тому що користувачі з досить прав для редагування чиїх історій.

Відповіді:


23

Аналізуючи сценарій - який представляє характеристики, пов’язані з предметом, відомим як тимчасові бази даних - з концептуальної точки зору можна визначити, що: (a) "теперішня" версія Story Blog Blog і (b) "минула" версія Story Blog Blog , хоча дуже схожі, є сутностями різних типів.

На додаток до цього, працюючи на логічному рівні абстракції, факти (представлені рядками) різних видів повинні зберігатися у різних таблицях. У розглянутому випадку, навіть коли досить схожі, (i) факти про "теперішні" Версії відрізняються від (ii) факти про "минулі" Версії .

Тому я рекомендую керувати ситуацією за допомогою двох таблиць:

  • один призначено винятково для «поточної» або «справжньої» Версії цих історій Щоденника , і

  • той, який є окремим, але також пов'язаний з іншим, для всіх "попередніх" або "минулих" версій ;

кожен з (1) трохи виразною кількістю стовпців і (2) різною групою обмежень.

Назад до концептуального шару, я вважаю , що -in ваш бізнес для навколишнього середовища Автор і редактор є поняттями , які можуть бути окреслені в якості ролей , які можуть бути відтворені на користувача , і ці важливі аспекти залежать від даних виведення ( з допомогою операцій маніпулювання логічного рівня) та інтерпретація (проводиться читачами та письменниками Blog Stories на зовнішньому рівні комп’ютеризованої інформаційної системи за сприяння однієї або декількох прикладних програм).

Я детально деталізую всі ці фактори та інші відповідні моменти наступним чином.

Правила бізнесу

Згідно з моїм розумінням ваших вимог, наступні формулювання бізнес-правил (складені у відповідності до відповідних типів суб'єктів господарювання та їх видів взаємозв'язків) є особливо корисними для створення відповідної концептуальної схеми:

  • Користувач пише нуль-один-або-багатьох BlogStories
  • BlogStory має нульовий один або багатьом BlogStoryVersions
  • Користувач написав нуль-один-або-багатьох BlogStoryVersions

Діаграма IDEF1X експозиції

Отже, для того , щоб викласти свою пропозицію в силі графічного пристрою, я створив зразок IDEF1x діаграма , яка є похідною від бізнесу - правил , сформульованих вище , та інших функцій , які здаються доречними. Це показано на малюнку 1 :

Рисунок 1 - Версії історії блогу IDEF1X Діаграма

Чому BlogStory та BlogStoryVersion концептуалізуються як два різні типи сутності?

Оскільки:

  • BlogStoryVersion екземпляр (тобто, «минуле» один) завжди має значення для UpdatedDateTime власності, в той час як BlogStory явище (тобто, «справжній» один) ніколи не утримує його.

  • Крім того, сутності цих типів однозначно ідентифікуються за значеннями двох різних наборів властивостей: BlogStoryNumber (у випадку подій BlogStory ) та BlogStoryNumber плюс CreatedDateTime (у випадку екземплярів BlogStoryVersion ).


Визначення інтеграції для інформаційного моделювання ( IDEF1X ) є високо рекомендовані дані моделювання методомякий був створенийякості стандарту в грудні 1993 року США Національним інститутом стандартів і технологій (NIST). Він заснований на ранньому теоретичному матеріалі авторство єдиним винуватцем у реляційну моделі , тобто д - р Ф. Кодда ; напогляд даних про відносини між особами та особами, розроблений доктором П.П. Ченом ; а також про техніку дизайну логічної бази даних, створену Робертом Г. Брауном.


Ілюстративний логічний макет SQL-DDL

Потім, грунтуючись на концептуальному аналізі, представленому раніше, я оголосив проект логічного рівня нижче:

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

Перевірено в цій скрипті SQL, який працює на MySQL 5.6.

BlogStoryстіл

Як ви бачите в демонстраційному дизайні, я визначив BlogStoryстовпець PRIMARY KEY (ПК для стислості) з типом даних INT. У зв'язку з цим, можливо, ви хочете виправити вбудований автоматичний процес, який генерує та призначає числове значення для такого стовпця в кожній вставці рядка. Якщо ви не заперечуєте час від часу залишати прогалини в цьому наборі значень, тоді ви можете використовувати атрибут AUTO_INCREMENT , який зазвичай використовується в середовищах MySQL.

Вводячи всі ваші індивідуальні BlogStory.CreatedDateTimeточки даних, ви можете використовувати функцію NOW () , яка повертає значення Дати та Часу, які є поточними на сервері баз даних у той самий момент операції ВСТУП. Для мене ця практика, очевидно, більше підходить і менш схильна до помилок, ніж використання зовнішніх процедур.

За умови, що, як було обговорено у коментарях (тепер видалених), ви хочете уникнути можливості збереження BlogStory.Titleдублікатів значень, ви повинні встановити UNIQUE обмеження для цього стовпця. Через те, що даний Заголовок може бути спільним для декількох (або навіть усіх) "минулих" BlogStoryVersions , тоді Унікальне обмеження для колонки не повинно встановлюватися BlogStoryVersion.Title.

Я включив BlogStory.IsActiveстовпець типу BIT (1) (хоча TINYINT також може бути використаний) у випадку, якщо вам потрібно забезпечити функцію «м'якого» або «логічного» DELETE.

Детальніше про BlogStoryVersionтаблицю

З іншого боку, ПК BlogStoryVersionтаблиці складається з (а) BlogStoryNumberта (б) стовпця з назвою, CreatedDateTimeякий, звичайно, позначає точний момент, у якому BlogStoryряд зазнав ВСТУП.

BlogStoryVersion.BlogStoryNumberокрім того, що є частиною ПК, також обмежується як ЗАМЕЧНИЙ КЛЮЧ (FK), який посилається BlogStory.BlogStoryNumber, конфігурація, що забезпечує референтну цілісність між рядками цих двох таблиць. У цьому відношенні реалізація автоматичної генерації a BlogStoryVersion.BlogStoryNumberне є необхідною, оскільки, встановлюючи як FK, значення ВСТАНОВЛЕННЯ в цей стовпець повинні бути "виведені" з тих, які вже вкладені у відповідний BlogStory.BlogStoryNumberаналог.

У BlogStoryVersion.UpdatedDateTimeстовпці, як очікується, слід зберігати момент часу, коли BlogStoryрядок було змінено і, як наслідок, додано до BlogStoryVersionтаблиці. Отже, ви можете використовувати функцію NOW () і в цій ситуації.

Інтервал обгорнула між BlogStoryVersion.CreatedDateTimeі BlogStoryVersion.UpdatedDateTimeвисловлює весь Період , в протягом якого BlogStoryрядок була «присутній» або «поточний».

Міркування для Versionстовпчика

Це може бути корисно думати BlogStoryVersion.CreatedDateTimeяк стовпець , який містить значення , яке представляє собою окрему «минуле» версію про більш BlogStory . Я вважаю це набагато вигідніше, ніж VersionIdабо VersionCode, оскільки це зручніше для користувачів в тому сенсі, що люди, як правило, більш знайомі з поняттями часу . Наприклад, автори блогу чи читачі можуть посилатися на BlogStoryVersion у такий спосіб, як:

  • «Я хочу побачити специфіку Версія в BlogStory ідентифікованої Номер 1750 , який був Created на 26 August 2015в 9:30».

Автор і редактор Ролі: Висновок даних і інтерпретація

При такому підході можна легко розрізнити , хто тримає «оригінал» AuthorIdз бетону BlogStory , що вибирає «рання» версію певних BlogStoryIdІЗ BlogStoryVersionтаблиці в силі застосування функції MIN () для BlogStoryVersion.CreatedDateTime.

Таким чином, кожне BlogStoryVersion.AuthorIdзначення, що міститься у всіх рядках "пізнішої" або "успішної" версій, вказує, природно, ідентифікатор автора відповідної версії , але також можна сказати, що таке значення одночасно позначає роль грає залученого користувача в якості редактора в «оригінальній» версії у вигляді BlogStory .

Так, даність AuthorId значення може бути розділене кількома BlogStoryVersionрядками, але це насправді інформація, яка розповідає щось дуже важливе про кожну Версію , тому повторення зазначеної дати не є проблемою.

Формат стовпців DATETIME

Що стосується типу даних DATETIME, так, ви праві, " MySQL отримує та відображає значення DATETIME у YYYY-MM-DD HH:MM:SSформаті " ", але ви можете впевнено вводити відповідні дані таким чином, і коли вам потрібно виконати запит, вам просто потрібно скористайтеся вбудованими функціями DATE та TIME , щоб, серед іншого, показати відповідні значення у відповідному форматі для ваших користувачів. Або ви, безумовно, могли б здійснити подібне форматування даних за допомогою програм (-ів) програми.

Наслідки BlogStoryоперацій UPDATE

Кожен раз, коли BlogStoryрядок зазнає ОНОВЛЕННЯ, ви повинні це забезпечити що відповідні значення, які були "присутніми" до зміни, потім ВСТАВЛЯються в BlogStoryVersionтаблицю. Таким чином, я настійно пропоную виконати ці операції в рамках однієї ОСВІТИ КИСЛОТИ, щоб гарантувати, що до них відносяться як до неподільної одиниці роботи. Ви також можете використовувати TRIGGERS, але вони, як правило, роблять речі неохайними.

Представляємо a VersionId або VersionCodeколонку

Якщо ви вирішите (з огляду на бізнес-обставини чи особисті переваги), додайте стовпчик BlogStory.VersionIdабо BlogStory.VersionCodeстовпчик, щоб виділити це BlogStoryVersions , вам слід задуматися про наступні можливості:

  1. А VersionCode може вимагати бути УНІКАЛЬНИМ у (i) всій BlogStoryтаблиці, а також у (ii) BlogStoryVersion.

    Тому вам потрібно реалізувати ретельно перевірений та абсолютно надійний метод, щоб створити та призначити коженCode значення.

  2. Можливо, VersionCodeзначення можуть повторюватися в різних BlogStoryрядках, але ніколи не дублюватися разом з одними і тими жBlogStoryNumber . Наприклад, ви могли б мати:

    • BlogStoryNumber3 - версія83o7c5c і, одночасно,
    • BlogStoryNumber86 - версія83o7c5c та
    • BlogStoryNumber 958- версія83o7c5c .

Пізніша можливість відкриває ще одну альтернативу:

  1. Збереження VersionNumberдля BlogStories, так що можуть бути:

    • BlogStoryNumber 23- версії1, 2, 3… ;
    • BlogStoryNumber 650- версії1, 2, 3… ;
    • BlogStoryNumber 2254- версії1, 2, 3… ;
    • тощо.

Зберігання «оригінальної» та «наступної» версій в одній таблиці

Хоча підтримка всіх BlogStoryVersions в одній індивідуальній базовій таблиці можлива, я пропоную цього не робити, оскільки ви змішали б два різних (концептуальні) типи фактів, що, таким чином, має небажані побічні ефекти на

  • обмеження даних та маніпулювання (на логічному рівні) разом з
  • пов'язана обробка та зберігання (на фізичному рівні).

Але за умови, що ви вирішите слідувати цьому курсу дій, ви все ще можете скористатися багатьма ідеями, детально описаними вище, наприклад:

  • композит ПК , що складається з шпальти INT ( BlogStoryNumber) і стовпці DATETIME ( CreatedDateTime);
  • використання серверних функцій з метою оптимізації відповідних процесів та
  • Автор і редактор виведені Ролі .

Бачачи, що, продовжуючи такий підхід, BlogStoryNumberзначення буде дублюватися, як тільки будуть додані "новіші" версії , варіант, який і ви могли б оцінити (який дуже схожий на ті, що згадувались у попередньому розділі), встановлює BlogStoryПК що складається з колон BlogStoryNumberі VersionCode, таким чином , ви могли б однозначно ідентифікувати кожну версію у виді BlogStory . І ви можете спробувати з комбінацією BlogStoryNumberтаVersionNumber теж.

Аналогічний сценарій

Ви можете знайти мою відповідь на це питання допомоги, оскільки я також пропоную дозволити тимчасовим можливостям у відповідній базі даних вирішувати порівняний сценарій.


2

Одним із варіантів є використання версії Нормальна форма (vnf). До переваг можна віднести:

  • Поточні дані та всі минулі дані містяться в одній таблиці.
  • Цей же запит використовується для отримання поточних даних або даних, які були поточними станом на будь-яку конкретну дату.
  • Іноземні ключові посилання на перетворені дані працюють так само, як і для неперевершених даних.

Додаткова вигода у вашому випадку, оскільки переорієнтовані дані однозначно ідентифікуються шляхом введення в силу ключової дати (дати внесення змін) частиною ключа, окреме поле version_id не потрібно.

Ось пояснення дуже подібного типу сутності.

Більш детальну інформацію можна знайти в слайд-презентації тут і не зовсім завершеному документі тут


1

Ваші стосунки

(story_id, version_id, editor_id, author_id, мітка часу, назва, вміст, coverimg)

не в 3-й нормальній формі. Для кожної версії вашої історії автор_id однаковий. Тож для подолання цього вам потрібні два відносини

(story_id, author_id)
(story_id, version_id, editor_id, часова марка, назва, вміст, coverimg)

Ключ першого відношення - story_idключ другого відношення - комбінований ключ (story_id, version_id). Якщо вам не подобається комбінований ключ, то ви можете використовувати лише version_idяк ключ


2
Мабуть, це не вирішує мою проблему, вона просто підкреслює їх
Віктор,

Таким чином, воно навіть не відповідає запиту: author_id значення поля повторюється в кожному рядку таблиці. Де і як я повинен його зберігати ?
чудо173

2
Я не дуже розумію, що говорить ваша відповідь. Це може бути тому, що я не є носієм англійської мови, тому ви можете спробувати пояснити це більш простими словами, будь ласка?
Віктор

Це означає, що вам слід уникати повторення номеру author_id (якщо story_id дорівнює двом рядкам, їх автор також є рівним) і розділити вашу таблицю на дві таблиці, як описано в моєму дописі. Таким чином, ви можете уникнути повторення autor_id.
чудо173
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.