Veritabanındaki bir kaydı nasıl denetleyebilirsiniz


177

Veritabanında bir kaydım olduğunu ve hem yönetici hem de normal kullanıcıların güncelleme yapabileceğini varsayalım.

Herkes, bu tablodaki her değişikliği nasıl kontrol edeceğinizi iyi bir yaklaşım / mimari önerebilir, böylece bir kaydı önceki bir revizyona geri almak mümkündür.

Yanıtlar:


164

FOOYönetici ve kullanıcıların güncelleyebileceği bir tablonuz olduğunu varsayalım . Çoğu zaman FOO tablosuna karşı sorgu yazabilirsiniz. Mutlu günler.

Sonra bir FOO_HISTORYtablo oluştururdum. Bu FOOtablonun tüm sütunları vardır . Birincil anahtar FOO artı bir RevisionNumber sütunu ile aynıdır. Bir yabancı anahtar vardır FOO_HISTORYiçin FOO. Düzeltmeyle ilgili UserId ve RevisionDate gibi sütunlar da ekleyebilirsiniz. RevisionNumbers'ı tüm *_HISTORYtablolarda (yani bir Oracle sekansından veya eşdeğerinden) sürekli artan bir şekilde doldurun . Saniyede sadece bir değişiklik olduğuna güvenmeyin (yani RevisionDatebirincil anahtara koymayın ).

Şimdi, her güncelleme FOOyaptığınızda, güncellemeyi yapmadan hemen önce eski değerleri eklersiniz FOO_HISTORY. Bunu tasarımınızda temel düzeyde yaparsınız, böylece programcılar yanlışlıkla bu adımı kaçırmazlar.

Bir satırı silmek FOOisterseniz bazı seçenekleriniz vardır. Tüm geçmişi basamaklandırın ve silin veya FOOsilinmiş olarak işaretleyerek mantıksal bir silme gerçekleştirin .

Bu çözüm, büyük ölçüde güncel değerlerle ve sadece zaman zaman tarihle ilgileniyorsanız iyidir. Her zaman geçmişe ihtiyacınız varsa, etkili başlangıç ​​ve bitiş tarihleri ​​koyabilir ve tüm kayıtları FOOkendi içinde tutabilirsiniz . Her sorgunun bu tarihleri ​​kontrol etmesi gerekir.


1
Veri erişim katmanınız doğrudan desteklemiyorsa, denetim tablosu güncellemesini veritabanı tetikleyicileri ile yapabilirsiniz. Ayrıca, sistem veri sözlüğünden içgözlem kullanan tetikleyicileri yapmak için bir kod oluşturucu oluşturmak zor değildir.
ConcernedOfTunbridgeWells

44
Ben woyuld aslında yeni veri, önceki değil eklemek öneririz , böylece geçmiş tablo tüm verileri vardır. Kırmızımsı verileri depolasa da, geçmiş veriler gerektiğinde her iki tabloda da arama yapmak için gereken özel durumları ortadan kaldırır.
Nerdfest

6
Şahsen hiçbir şeyi silmemenizi tavsiye ederim (bunu belirli bir temizlik faaliyetine erteleyin) ve ekleme / güncelleme / silme olup olmadığını belirtmek için bir "işlem türü" sütununa sahip olurum. Silme için satırı normal olarak kopyalarsınız, ancak işlem türü sütununa "sil" i koyarsınız.
Neil Barnwell

3
@Hydrargyrum Geçerli değerleri tutan bir tablo, tarihi tablodan daha iyi performans gösterir. Ayrıca, geçerli değerleri referans alan yabancı anahtarlar tanımlamak isteyebilirsiniz.
WW.

2
There is a foreign key from FOO_HISTORY to FOO': Kötü fikir, geçmişi değiştirmeden foo kayıtlarını silmek istiyorum. geçmiş tablosu normal kullanımda yalnızca eklenti olmalıdır.
Jasen

46

Ben veritabanı kayıtları içeriğini sürümleme arıyorsanız düşünüyorum (birisi bir soru / cevap düzenlediğinde StackOverflow yaptığı gibi). İyi bir başlangıç ​​noktası, revizyon izleme kullanan bazı veritabanı modellerine bakmak olabilir .

Akla gelen en iyi örnek Wikipedia motoru MediaWiki'dir. Burada veritabanı diyagramını , özellikle revizyon tablosunu karşılaştırın .

Hangi teknolojileri kullandığınıza bağlı olarak, iyi fark / birleştirme algoritmaları bulmanız gerekir.

.NET için bu soruyu kontrol edin .


30

BI dünyasında, sürümlendirmek istediğiniz tabloya bir startDate ve endDate ekleyerek bunu başarabilirsiniz. İlk kaydı tabloya eklediğinizde, startDate doldurulur, ancak endDate null olur. İkinci kaydı eklediğinizde, ilk kaydın endDate'ini ikinci kaydın startDate'i ile güncelleştirirsiniz.

Geçerli kaydı görüntülemek istediğinizde, endDate öğesinin boş olduğu kaydı seçersiniz.

