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

提示特性として知られている被写体に関連付け-whichシナリオ分析時間データベース -概念的な観点からは、一方が決定することができる:()「現在」ストーリー版ブログ及び(b)は、「過去」ブログストーリー版、非常にものの似ている、さまざまな種類のエンティティです。

それに加えて、抽象化の論理レベルで作業する場合、異なる種類のファクト(行で表される)を異なるテーブルに保持する必要があります。検討中のケースでは、非常に類似していても、(i)「現在の」バージョンに関する事実は、(ii)「過去の」バージョンに関する事実とは異なります。

したがって、2つのテーブルを使用して状況を管理することをお勧めします。

  • 「現在の」または「存在」のために専用に1 バージョンブログストーリー、および

  • すべての「前」または「過去」のために、別々のですが、また他とリンク1 バージョン

それぞれ(1)わずかに異なる列数と(2)異なる制約グループを持ちます。

概念層に戻ると、ビジネス環境では、作成者編集者ユーザーが再生できるロールとして描写できる概念であり、これらの重要な側面はデータの派生に依存すると考えています(論理レベルの操作操作を介して)および解釈(1つまたは複数のアプリケーションプログラムの支援を受けて、コンピューター化された情報システムの外部レベルでBlog Storiesリーダーおよびライターが実行)。

これらすべての要因とその他の関連するポイントを以下に詳述します。

ビジネスルール

お客様の要件を理解した上で、次のビジネスルールの定式化(関連するエンティティタイプとそれらの種類の相互関係の観点からまとめる)は、対応する概念スキーマの確立に特に役立ちます。

  • ユーザーは、ゼロ・ワン・オア・多くの書き込みBlogStoriesを
  • A BlogStoryはゼロ-1-または-多く保持しているBlogStoryVersionsを
  • ユーザーは、ゼロ・ワン・オア・多くの書いたBlogStoryVersionsを

説明的なIDEF1X図

したがって、グラフィカルデバイスを使用して提案を説明するために、上記で定式化されたビジネスルールおよび関連すると思われるその他の機能から派生しダイアグラムIDEF1X サンプルを作成しました。図1に示します

図1-ブログストーリーバージョンIDEF1X図

BlogStoryBlogStoryVersionが2つの異なるエンティティタイプとして概念化されるのはなぜですか?

なぜなら:

  • A BlogStoryVersionのインスタンス(すなわち、「過去」1)は、常にのための値を保持UpdatedDateTimeの一方で、プロパティをBlogStory発生(すなわち、「現在」1)がそれを保持していることはありません。

  • また、これらのタイプのエンティティは、BlogStoryNumberBlogStoryオカレンスの場合)、およびBlogStoryNumberCreatedDateTimeBlogStoryVersionインスタンスの場合)の2つの異なるプロパティセットの値によって一意に識別されます。


インフォメーションモデリングのための統合の定義 IDEF1Xは)として設立された非常にお勧めのデータモデリング技法である標準米国の1993年12月における米国国立標準技術研究所(NIST)。それは著早い理論的な材料に基づいている唯一の発信元リレーショナルモデル、すなわち、博士はEFコッド。上のエンティティリレーションシップによって開発されたデータのビュー、博士PP・チェン。また、Robert G. Brownが作成したLogical Database Design Techniqueについても。


例示的な論理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.
);

MySQL 5.6で実行されるこのSQL Fiddleでテスト済み。

BlogStoryテーブル

デモデザインでわかるようにBlogStory、INTデータ型でPRIMARY KEY(簡潔にするためにPK)列を定義しました。この点で、各行の挿入でそのような列の数値を生成して割り当てる組み込みの自動プロセスを修正することができます。この値のセットに時折ギャップを残しても構わない場合は、MySQL環境で一般的に使用されるAUTO_INCREMENT属性を使用できます。

個々のBlogStory.CreatedDateTimeデータポイントをすべて入力する場合、NOW()関数を使用して、正確なINSERT操作の瞬間にデータベースサーバーで現在の日付と時刻の値を返します。私にとって、この方法は外部ルーチンを使用するよりも明らかに適切であり、エラーが発生しにくいです。

