To subtelne.
Jeśli wymaganiem biznesowym jest „Chcę audytować zmiany w danych - kto co zrobił i kiedy?”, Zwykle można użyć tabel audytu (zgodnie z przykładem wyzwalacza opublikowanym przez Keethanjan). Nie jestem wielkim fanem wyzwalaczy, ale ma tę wielką zaletę, że jest stosunkowo bezbolesny w implementacji - Twój istniejący kod nie musi wiedzieć o wyzwalaczach i kontrolach.
Jeśli wymaganiem biznesowym jest „pokaż mi, jaki był stan danych w danym dniu w przeszłości”, oznacza to, że aspekt zmiany w czasie wszedł do Twojego rozwiązania. Chociaż możesz po prostu zrekonstruować stan bazy danych, po prostu patrząc na tabele audytu, jest to trudne i podatne na błędy, a dla każdej skomplikowanej logiki bazy danych staje się nieporęczne. Na przykład, jeśli firma chce wiedzieć, „znajdź adresy listów, które powinniśmy byli wysłać do klientów, którzy mieli zaległe, niezapłacone faktury pierwszego dnia miesiąca”, prawdopodobnie będziesz musiał przeszukać pół tuzina tabel audytowych.
Zamiast tego możesz wprowadzić koncepcję zmiany w czasie do projektu schematu (jest to druga opcja sugerowana przez Keethanjan). Jest to zmiana w Twojej aplikacji, zdecydowanie na poziomie logiki biznesowej i trwałości, więc nie jest to trywialne.
Na przykład, jeśli masz taki stół:
CUSTOMER
---------
CUSTOMER_ID PK
CUSTOMER_NAME
CUSTOMER_ADDRESS
i chciałeś śledzić w czasie, możesz to zmienić w następujący sposób:
CUSTOMER
------------
CUSTOMER_ID PK
CUSTOMER_VALID_FROM PK
CUSTOMER_VALID_UNTIL PK
CUSTOMER_STATUS
CUSTOMER_USER
CUSTOMER_NAME
CUSTOMER_ADDRESS
Za każdym razem, gdy chcesz zmienić rekord klienta, zamiast aktualizować rekord, ustawiasz VALID_UNTIL bieżącego rekordu na TERAZ () i wstawiasz nowy rekord z VALID_FROM (teraz) i null VALID_UNTIL. Ustawiasz status „CUSTOMER_USER” na identyfikator logowania bieżącego użytkownika (jeśli chcesz go zachować). Jeśli klient musi zostać usunięty, użyj flagi CUSTOMER_STATUS, aby to zaznaczyć - nigdy nie możesz usunąć rekordów z tej tabeli.
Dzięki temu zawsze możesz sprawdzić, jaki był stan tabeli klientów na dany termin - jaki był adres? Czy zmienili imię? Łącząc się z innymi tabelami z podobnymi datami valid_from i valid_until, możesz odtworzyć cały obraz historycznie. Aby znaleźć aktualny stan, wyszukujesz rekordy z zerową datą VALID_UNTIL.
Jest nieporęczny (ściśle mówiąc, nie potrzebujesz parametru valid_from, ale sprawia to, że zapytania są trochę łatwiejsze). To komplikuje projekt i dostęp do bazy danych. Ale to znacznie ułatwia odbudowę świata.
Oto prosty sposób, aby to zrobić:
Najpierw utwórz tabelę historii dla każdej tabeli danych, którą chcesz śledzić (przykładowe zapytanie poniżej). Ta tabela będzie miała wpis dla każdego zapytania wstawiania, aktualizowania i usuwania wykonanego w każdym wierszu tabeli danych.
Struktura tabeli historii będzie taka sama, jak tabela danych, którą śledzi, z wyjątkiem trzech dodatkowych kolumn: kolumny do przechowywania operacji, która miała miejsce (nazwijmy to „akcją”), daty i godziny operacji oraz kolumny do przechowywania numeru kolejnego („poprawka”), który zwiększa się dla każdej operacji i jest grupowany według kolumny klucza podstawowego tabeli danych.
Aby wykonać to zachowanie sekwencjonowania, tworzony jest indeks dwukolumnowy (złożony) w kolumnie klucza podstawowego i kolumnie zmiany. Zauważ, że możesz wykonywać sekwencjonowanie w ten sposób tylko wtedy, gdy silnik używany przez tabelę historii to MyISAM ( zobacz „MyISAM Notes” na tej stronie)
Tabelę historii można dość łatwo utworzyć. W zapytaniu ALTER TABLE poniżej (oraz w zapytaniach wyzwalających poniżej) zamień „primary_key_column” na rzeczywistą nazwę tej kolumny w tabeli danych.
Następnie tworzysz wyzwalacze:
I jesteś skończony. Teraz wszystkie wstawienia, aktualizacje i usunięcia w „MyDb.data” zostaną zapisane w „MyDb.data_history”, co daje taką tabelę historii (bez wymyślonej kolumny „data_columns”)
Aby wyświetlić zmiany dla danej kolumny lub kolumn od aktualizacji do aktualizacji, musisz dołączyć do siebie tabelę historii na kluczu podstawowym i kolumnach sekwencji. W tym celu możesz utworzyć widok, na przykład:
Edycja: O wow, ludzie lubią moją tabelę historii sprzed 6 lat: P
Moja realizacja wciąż nuci, jak sądzę, staje się większa i bardziej nieporęczna. Napisałem widoki i całkiem niezły interfejs użytkownika, aby spojrzeć na historię w tej bazie danych, ale nie sądzę, aby była kiedykolwiek używana zbyt często. Tak to idzie.
Aby odnieść się do niektórych komentarzy w przypadkowej kolejności:
Zrobiłem własną implementację w PHP, która była nieco bardziej zaangażowana i uniknąłem niektórych problemów opisanych w komentarzach (co ważne, przenosząc indeksy. Jeśli przeniesiesz unikalne indeksy do tabeli historii, wszystko się zepsuje. Istnieją rozwiązania dla to w komentarzach). Podążanie za tym postem do listu może być przygodą, w zależności od tego, jak ugruntowana jest Twoja baza danych.
Jeśli relacja między kluczem podstawowym a kolumną wersji wydaje się być wyłączona, zwykle oznacza to, że klucz złożony został w jakiś sposób zepsuty. W kilku rzadkich przypadkach zdarzyło mi się to i nie wiedziałem, co jest przyczyną.
Okazało się, że to rozwiązanie jest dość wydajne, używając wyzwalaczy. Ponadto MyISAM jest szybki we wkładkach, co jest wszystkim, co robią wyzwalacze. Możesz to jeszcze bardziej poprawić dzięki inteligentnemu indeksowaniu (lub jego braku ...). Wstawienie pojedynczego wiersza do tabeli MyISAM z kluczem podstawowym nie powinno być operacją, którą musisz zoptymalizować, tak naprawdę, chyba że masz poważne problemy w innym miejscu. Przez cały czas, gdy korzystałem z bazy danych MySQL, ta implementacja tabeli historii była włączona, nigdy nie było to przyczyną żadnego z (wielu) problemów z wydajnością, które się pojawiły.
jeśli otrzymujesz powtarzające się wstawienia, sprawdź, czy w warstwie oprogramowania nie ma zapytań typu INSERT IGNORE. Hrmm, nie pamiętam teraz, ale myślę, że są problemy z tym schematem i transakcjami, które ostatecznie kończą się niepowodzeniem po uruchomieniu wielu akcji DML. Przynajmniej coś, na co należy zwrócić uwagę.
Ważne jest, aby pola w tabeli historii i tabeli danych były zgodne. Albo raczej, że twoja tabela danych nie ma WIĘCEJ kolumn niż tabela historii. W przeciwnym razie zapytania insert / update / del w tabeli danych zakończą się niepowodzeniem, gdy operacje wstawiania do tabel historii wstawią kolumny w zapytaniu, które nie istnieją (z powodu d. * W zapytaniach wyzwalających), a wyzwalacz nie powiedzie się. Byłoby wspaniale, gdyby MySQL miał coś w rodzaju wyzwalaczy schematu, w którym można by zmienić tabelę historii, gdyby kolumny zostały dodane do tabeli danych. Czy MySQL ma to teraz? Obecnie Reaguję: P.
źródło
CREATE TABLE MyDB.data_history as select * from MyDB.data limit 0;
owner
pole, a do aktualizacji mógłbym dodaćupdatedby
pole, ale do usunięcia nie jestem pewien, jak mógłbym to zrobić za pomocą wyzwalaczy. aktualizacjadata_history
wiersza z identyfikatorem użytkownika w jest brudna: PMożesz stworzyć wyzwalacze, aby rozwiązać ten problem. Oto poradnik, jak to zrobić (link zarchiwizowany).
Innym rozwiązaniem byłoby pozostawienie pola Rewizja i zaktualizowanie tego pola przy zapisywaniu. Możesz zdecydować, że maksymalna jest najnowszą wersją lub że 0 to najnowszy wiersz. To zależy od Ciebie.
źródło
Oto jak to rozwiązaliśmy
Tabela użytkowników wyglądała tak
Zmieniły się wymagania biznesowe i musieliśmy sprawdzić wszystkie poprzednie adresy i numery telefonów, jakie kiedykolwiek posiadał użytkownik. nowy schemat wygląda tak
Aby znaleźć aktualny adres dowolnego użytkownika, wyszukujemy dane użytkownika z wersją DESC i LIMIT 1
Aby uzyskać adres użytkownika między określonym przedziałem czasu, możemy użyć created_on bewteen (data1, data 2)
źródło
revision=1
zid_user=1
? Najpierw pomyślałem, że liczyłeś,0,2,3,...
ale potem zobaczyłem, że dlaid_user=2
liczenia rewizji jest0,1, ...
id
i idid_user
kolumny. Just use a group ID of
(identyfikator użytkownika) irevision
.MariaDB obsługuje wersję systemu od 10.3, która jest standardową funkcją SQL, która robi dokładnie to, co chcesz: przechowuje historię rekordów tabeli i zapewnia dostęp do niej za pośrednictwem
SELECT
zapytań. MariaDB to rozwidlenie MySQL dla otwartego rozwoju. Więcej informacji na temat wersji systemu można znaleźć pod tym linkiem:https://mariadb.com/kb/en/library/system-versioned-tables/
źródło
Dlaczego po prostu nie użyć plików dziennika bin? Jeśli replikacja jest ustawiona na serwerze MySQL, a format pliku binlog jest ustawiony na ROW, wówczas wszystkie zmiany mogą zostać przechwycone.
Można użyć dobrej biblioteki Pythona o nazwie noplay. Więcej informacji tutaj .
źródło
Tylko moje 2 centy. Stworzyłbym rozwiązanie, które dokładnie rejestruje, co się zmieniło, bardzo podobne do rozwiązania transienta.
Moja tabela zmian byłaby prosta:
DateTime | WhoChanged | TableName | Action | ID |FieldName | OldValue
1) Kiedy cały wiersz zostanie zmieniony w głównej tabeli, do tej tabeli trafi wiele wpisów, ALE jest to bardzo mało prawdopodobne, więc nie jest to duży problem (ludzie zwykle zmieniają tylko jedną rzecz) 2) OldVaue (i NewValue, jeśli want) musi być jakimś epickim „dowolnym typem”, ponieważ mogą to być dowolne dane, może istnieć sposób na zrobienie tego z typami RAW lub po prostu za pomocą ciągów JSON do konwersji wejścia i wyjścia.
Minimalne użycie danych, przechowuje wszystko, czego potrzebujesz i może być używane dla wszystkich tabel jednocześnie. Sam to badam w tej chwili, ale może to się skończyć.
W przypadku tworzenia i usuwania tylko identyfikator wiersza, żadne pola nie są potrzebne. Po usunięciu flagi na głównej tabeli (aktywna?) Byłaby dobra.
źródło
Bezpośrednim sposobem na to jest utworzenie wyzwalaczy w tabelach. Ustaw warunki lub metody mapowania. Gdy nastąpi aktualizacja lub usunięcie, zostanie automatycznie wstawiony do tabeli zmian.
Ale najważniejsze jest to, że mamy dużo kolumn i dużo tabel. Musimy wpisać nazwę każdej kolumny każdej tabeli. Oczywiście szkoda czasu.
Aby lepiej sobie z tym poradzić, możemy stworzyć pewne procedury lub funkcje do pobierania nazw kolumn.
W tym celu możemy również użyć narzędzia trzeciej części. Tutaj piszę program java Mysql Tracker
źródło
create table like table
Myślę, że łatwo powiela wszystkie kolumny