MySQL ile versiyonlama sistemi uygulamak


15

Bunun burada ve burada sorulduğunu biliyorum , ancak farklı bir olası uygulama ile aynı fikrim var ve yardıma ihtiyacım var.

Başlangıçta blogstoriesbu yapıya sahip masam vardı :

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

Blogdaki her hikaye için bazı versiyonlama sistemi uygulamak istediğime karar verdikten sonra, aklıma gelen ilk şey, düzenlemeleri tutmak için farklı bir tablo oluşturmaktı ; Bundan sonra, mevcut tabloyu düzenlemeler yerine sürümleri tutacak şekilde değiştirebileceğimi düşündüm . Aklıma gelen yapı bu:

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

Buraya gelmem nedenleri:

  • uidBaşlangıç tablo alanı tablosundaki benzersiz. Şimdi, story_idartık benzersiz değil. Bununla nasıl başa çıkmalıyım? ( story_id = xSon sürümü bulabileceğimi ve sonra bulabileceğimi düşündüm , ancak bu çok kaynak tüketiyor gibi görünüyor, bu yüzden lütfen tavsiyenizi verin)
  • author_idalan değeri tablonun her satırında yinelenir. Nerede ve nasıl saklamalıyım?

Düzenle

Benzersiz kod oluşturma süreci şu CreateUniqueCodeişlevdedir:

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

Kod yazılır Hack ve aslen tarafından PHP yazılmış @Scott onun içinde cevap .

Alanlar author_idve farklı editor_id olabilir , çünkü herhangi birinin hikayelerini düzenlemek için yeterli izne sahip kullanıcılar vardır.

Yanıtlar:


23

Sunar özellikleri olarak bilinen konuyla ilgili -ki senaryoyu analiz zamansal veri tabanları - kavramsal açısından bakıldığında, bir tespit edilebilir: (a) bir “mevcut” Öyküsü Version Blog ve (b), bir “son” Blog Öyküsü şekli , çok rağmen benzeyen, farklı tipte varlıklardır.

Buna ek olarak, mantıksal soyutlama düzeyinde çalışırken, farklı türlerin gerçekleri (satırlarla temsil edilir) farklı tablolarda tutulmalıdır. Söz konusu durumda, oldukça benzer olsa bile, (i) “mevcut” Sürümler hakkındaki gerçekler (ii) “Geçmiş” Sürümler hakkındaki gerçeklerden farklıdır .

Bu nedenle durumu iki tablo aracılığıyla yönetmenizi öneririm:

  • “geçerli” veya “mevcut” için özel olarak ayrılmış bir Sürümleri ait blog Hikayeler ve

  • biri "önceki" veya "geçmiş" Sürümler için ayrı ancak diğeriyle bağlantılı ;

her biri (1) biraz farklı sütunlara ve (2) farklı bir kısıtlama grubuna sahiptir.

Kavramsal katmana geri dönersek - iş ortamınızda - Yazar ve Editör'ün , bir Kullanıcı tarafından oynanabilecek Roller olarak tanımlanabilecek kavramlar olduğunu ve bu önemli yönlerin veri türüne (mantıksal düzey manipülasyon işlemleri yoluyla) bağlı olduğunu düşünüyorum. ve yorumlama ( bir veya daha fazla uygulama programının yardımı ile Blog Hikayeleri okuyucuları ve yazarları tarafından bilgisayarlı bilgi sisteminin dış seviyesinde gerçekleştirilir).

Tüm bu faktörleri ve diğer ilgili noktaları aşağıdaki gibi detaylandıracağım.

İş kuralları

Gereksinimlerinizi anladığım kadar, aşağıdaki iş kuralları formülasyonları (ilgili varlık türleri ve bunların karşılıklı ilişki türleri açısından bir araya getirilir), ilgili kavramsal şemanın oluşturulmasında özellikle yardımcı olur :

  • Bir Kullanıcı sıfır bir veya birden fazla Blog Yazısı yazar
  • Bir BlogStory sıfır veya bir veya daha fazla BlogStoryVersions tutar
  • Bir Kullanıcı sıfır bir veya birçok BlogStoryVersions yazdı

Açıklayıcı IDEF1X diyagramı

Sonuç olarak, bir grafik aleti ile benim öneri açıklamak amacıyla, bir örnek IDEF1X oluşturduk bir ilgili gibi iş üzerinde formüle kuralları ve diğer özellikler elde edilir diyagramıdır. Şekil 1'de gösterilmiştir :

Şekil 1 - Blog Hikayesi Sürümleri IDEF1X Diyagramı

BlogStory ve BlogStoryVersion neden iki farklı varlık türü olarak kavramsallaştırılıyor?

