Projekt bazy danych, problem z duplikacją i kasowaniem rekordów

0

Witam,

Zastanawiam się nad rozwiązaniem takiego problemu:

Weźmy przykład tabeli klienci, *faktury *i pozycje faktury gdzie wszystko jest jasne - pozycja faktury musi dodana do faktury która jest wystawiona dla danego klienta. Dane nie mogą być skasowane.

A co w przypadku jeśli wymaganie mówi że, pozycja faktury musi być wystawiona dla istniejącej faktury i danego klienta ale później mamy mieć możliwość usunięcia klienta z bazy jak i faktury pozostawiając pozycje faktury tak by przedstawiała cały czas np numer faktury i nazwę klienta czyli jakąś wybraną informacje z tabel klienci i faktury (choć rekordy zawierające pełny opis zostały już skasowane) a po jakimś czasie usuwamy również i pozycje.

Wiem przykład nie realny ale to tylko przykład który mi się wydał najprostszy do zrozumienia o co mi chodzi.

W takim wypadku dodając pozycję zawsze muszę dodać informację o nazwie klienta i numerze faktury do pozycji:

  • dodając kolumny do pozycji faktury i kopiując nazwę klienta i numer faktury mamy duplikacje danych (tym bardziej jeśli mamy klienta który złożył tysiące faktur),
  • tworząc dodatkowe tabele do których będzie kopiowana nazwa klienta raz i łączona z pozycjami poprzez relacje unikamy duplikacji (klient skopiowany raz z tabeli klienci i łączony z pozycjami poprzez relacje z dodatkową tabelą), tak samo dla numeru faktury. Jeśli klient zostanie usunięty z bazy to pozycja w dodatkowej tabeli pozostanie więc pozycje faktury wciąż wyświetlą wymagane informacje,
  • można nie kasować rekordów z tabeli klienci i faktury a tylko ukrywać je ustawiając np flagę, ale jeśli namy jakieś restrykcyjne pole np nazwisko jako unique to nie możemy dodać nowego z tym samym a ukryty rekord może blokować dodanie nowego.

Jakie macie inne rozwiązania na taki problem?

0

Sytuacja wydaje się w ogóle bez sensu, ale zakładając, że to czysto hipotetyczne rozważania:
Z całą pewnością nie dodawałabym dodatkowych kolumn. Czyli mamy tabelę użytkownik połączoną kluczem z tabelą faktura i tabelę faktura połączoną kluczem z tabelą pozycja_z_faktury.
Zakładając, że dane z tabel użytkownik i faktura byłyby potrzebne nawet kiedy nie są już dostępne w systemie (usunięte), po prostu dla każdej z tych tabel zrobiłabym odpowiadającą tabelę z danymi zarchiwizowanymi. W zależności od wymagań, przechowywane byłyby tam wszystkie bądź część informacji dotyczących faktury.

2

to wcale nie jest wydumany problem bo (jeśli chodzi o faktury) wystarczy, że kontrahent zmieni adres (NIP zostaje) a program musi "stare" faktury nadal drukować ze "starym" adresem a "nowe" z "nowym".
Wracając do pytania to nie da się usunąć dane i mieć dane. Zapisywanie kopii nagłówka w pozycji to nie jest rozwiązanie - te dane gdzieś muszą być. Generalnie podejścia są dwa:

  1. dane archiwalne w tej samej tabeli co dane główne
  2. dane archiwalne w dodatkowej tabeli.

Dane archiwalne dotyczą nie tylko tabel z fakturą (nagłówek i pozycje ale też wszystkich tabel "towarzyszących" - kontrahent, towar i co tam jeszcze jest w bazie, wraz z ferajną)
Oba podejścia mają swoje zady i walety :).
W pierwszej np. problemem będzie pilnowanie unikalność np. numeru kontrahenta (nie ID rekordu!) bo będzie się on powtarzał - jeden raz w danych aktualnych (i on musi być unikalny) i x razy w danych archiwalnych. Tabela będzie "puchła" dodatkowymi danymi (ale partycjonowanie i indeksy w większości przypadków eliminują ten problem, a dyski są stosunkowo tanie).
W drugim podejściu główny problem to skomplikowanie logiki. Np. niemożność użycia kluczy obcych (bo dane mogą być w dwóch różnych tabelach) lub użycie dwóch FK do dwóch tabel, gdzie wypełnione może być tylko jedno z pól (czyli np. pola kontrahent_id z FK do tabeli kontrahent i kontrahent_id_kopia do tabeli kontrahent_kopia). Drugi największy problem to pilnowanie spójności danych - przy usuwaniu kontrahenta trzeba go po pierwsze przenieść do tabeli archiwalnej a po drugie podmienić ID w fakturach na nowe ID w tabeli archiwalnej.
Wady każdego sposobu to jednocześnie zalety drugiego (w sensie, że ten drugi tych wad nie ma).