Buna bazen tip 2 Yavaşça Değişen Boyut denir . Ayrıca bkz.


Bu yaklaşımı kullanarak masam oldukça büyümeyecek mi?
Niels Bosma

1
Evet, ancak tabloyu endeksleyerek ve / veya bölümleyerek bunu başarabilirsiniz. Ayrıca, sadece küçük bir avuç büyük masa olacak. Çoğu çok daha küçük olacak.
ConcernedOfTunbridgeWells

Yanılmıyorsam buradaki tek düşüş, değişiklikleri saniyede bir kez doğru olarak sınırlamasıdır?
pimbrouwers

@pimbrouwers evet, sonuçta alanların kesinliğine ve onları dolduran işleve bağlıdır.
Dave Neeley

9

SQL 2008'e yükseltin.

SQL 2008'de SQL Değişiklik İzlemeyi kullanmayı deneyin. Zaman damgası ve kaldırıldı olarak işaretleme sütunu kesmek yerine, veritabanınızdaki verilerdeki değişiklikleri izlemek için bu yeni özelliği kullanabilirsiniz.

MSDN SQL 2008 Değişiklik Takibi


7

Sadece bu soruna iyi bir çözüm eklemek için bir geçici veritabanı kullanmak istedim . Birçok veritabanı satıcısı bu özelliği ister hazır ister bir uzantı yoluyla sunar. Geçici tablo uzantısını PostgreSQL ile başarıyla kullandım ama diğerleri de var. Veritabanındaki bir kaydı güncellediğinizde, veritabanı bu kaydın önceki sürümüne de basılı tutulur.


6

İki seçenek:

  1. Geçmiş tablosuna sahip olun - orijinal veriler her güncellendiğinde eski verileri bu geçmiş tablosuna ekleyin.
  2. Denetim tablosu - öncesi ve sonrası değerleri - yalnızca denetim tablosundaki değiştirilen sütunlar için, kimin ne zaman güncelleneceği gibi diğer bilgilerle birlikte depolayın.

5

SQL tetikleyicileri aracılığıyla bir SQL tablosunda denetim gerçekleştirebilirsiniz. Bir tetikleyiciden 2 özel tabloya ( takılı ve silinmiş ) erişebilirsiniz . Bu tablolar, tablo her güncellendiğinde eklenen veya silinen satırları içerir. Tetikleyici SQL'de, bu değiştirilmiş satırları alıp denetim tablosuna ekleyebilirsiniz. Bu yaklaşım, denetiminizin programcı için şeffaf olduğu anlamına gelir; onlardan veya herhangi bir uygulama bilgisinden hiçbir çaba gerektirmez.

Bu yaklaşımın avantajı, sql işleminin veri erişim DLL'lerinizden mi yoksa manuel bir SQL sorgusu üzerinden mi gerçekleştiğinden bağımsız olarak denetimin gerçekleştirilmesidir; (denetim sunucunun kendisinde yapıldığından).


3

Hangi veritabanını söylemiyorsunuz ve yazı etiketlerinde görmüyorum. Oracle içinse, Designer'da yerleşik olan yaklaşımı önerebilirim: günlük tablolarını kullanın . Başka bir veritabanı içinse, temelde aynı şekilde tavsiye ederim ...

Başka bir DB'de çoğaltmak istediğinizde veya belki de sadece anlamak istiyorsanız, bir tablo için aynı alan özelliklerine sahip, sadece normal bir veritabanı tablosu da oluşturulmuş bir gölge tablosu olmasıdır. , artı bazı ek alanlar: en son yapılan işlem gibi (dize, ekleme için tipik "INS" değerleri, güncelleme için "UPD" ve silme için "DEL"), işlemin gerçekleştirildiği tarih için tarih ve kimin yaptığı kullanıcı kimliği o.

Tetikleyiciler aracılığıyla , tablodaki herhangi bir satıra yapılan her eylem, günlük tablosuna yeni değerler, hangi eylemin gerçekleştirildiği, ne zaman ve hangi kullanıcı tarafından yeni bir satır ekler. Hiçbir satırı silmezsiniz (en azından son birkaç aydır). Evet, büyük, kolayca milyonlarca satır büyüyecek, ancak günlük kaydının başlamasından veya eski günlük satırlarının en son temizlendiği ve son değişikliği kimin yaptığı zamandan beri herhangi bir zamanda herhangi bir kaydın değerini kolayca izleyebilirsiniz .

Oracle'da ihtiyacınız olan her şey otomatik olarak SQL kodu olarak oluşturulur, tek yapmanız gereken onu derlemek / çalıştırmaktır; ve temel bir CRUD uygulamasıyla birlikte gelir (aslında sadece "R").


2

Ben de aynı şeyi yapıyorum. Ders planları için bir veritabanı yapıyorum. Bu planlar atomik değişiklik versiyonlama esnekliğine ihtiyaç duyar. Başka bir deyişle, ders planlarında ne kadar küçük olursa olsun, her değişikliğe izin verilmesi gerekiyor, ancak eski sürümün de bozulmadan tutulması gerekiyor. Bu şekilde, ders oluşturucuları öğrenciler bunları kullanırken ders planlarını düzenleyebilir.