Çünkü:

  • Bir BlogStoryVersion örneği (yani, “geçmiş” bir örnek) her zaman UpdatedDateTime özelliği için bir değer tutarken, BlogStory oluşumu (yani, “mevcut” bir örnek) hiçbir zaman bunu tutmaz .

  • Ayrıca, bu türlerin varlıkları iki farklı özellik kümesinin değerleri ile benzersiz olarak tanımlanır: BlogStoryNumber ( BlogStory olayları durumunda ) ve BlogStoryNumber artı CreatedDateTime ( BlogStoryVersion örnekleri durumunda ).


a Bilgi Modelleme için Entegrasyon Tanımı ( IDEF1X ),Aralık 1993'te Amerika Birleşik Devletleri Ulusal Standartlar ve Teknoloji Enstitüsü (NIST)tarafından standart olarak oluşturulmuş, oldukça tavsiye edilen bir veri modelleme tekniğidir. Erken teorik malzemeye dayanmaktadır tarafından kaleme yegane yaratıcısı ait ilişkisel modeli yani, Dr EF Codd ; üzerinde Varlık-İlişki tarafından geliştirilen veriler, görüş Dr. PP Chen ; ve ayrıca Robert G. Brown tarafından oluşturulan Mantıksal Veritabanı Tasarım Tekniği.


Açıklayıcı mantıksal SQL-DDL düzeni

Daha sonra, daha önce sunulan kavramsal analize dayanarak, aşağıdaki mantıksal seviyedeki tasarımı beyan ettim:

-- 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 üzerinde çalışan bu SQL Fiddle'da test edilmiştir .

BlogStorytablo

Demo tasarımında da görebileceğiniz gibi BlogStory, INT veri tipiyle PRIMARY KEY (kısaltma için PK) sütununu tanımladım . Bu bağlamda, her satır eklemesinde böyle bir sütun için sayısal bir değer üreten ve atayan yerleşik bir otomatik işlemi düzeltmek isteyebilirsiniz. Bu değer kümesinde zaman zaman boşluk bırakmayı düşünmezseniz , MySQL ortamlarında yaygın olarak kullanılan AUTO_INCREMENT özniteliğini kullanabilirsiniz .

Tüm bireysel BlogStory.CreatedDateTimeveri noktalarınızı girerken , veritabanı sunucusunda geçerli olan Tarih ve Saat değerlerini tam INSERT işlem anında döndüren NOW () işlevini kullanabilirsiniz . Bana göre, bu uygulama kesinlikle harici rutinlerin kullanımından daha uygun ve hatalara daha az eğilimlidir.

(Artık kaldırılmış) yorumlarda tartışıldığı gibi, BlogStory.Titleyinelenen değerleri koruma olasılığından kaçınmak istemeniz koşuluyla, bu sütun için bir UNIQUE kısıtlaması ayarlamanız gerekir . Bir arama sonuçları gerçeğine Başlık birkaç (hatta tüm) “Geçtiğimiz” tarafından paylaşılabilir BlogStoryVersions ardından bir UNIQUE gerekir değil için kurulacak BlogStoryVersion.Titlesütununda.

I dahil BlogStory.IsActivetipi kolon BIT (1) , (a-se Tinyint Eğer “yumuşak” ya da “mantıklı” SİL işlevselliği sağlamak için ihtiyaç halinde de kullanılabilir).

BlogStoryVersionTablo hakkında detaylar

Öte yandan, BlogStoryVersiontablonun PK'si, (a) BlogStoryNumberve (b) adında bir sütundan oluşur CreatedDateTime, elbette, bir BlogStorysatırın bir INSERT geçirdiği kesin anı işaretler .

BlogStoryVersion.BlogStoryNumber, PK'nın bir parçası olmasının yanı sıra, bu iki tablonun satırları arasında referans bütünlüğünüBlogStory.BlogStoryNumber zorunlu kılan bir yapılandırma olan , referans veren bir YABANCI ANAHTAR (FK) olarak kısıtlanır . Bu bağlamda, otomatik bir a neslinin uygulanması gerekli değildir, çünkü FK olarak ayarlandığında, bu sütuna EKLENEN değerler ilgili muadilinde zaten bulunanlardan “alınmalıdır” .BlogStoryVersion.BlogStoryNumberBlogStory.BlogStoryNumber

BlogStoryVersion.UpdatedDateTimeKolon, beklendiği gibi, muhafaza etmelidir, bir zaman noktası BlogStorysatır ilave bir sonucu olarak, modifiye edilmiş ve BlogStoryVersiontablo. Bu nedenle, bu durumda NOW () işlevini de kullanabilirsiniz.