0

Przedstawiony przykład oczywiście nie ma sensu w rozumieniu usuwania kontrahenta, faktury i pozostawiania pozycji ale podstawiając pod te nazwy tabel całkiem inne z innymi danymi ale takimi wymaganiami staje się logiczny ale trudniejszy do zobrazowania dlatego wybrałem przykład kontrahenta - faktury - pozycji. abrakadaber jednak dobrze zwrócił uwagę że nawet ten przykład wymaga dodatkowego archiwizowania przy updacie danych.

Hmm bardziej sensowny przykład to sensor - alert - alarm gdzie sensor to jakieś urządzenie generujące dane, alert jako kryteria generowania alarmów na podstawie danych z sensora a alarm to alarm wygenerowany na podstawie zapisanych kryteriów. Dla danego urządzenia kryteria generowania alarmów mogą się zmieniać w czasie jak i same sensory mogą być dodawane/usuwane z systemu (np pod względem obciążenia serwera) ale alarmy zostają i są przechowywane przez jakiś czas.

Zdecydowanie wariant z dodatkowymi tabelami wydaje się najbardziej odpowiedni i taki zakładałem, duplikacja danych nie jest aż tak duża z jednoczesnym utrzymaniem spójności danych - tu wniósłbym poprawkę do tego co napisałeś: " niemożność użycia kluczy obcych" oraz " pilnowanie spójności danych - przy usuwaniu kontrahenta trzeba go po pierwsze przenieść do tabeli archiwalnej " - zamiast pilnować spójności przy kasowaniu i przenosić, podmieniać ID w pozycjach myślałem nad inną logiką - tabela pozycje faktury czy alarm z drugiego przykładu ma relację nie do faktury czy alertu ale do tabel archiwizujących. Dodając pozycje faktury czy alarm w pierwszej kolejności wypełniam tabelę archiwizującą a następnie pozycję czy alarm z odpowiednim FK do tabeli archiwizującej. Dane do tabeli archiwizującej dodaję tylko wtedy gdy nie istnieją czyli dany kontrahent dodany będzie raz ale jeśli zmienimy mu NIP to będzie dodany ponownie (nowe zestaw danych). W tym przypadku nie trzeba nic updatować czy podmieniać ID jeśli dane się zmienią czy klient zostanie skasowany. Pomiędzy tabelą archiwizującą a właściwą może być relacja (ale nie musi) ale zakładająca że w przypadku skasowania kontrahenta, w tabeli archiwizującej FK będzie będzie ustawione na NULL.

Widzę też dodatkową zaletę co jest moim dodatkowym pytaniem czy warto to robić. Zaleta w postaci odseparowania danych konfiguracyjnych systemu od danych generowanych przez system co w przypadku systemu generujągo np setki insertów na minutę może mieć znaczenie. Również w przypadku tworzenia backupów konfiguracyjnych widzę jako plus. Dane konfiguracyjne trzymane w jednej bazie, tu lepiej użyć przykładu sensor - alert, a dane wraz z tabelami archiwizującymi w drugiej bazie (w tym przypadku nie ma relacji pomiędzy tabelą właściwą kontrahent / sensor a archiwizującą dlatego dodałem powyżej w nawiasie "ale nie musi").

PS Dzięki za zainteresowanie tematem i odpowiedzi :)

1

myślę, że jakbyś od razu napisał o co Ci chodzi zamiast próbować, nawet nie wiem co próbowałeś zrobić, wymyślać przykłady z tyłka to miałbyś konkretne odpowiedzi dostosowane do pytania. Wbrew pozorom są tu rozgarnięci ludzie, którzy pracują z różnymi systemami i danymi i potrafią ogarnąć różne poziomy abstrakcji.

Twojego ostatniego postu praktycznie nie da się czytać - piszesz i dwóch różnych rzeczach, które nijak się mają do siebie. Te przykłady wcale nie są logiczne. Nie wiem czemu uparłeś się prosty przypadek (dane z czujników + alerty i info o konfiguracji wyzwalaczy w danej chwili) prezentować w postaci rzeczywistości, która wcale tak nie działa - faktury i kontrahenci.

0

Rozwiązaniem jest raczej soft delete. W ogóle rzadko zachodzi potrzeba rzeczywistego kasowania danych z bazy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1