(削除された)コメントで説明したように、BlogStory.Title重複する値を保持する可能性を避けたい場合は、この列にUNIQUE制約を設定する必要があります。特定のタイトルが複数の(またはすべての)「過去の」BlogStoryVersionsで共有される可能性があるため、列に対してUNIQUE制約を確立しないでくださいBlogStoryVersion.Title

「ソフト」または「論理」DELETE機能を提供する必要がある場合に備えて、BIT(1)BlogStory.IsActive型の列を含めました(TINYINTも使用できます)。

BlogStoryVersionテーブルの詳細

一方、BlogStoryVersionテーブルのPKは、(a)BlogStoryNumberと(b)CreatedDateTimeもちろんBlogStory列がINSERTを行った正確な瞬間をマークするという名前の列で構成されます。

BlogStoryVersion.BlogStoryNumberは、PKの一部であることに加えて、これらの2つのテーブルの行間で参照整合性BlogStory.BlogStoryNumberを強制する構成を参照する外部キー(FK)としても制約されます。この点で、aの自動生成を実装する必要はありません。FKとして設定されるため、この列に挿入される値は、関連するカウンターパートに既に含まれている値から「引き出す」必要があるからです。BlogStoryVersion.BlogStoryNumberBlogStory.BlogStoryNumber

BlogStoryVersion.UpdatedDateTimeカラムは予想通り、保持する必要があり、時点BlogStory行が追加、結果として、修正されたBlogStoryVersionテーブル。したがって、この状況でもNOW()関数を使用できます。

間隔の間に理解BlogStoryVersion.CreatedDateTimeBlogStoryVersion.UpdatedDateTime全体の発現期間、その間BlogStoryの行が「有り」または「現在の」であったが。

Version列に関する考慮事項

考えることが有用でできるBlogStoryVersion.CreatedDateTime特定の「過去」を表す値を保持列としてバージョンBlogStoryを。これはa VersionIdVersionCodeに比べてはるかに有益だと思います。なぜなら、人々は時間の概念に精通しているという意味で、ユーザーにとって使いやすいからです。たとえば、ブログの作成者または読者は、次のような方法でBlogStoryVersionを参照できます。

  • 「私は特定の見たいバージョンBlogStoryで識別番号 1750れた作成された26 August 2015での9:30」。

著者編集者の役割:データ導出および解釈

このアプローチを使用すると、MIN()関数をに適用することで、特定のFROMの「最も古い」バージョンを選択AuthorIdする具体的なBlogStoryの「オリジナル」を誰が持っているかを簡単に区別できます。BlogStoryIdBlogStoryVersionBlogStoryVersion.CreatedDateTime

このように、すべての「後」または「後続」バージョン行にBlogStoryVersion.AuthorId含まれる各値は、当然、手元の各バージョンの作成者識別子を示しますが、そのような値は、同時に、役割関与によって演じユーザーとしてエディタ「オリジナル」のバージョンBlogStory

はい、特定のAuthorId値は複数のBlogStoryVersion行で共有できますが、これは実際には各Versionについて非常に重要なことを伝える情報の一部であるため、前述のデータの繰り返しは問題ではありません

DATETIME列の形式

DATETIMEデータ型については、そうです、「MySQLはDATETIME値を ' YYYY-MM-DD HH:MM:SS'形式で取得および表示します」が、この方法で適切なデータを自信を持って入力でき、クエリを実行する必要がある場合は組み込みのDATEおよびTIME関数を使用して、とりわけ、ユーザーに適切な形式で関連する値を表示します。または、アプリケーションプログラムコードを介してこの種のデータフォーマットを確実に実行できます。

BlogStoryUPDATE操作の意味

たびにBlogStory行がUPDATEを受けるには、あなたは確認する必要があります変更が行われたまでは「存在」して対応する値は、その後に挿入されていることをBlogStoryVersion表。したがって、これらの操作を単一のACID TRANSACTION内で実行して、不可分な作業単位として扱われることを保証することを強くお勧めします。TRIGGERSを採用することもできますが、いわば物事を乱雑にする傾向があります。

VersionIdまたはVersionCode列の紹介