Aralıklı arasında kavramış BlogStoryVersion.CreatedDateTimeve BlogStoryVersion.UpdatedDateTimetüm ifade Dönemi bir süre boyunca BlogStorysatır “mevcut” veya “geçerli” oldu.

Bir Versionsütun için dikkat edilmesi gerekenler

Bir BlogStory'ninBlogStoryVersion.CreatedDateTime belirli bir “geçmiş” Sürümünü temsil eden değeri tutan sütun olarak düşünmek yararlı olabilir . Bunu bir veya çok daha faydalı görüyorum , çünkü insanların zaman kavramlarına daha aşina olmaları açısından kullanıcı dostudur. Örneğin, blog yazarları veya okuyucular bir BlogStoryVersion'a aşağıdakine benzer bir şekilde başvurabilir :VersionIdVersionCode

  • “Ben özgü görmek istiyorum Versiyon ait BlogStory tarafından tespit Number 1750 edildi düzenlendi üzerine 26 August 2015de 9:30”.

Yazar ve Editör Roller: Veri türetme ve yorumlanması

Bu yaklaşımla, kolayca “orijinal” kimin elinde olduğunu ayırt edebilir AuthorIdbir betonun BlogStory “en erken” seçerek Sürüm belli bir BlogStoryIdGELEN BlogStoryVersionuygulayarak sayesinde masanın MİN () fonksiyonu için BlogStoryVersion.CreatedDateTime.

Bu şekilde, tüm "daha sonraki" veya "başarılı" Sürüm satırlarında BlogStoryVersion.AuthorIdbulunan her değer , doğal olarak, eldeki ilgili Sürümün Yazar tanımlayıcısını gösterir , ancak böyle bir değerin aynı zamanda, Rol dahil oynadığı Kullanıcı olarak Editor “orijinal” nin Version a BlogStory .

Evet, belirli bir AuthorIddeğer birden çok BlogStoryVersionsatır tarafından paylaşılabilir , ancak bu aslında her Sürüm için çok önemli bir şey söyleyen bir bilgi parçasıdır , bu nedenle söz konusu verilerin tekrarlanması bir sorun değildir .

DATETIME sütunlarının biçimi

DATETIME veri türüne gelince, evet, haklısınız, “ MySQL, DATETIME değerlerini ' YYYY-MM-DD HH:MM:SS' biçiminde alır ve görüntüler ”, ancak ilgili verileri güvenle bu şekilde girebilirsiniz ve bir sorgu gerçekleştirmeniz gerektiğinde diğer şeylerin yanı sıra, ilgili değerleri kullanıcılarınız için uygun biçimde göstermek için yerleşik DATE ve TIME işlevlerinden yararlanın. Ya da kesinlikle bu tür veri biçimlendirmelerini uygulama program (lar) ınız koduyla yapabilirsiniz.

Etkileri BlogStoryGÜNCELLEME operasyonları

Her BlogStorysatırda bir GÜNCELLEME olduğunda , değişiklik yapılana kadar “mevcut” karşılık gelen değerlerin BlogStoryVersiontabloya eklenmesini sağlamalısınız . Bu nedenle, bölünmez bir İş Birimi olarak muamele görmelerini garanti etmek için bu operasyonları tek bir ASİT İŞLEMİ içinde gerçekleştirmenizi şiddetle tavsiye ederim . TRIGGERS'ı da kullanabilirsiniz, ancak tabiri caizse, işleri düzensiz yapma eğilimindedirler.

Bir VersionIdveya VersionCodesütuna giriş

BlogStoryVersions'ı ayırt etmek için (iş koşulları veya kişisel tercih nedeniyle) bir BlogStory.VersionIdveya BlogStory.VersionCodesütun eklemeyi seçerseniz , aşağıdaki olasılıkları düşünmeniz gerekir:

  1. A'nın VersionCode(i) tüm BlogStorytabloda ve ayrıca (ii) 'de EŞSİZ olması gerekebilir BlogStoryVersion.

    Bu nedenle, her bir değeri oluşturmak ve atamak için dikkatle test edilmiş ve tamamen güvenilir bir yöntem uygulamanız gerekir Code.

  2. Belki, VersionCodedeğerler farklı BlogStorysatırlarda tekrarlanabilir , ancak asla aynı ile tekrarlanamaz BlogStoryNumber. Örneğin:

    • bir BlogStoryNumber 3- Sürüm83o7c5c ve aynı anda
    • Bir BlogStoryNumber 86- Sürüm83o7c5c ve
    • Bir BlogStoryNumber 958- Sürüm83o7c5c .

