Veritabanı tasarımı: “arşiv” sorunu nasıl ele alınır?


18

Eminim ki birçok uygulama, kritik uygulama, banka vb. Bunu günlük olarak yapar.

Tüm bunların ardındaki fikir:

  • tüm satırların bir geçmişi olmalıdır
  • tüm bağlantılar tutarlı olmalıdır
  • "mevcut" sütunları almak için istekte bulunmak kolay olmalıdır
  • modası geçmiş şeyler satın alan müşteriler, bu ürün artık kataloğun bir parçası olmasa bile satın aldıklarını görmelidir.

ve bunun gibi.

İşte yapmak istediğim şey ve karşılaştığım sorunları açıklayacağım.

Tüm tablolarım şu sütunlara sahip olacak:

  • id
  • id_origin
  • date of creation
  • start date of validity
  • start end of validity

İşte CRUD operasyonları için fikirler:

  • create = id_origin= id, date of creation= şimdi, start date of validity= şimdi, end date of validity= null (= geçerli etkin kayıt olduğu anlamına gelir) ile yeni satır ekle
  • güncelleme =
    • read = end date of validity== null ile tüm kayıtları oku
    • "current" kaydını güncelle end date of validity= null end date of validity= ile
    • yeni değerlerle yeni bir tane oluşturun ve end date of validity= null (= geçerli etkin kayıt olduğu anlamına gelir)
  • delete = "geçerli" kaydı güncelle end date of validity= null end date of validity= şimdi ile

İşte benim sorunum: çoktan çoğa dernekler ile. Değerleri olan bir örnek verelim:

  • Tablo A (id = 1, id_origin = 1, başlangıç ​​= şimdi, bitiş = null)
  • Tablo A_B (başlangıç ​​= şimdi, bitiş = null, id_A = 1, id_B = 48)
  • Tablo B (id = 48, id_origin = 48, başlangıç ​​= şimdi, bitiş = null)

Şimdi A tablosunu güncellemek istiyorum, kayıt id = 1

  • İd = 1 değerini end = şimdi ile işaretliyorum
  • Benim ilişki A_B kaybettim damn ... masa A içine yeni bir değer eklemek ve sürece ben ilişkiyi çoğaltmak de ... Bu bir tabloya sona ereceğini:

  • Tablo A (id = 1, id_origin = 1, başlangıç ​​= şimdi, bitiş = şimdi + 8mn)

  • Tablo A (id = 2, id_origin = 1, başlangıç ​​= şimdi + 8mn, bitiş = null)
  • Tablo A_B (başlangıç ​​= şimdi, bitiş = null, id_A = 1, id_B = 48)
  • Tablo A_B (başlangıç ​​= şimdi, bitiş = null, id_A = 2, id_B = 48)
  • Tablo B (id = 48, id_origin = 48, başlangıç ​​= şimdi, bitiş = null)

Ve ... başka bir sorunum daha var: A_B ilişkisi: (id_A = 1, id_B = 48) kullanılmıyor mu yoksa işaretlemem mi (A - id = 1 kullanılmıyor, ancak B - 48 değil)?

Bununla nasıl başa çıkılır?

Bunu büyük ölçekte tasarlamalıyım: ürünler, ortaklar vb.

Bu konudaki deneyiminiz nedir? Nasıl yapardın (nasıl yaptın)?

-- Düzenle

Bu çok ilginç bir makale buldum , ancak "caso eskimiş" (= gerçekten istediğim) ile düzgün bir şekilde ilgilenmiyor


Güncelleme kaydının verilerini, id_hist_prev alanı ile geçmişin bağlantılı listesini tutarak yeni bir kimlikle güncellenmeden önce yenisine kopyalamaya ne dersiniz? Yani şimdiki kaydın kimliği asla değişmez

Tekerleği yeniden icat etmek yerine, örneğin Oracle'da Flashback Veri Arşivi'ni kullanmayı düşündünüz mü?
Jack Douglas

Yanıtlar:


4

Bu gereksinimlerin denetim amaçlı mı yoksa CRM ve alışveriş sepetleri gibi basit tarihsel referanslar için mi olduğu açık değildir.

Her iki durumda da, bunun gerekli olduğu her ana alan için bir ana ve main_archive tablosuna sahip olun. "Main" yalnızca geçerli / etkin girişlere sahip olurken "main_archive", ana sayfaya giren her şeyin bir kopyasına sahip olacaktır. Main_archive içine ekleme / güncelleme, insert / update'ten main'e tetikleyici olabilir. Main_archive'a karşı silmeler, daha uzun bir süre boyunca devam edebilir.

Cust X Ürünü satın aldığınız gibi referans sorunları için, cust_archive -> product_archive referans referans endişenizi çözmenin en kolay yolu, hiçbir zaman product_archive girişlerini silmemektir. Genellikle, karmaşa bu tabloda çok daha düşük olmalıdır, bu nedenle boyut bir endişe için çok kötü olmamalıdır.

HTH.


2
Harika bir yanıt, ancak bir arşiv tablosuna sahip olmanın başka bir faydasının, bu tür verilerle ilgili raporlamayı çok daha verimli hale getirerek normalleştirilme eğiliminde olmalarını eklemek isterim. Bu yaklaşımla başvurunuzun raporlama ihtiyaçlarını da göz önünde bulundurun.
maple_shaft

1
Tasarladığım veritabanlarının çoğunda, tüm 'ana' tablolar ürün adının bir önekine sahiptir LP_ve her önemli tablonun eşdeğeri vardır LH_, tetikleyiciler ekleme, güncelleme, silme üzerine geçmiş satırlar ekler. Her durumda işe yaramaz, ancak yaptığım şeyler için sağlam bir model olmuştur.