BlogStoryVersionsを区別するためにBlogStory.VersionIdor BlogStory.VersionCodeカラムを組み込むことを(ビジネス環境または個人的な好みのために)選択する場合、次の可能性を検討する必要があります。

  1. A VersionCodeは(i)BlogStoryテーブル全体と(ii)で一意である必要がありますBlogStoryVersion

    したがって、各値を生成して割り当てるには、慎重にテストされた完全に信頼できる方法を実装する必要がありCodeます。

  2. たぶん、VersionCode値は異なるBlogStory行で繰り返される可能性がありますが、同じとともに複製されることはありませんBlogStoryNumber。たとえば、次のものがあります。

    • BlogStoryNumber 3-バージョン83o7c5c同時と、
    • BlogStoryNumber 86-バージョン83o7c5c
    • BlogStoryNumber 958-バージョン83o7c5c

後者の可能性は別の選択肢を開きます:

  1. を保持するVersionNumberためBlogStories、次のようになります。

    • BlogStoryNumber- 23バージョン1, 2, 3…
    • BlogStoryNumber- 650バージョン1, 2, 3…
    • BlogStoryNumber- 2254バージョン1, 2, 3…

単一のテーブルに「オリジナル」バージョンと「後続」バージョンを保持する

すべてのBlogStoryVersions同じ個別のベーステーブルで維持することは可能ですが、2つの異なる(概念)タイプのファクトを混合するため、これを行わないことをお勧めします。

  • データの制約と操作(論理レベル)、および
  • 関連する処理およびストレージ(物理層)。

ただし、その行動方針に従うことを選択した場合は、上記で説明した多くのアイデアを活用できます。例:

  • 複合 INTカラム(成るPK BlogStoryNumber)およびDATETIMEカラム(CreatedDateTime)。
  • 関連するプロセスを最適化するためのサーバー機能の使用
  • 著者編集者誘導の役割

このようなアプローチを進めると、「新しい」バージョンが追加されるとすぐにBlogStoryNumber値が複製され、評価できるオプション(前のセクションで説明したものと非常に似ている)がPKを確立していることがわかります。列で構成して、このようにあなたは一意に識別することができるだろうバージョンBlogStoryを。そして、あなたはの組み合わせで試すことができますし、あまりにも。BlogStoryBlogStoryNumberVersionCodeBlogStoryNumberVersionNumber

同様のシナリオ

このヘルプの質問に対する私の答えを見つけるかもしれません。私は、同様のシナリオに対処するために、関係するデータベースの一時的な機能を有効にすることも提案しているからです。


2

1つのオプションは、バージョン標準形式(vnf)を使用することです。利点は次のとおりです。

  • 現在のデータと過去のすべてのデータは同じテーブルにあります。
  • 同じクエリを使用して、現在のデータまたは特定の日付の時点で最新のデータを取得します。
  • バージョン管理されたデータへの外部キー参照は、バージョン管理されていないデータと同じように機能します。

有効な日付(変更が行われた日付)をキーの一部にすることでバージョン対応データが一意に識別されるため、別のversion_idフィールドは必要ありません。

これは、非常によく似たタイプのエンティティの説明です。

詳細については、スライドプレゼンテーションをご覧ください。完成していないドキュメントはこちらをご覧ください。


1

あなたの関係

(story_id、version_id、editor_id、author_id、timestamp、title、content、coverimg)

第3正規形ではありません。ストーリーのすべてのバージョンで、author_idは同じです。したがって、これを克服するには2つの関係が必要です

(story_id、author_id)
(story_id、version_id、editor_id、timestamp、title、content、coverimg)

最初の関係story_idのキーは、2番目の関係のキーは結合キー(story_id, version_id)です。複合キーが気に入らない場合はversion_id、キーとしてのみ使用できます


2
これは私の問題を解決していないようで、単に強調しているだけです。
ビクター

そのため、クエリには答えさえしません ' author_id フィールド値はテーブルの各行で繰り返されています。どこに、どのように保管すればよいですか?
奇跡173

2
私はあなたの答えが何を言っているのか本当に理解していません。私は英語が母国語ではないからかもしれませんが、もっと簡単な言葉で説明してください。
ビクター

つまり、author_id番号の繰り返しを避け(story_idが2行で等しい場合、author_idも等しい)、私の投稿で説明されているように2つのテーブルにテーブルを分割する必要があります。したがって、author_idの繰り返しを避けることができます。
奇跡173
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.