Daha sonraki olasılık başka bir alternatif açar:

  1. Bir tutulması VersionNumberiçin BlogStories, bu yüzden söz konusu olabilir:

    • BlogStoryNumber 23- Sürümler1, 2, 3… ;
    • BlogStoryNumber 650- Sürümler1, 2, 3… ;
    • BlogStoryNumber 2254- Sürümler1, 2, 3… ;
    • vb.

“Orijinal” ve “sonraki” sürümleri tek bir tabloda tutma

Tüm muhafaza, ancak BlogStoryVersions olarak aynı ayrı temel tablo mümkündür, böylece istenmeyen yan etkileri vardır gerçekler, iki farklı (kavramsal) türleri karıştırma olacaktır, çünkü bunu yapmak için tavsiye

  • veri kısıtlamaları ve manipülasyonu (mantıksal düzeyde) ile birlikte
  • ilgili işleme ve depolama (fiziksel katmanda).

Ancak, bu hareket tarzını izlemeyi seçmeniz durumunda, yukarıda ayrıntılı olarak verilen fikirlerin çoğundan yararlanabilirsiniz, örneğin:

  • bir INT sütunu ( ) ve bir DATETIME sütunu ( ) içeren bir bileşik PK ;BlogStoryNumberCreatedDateTime
  • ilgili işlemleri optimize etmek için sunucu işlevlerinin kullanımı ve
  • Yazar ve Editör türetilebilir Rolleri .

Böyle bir yaklaşıma devam ederek, "daha yeni" Sürümler eklenir eklenmez bir BlogStoryNumberdeğerin çoğaltılacağını görerek, değerlendirebileceğiniz ve (önceki bölümde bahsedilenlere çok benzer) bir seçenek PK oluşturuyor sütunlardan oluşur ve bu şekilde bir BlogStory'nin her bir Sürümünü benzersiz bir şekilde tanımlayabilirsiniz . Ve bir kombinasyonu ile deneyebilir ve çok.BlogStoryBlogStoryNumberVersionCodeBlogStoryNumberVersionNumber

Benzer senaryo

Bu yardım sorusuna cevabımı bulabilirsiniz , çünkü ilgili veritabanındaki geçici yeteneklerin karşılaştırılabilir bir senaryo ile başa çıkmasını öneriyorum .


2

Seçeneklerden biri Sürüm Normal Formunu (vnf) kullanmaktır. Avantajları:

  • Geçerli veriler ve geçmiş tüm veriler aynı tabloda bulunur.
  • Aynı sorgu, geçerli verileri veya belirli bir tarih itibariyle güncel olan verileri almak için kullanılır.
  • Sürümlendirilmiş verilere yapılan yabancı anahtar referansları, sürümlendirilmemiş verilerle aynı şekilde çalışır.

Durumunuzda ek bir avantaj, sürüm tarihi verileri anahtarın yürürlük tarihi (değişikliğin yapıldığı tarih) bir parçası haline getirilerek benzersiz bir şekilde tanımlandığından, ayrı bir version_id alanı gerekli değildir.

İşte çok benzer bir varlık türü için bir açıklama.

Daha fazla ayrıntıyı burada bir slayt sunumunda ve tam olarak tamamlanmamış bir belgede bulabilirsiniz.


1

İlişkiniz

(story_id, version_id, editor_id, author_id, zaman damgası, başlık, içerik, coverimg)

3. normal formda değil. Hikayenizin her sürümü için author_id aynıdır. Bunun üstesinden gelmek için iki ilişkiye ihtiyacınız var

(hikaye_kimliği, yazar_kimliği)
(hikaye_kimliği, sürüm_kimliği, editör_kimliği, zaman damgası, başlık, içerik, coverimg)

İlk ilişkinin story_idanahtarı, ikinci ilişkinin anahtarı birleşik anahtardır (story_id, version_id). Birleşik anahtarı sevmiyorsanız, yalnızca version_idanahtar olarak kullanabilirsiniz


2
Bu benim sorunumu çözmüyor gibi görünüyor, sadece onları vurgular
Victor

Bu nedenle author_id , tablonun her satırında alan değeri tekrarlanıyor sorgusunu bile yanıtlamaz . Nerede ve nasıl saklamalıyım ?
miracle173

2
Cevabınızın ne ifade ettiğini gerçekten anlamıyorum. Ben anadili İngilizce olmayan biri olmadığım için olabilir, bu yüzden daha basit ve basit kelimelerle anlatmaya çalışabilir misiniz?
Victor

Bu, author_id numarasının tekrarından kaçınmanız gerektiği anlamına gelir (story_id iki satır için eşitse, author_id de eşittir) ve tablonuzu yazımda açıklandığı gibi iki tabloya ayırın. Böylece author_id tekrarını önleyebilirsiniz.
miracle173
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.