Kabul ediyorum - sorguların çoğunluğu "mevcut" satırlar içinse, muhtemelen akımı iki tabloya ayırarak mükemmel bir avantaj elde edersiniz. Bir görüş, onları kolaylık olarak bir araya getirebilir. Bu şekilde, geçerli satırlara sahip veri sayfaları bir arada olur ve muhtemelen önbellekte daha iyi kalır ve güncel mantıkla geçerli veriler için sorguları sürekli olarak nitelemenize gerek kalmaz.
onupdatecascade

1
@onupdatecascade: (en azından bazı RDBMS'lerde), bu UNIONgörünüme hem mevcut hem de geçmiş kayıtlarda benzersiz bir kısıtlama uygulamak gibi harika şeyler yapmanıza izin veren dizinler koyabileceğinizi unutmayın.
Tüm Ticaretten Jon

5 yıl sonra, tonlarca şey yaptım ve her zaman fikrini geri aldım. Değiştirdiğim tek şey, tarih tablolarında " id" ve " id_ref" sütunlarına sahip olmam . id_reftablonun gerçek fikrine referanstır. Örnek: personve person_h. içinde person_hben "var id" ve " id_ref" nerede id_ref'ilişkilidir person.id' Ben de aynı olan birçok satır olabilir böylece person.id(= bir satır ne zaman persondeğiştirilir) ve tüm id'hepsi benim tabloları s autoinc vardır.
Olivier Pons

2

Bu, fonksiyonel programlama ile bazı örtüşmelere sahiptir; özellikle değişmezlik kavramı.

Bir tablonuz var PRODUCT, diğeri denilen PRODUCTVERSIONveya benzeri Bir ürünü değiştirdiğinizde güncelleme yapmazsınız, sadece yeni bir PRODUCTVERSIONsatır eklersiniz. En son sürümü almak için, tabloyu sürüm numarasına (desc), zaman damgasına (desc) göre dizine ekleyebilir veya bir işaretiniz ( LatestVersion) kullanabilirsiniz.

Şimdi bir ürünü referans alan bir şeyiniz varsa, hangi tabloya işaret ettiğine karar verebilirsiniz. Varlığı PRODUCT(her zaman bu ürüne atıfta bulunur) veya PRODUCTVERSIONvarlığa (yalnızca ürünün bu sürümünü belirtir) işaret ediyor mu?

Karmaşık oluyor. Ürünün resimleri varsa ne olur? Sürüm tablosuna işaret etmeleri gerekir, çünkü değiştirilebilirler, ancak çoğu durumda, gereksiz yere verileri çoğaltmak istemezsiniz. Bu, bir PICTUREtabloya ve PRODUCTVERSIONPICTUREçoktan çoğa ilişkiye ihtiyacınız olduğu anlamına gelir .


1

Tüm tablolarımdaki 4 alanla buradan her şeyi uyguladım :

  • İD
  • date_creation
  • date_validity_start
  • date_validity_end

Bir kaydın her değiştirilmesi gerektiğinde, çoğaltırım, çoğaltılan kaydı "eski" = date_validity_end=NOW()ve geçerli kaydı iyi olarak işaretlerim date_validity_start=NOW()ve date_validity_end=NULL.

İşin püf noktası çoktan çoğa ve birden çokya ilişkidir: onlara dokunmadan çalışır! Her şey daha karmaşık sorgular hakkında: kesin bir tarihte bir sorgu (= şimdi değil), her birleştirme ve ana tablo için bu kısıtlamaları eklemek için var:

WHERE (
  (date_validity_start<=:dateparam AND date_validity_end IS NULL)
  OR
  (date_validity_start<=:dateparam AND date_validity_start>=:dateparam)
)

Yani ürün ve niteliklerle (çoktan çoğa ilişki):

SELECT p.*,a.*

FROM products p

JOIN products_attributes pa
ON pa.id_product = p.id
AND (
  (pa.date_validity_start<=:dateparam AND pa.date_validity_end IS NULL)
  OR
  (pa.date_validity_start<=:dateparam AND pa.date_validity_start>=:dateparam)
)

JOIN attributes a
ON a.id = pa.id_attribute
AND (
  (a.date_validity_start<=:dateparam AND a.date_validity_end IS NULL)
  OR
  (a.date_validity_start<=:dateparam AND a.date_validity_start>=:dateparam)
)

WHERE (
  (p.date_validity_start<=:dateparam AND p.date_validity_end IS NULL)
  OR
  (p.date_validity_start<=:dateparam AND p.date_validity_start>=:dateparam)
)

0

Buna ne dersin? Geçmişte yaptığım şeyler için basit ve oldukça etkili görünüyor. "Geçmiş" tablonuzda farklı bir PK kullanın. Dolayısıyla, "CustomerID" alanınız Müşteri tablonuzdaki PK'dir, ancak "geçmiş" tablosunda PK'nız "NewCustomerID" olur. "MüşteriNo" sadece salt okunur bir alan haline gelir. Bu, Geçmişte "MüşteriNo" nu değiştirmez ve tüm ilişkileriniz aynı kalır.


Çok güzel bir fikir. Yaptığım şey çok benzer: Kaydı çoğaltıyorum ve yeni kaydı "eski" olarak işaretliyorum, böylece mevcut kayıt hala aynı. Not Her tabloda bir tetikleyici oluşturmak istedim, ancak bu tablonun bir tetikleyicisine girdiğinizde mysql bir tablonun değiştirilmesini yasaklar. PostGRESQL bunu yapıyor. SQL sunucusu bunu yapar. Oracle bunu yapar. Kısacası MySQL'in hala çok uzun bir yolu var ve bir dahaki sefere veritabanı sunucumu seçerken iki kez düşüneceğim.
Olivier Pons
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.