Çalışma şekli, bir öğrenci bir ders yaptıktan sonra, sonuçlarının tamamladıkları sürüme eklenmesidir. Bir değişiklik yapılırsa, sonuçları her zaman sürümlerini gösterecektir.

Bu şekilde, bir ders kriteri silinir veya taşınırsa, sonuçları değişmez.

Şu anda bunu yapıyorum yolu tüm verileri tek bir tablodaki işlemektir. Normalde sadece bir kimlik alanı olurdu, ama bu sistemde, bir id ve bir sub_id kullanıyorum. Alt_kimliği güncellemeler ve silmeler yoluyla her zaman satırda kalır. Kimlik otomatik olarak artırılır. Ders planı yazılımı en yeni alt_kime bağlanır. Öğrenci sonuçları kimliğe bağlanacaktır. Değişiklikler olduğunda izleme için bir zaman damgası da ekledim, ancak sürümlendirmeyi işlemek gerekli değil.

Değiştirebileceğim bir şey, test ettikten sonra, daha önce belirtilen endDate boş fikrini kullanabileceğimdir. Sistemimde, en yeni sürümü bulmak için, max (id) bulmak zorunda kalacak. Diğer sistem sadece endDate = null değerini arar. Faydaların başka bir tarih alanına sahip olup olmadığından emin değilim.

Benim görüşüm.


2

@WW. cevap iyi bir yanıttır Bir başka yol da bir sürüm sütunu yapmak ve tüm sürümlerinizi aynı tabloda tutmaktır.

Bir masa yaklaşımı için:

  • En son ala Word Press'i belirtmek için bir bayrak kullanın
  • VEYA sürümden daha kötü bir şey yapın outer join.

outer joinRevizyon numaralarını kullanan yöntemin örnek SQL'i :

SELECT tc.*
FROM text_content tc
LEFT OUTER JOIN text_content mc ON tc.path = mc.path
AND mc.revision > tc.revision
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- path in this case is our natural id.

Kötü haber yukarıdaki bir gerektirir outer joinve dış birleşimler yavaş olabilir. İyi haber şu ki, yeni girişler oluşturmak teorik olarak daha ucuzdur, çünkü işlemlerin dışında bir yazma işleminde (veritabanınızın atomik olduğu varsayılarak) yapabilirsiniz.

İçin yeni bir düzeltme yapan bir örnek şunlar '/stuff'olabilir:

INSERT INTO text_content (id, path, data, revision, revision_comment, enabled, create_time, update_time)
(
SELECT
(md5(random()::text)) -- {id}
, tc.path
, 'NEW' -- {data}
, (tc.revision + 1)
, 'UPDATE' -- {comment}
, 't' -- {enabled}
, tc.create_time
, now() 
FROM text_content tc
LEFT OUTER JOIN text_content mc ON tc.path = mc.path
AND mc.revision > tc.revision
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- {path}
)

Eski verileri kullanarak ekleriz. Bu, yalnızca bir sütunu güncellemek ve iyimser kilitleme ve / veya işlemlerden kaçınmak istediğinizi varsa özellikle yararlıdır.

Bayrak yaklaşımı ve geçmiş tablosu yaklaşımı iki satırın eklenmesini / güncellenmesini gerektirir.

outer joinRevizyon numarası yaklaşımının diğer avantajı, tetikleyicilerle daha sonra her zaman çoklu tablo yaklaşımına yeniden bakabilmenizdir, çünkü tetikleyiciniz esas olarak yukarıdaki gibi bir şey yapmalıdır.


2

Alok Audit tableyukarıda önerdi , yazımda açıklamak istiyorum.

Projemde bu şemadan bağımsız, tek masa tasarımını benimsedim.

Şema:

  • id - INTEGER OTOMATİK KURULUM
  • kullanıcı adı - STRING
  • tablename - STRING
  • eski değer - METİN / JSON
  • yeni değer - METİN / JSON
  • createdon - DATETIME

Bu tablo, her tablo için geçmiş kayıtları tek bir yerde tutabilir ve nesne geçmişini tek bir kayıtta tutabilir. Bu tablo, verilerin değiştiği tetikleyiciler / kancalar kullanılarak, hedef satırın eski ve yeni değer anlık görüntüsünü depolayarak doldurulabilir.

Bu tasarıma sahip profesyoneller:

  • Geçmiş yönetimi için yönetilecek daha az sayıda tablo.
  • Eski ve yeni durumların her birinin tam anlık görüntüsünü saklar.
  • Her masada arama yapmak kolaydır.
  • Tabloya göre bölüm oluşturabilir.
  • Tablo başına veri saklama politikasını tanımlayabilir.

Bu tasarım ile eksileri:

  • Sistemde sık değişiklik yapılması durumunda veri boyutu büyük olabilir.
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.