03-11-2008

denormalizować

Zapewne to, co napiszę tutaj będzie trywialne - ale może ktoś z tego skorzysta...

Jakoś niedawno bawiłem się w walidację formularzy w django - a chwilę później ^alex zetknął się z takim problemem i zapytał na blipie. Miałem to na świeżo, więc nawet dostałem ++ do lansu (tutaj).

Dziś znów, podczas pisania swojego nowego cudeńka (o którym tutaj, mam nadzieję, więcej wkrótce napiszę) zetknąłem się z problemem w modelu, który sprowadził się do tego - normalizować czy denormalizować bazę?

Nad problemem normalizacja czy nie zastanawiał się też Jeff Atwood na swoim blogu.

Sytuacja jest taka: dwie tabele, jedna z nich zawiera dane transakcji - nazwijmy ją tabelą A. Druga - tabela B, połączona z tabelką A przez ForeignKey.

Mamy więc many B - one A.



Teraz, chcę wybrać te obiekty A, które mają jeden (lub zero) powiązanych B.

Po kilku próbach zrobienia tego przez normalne filtrowanie, zapytałem na stackoverflow. Jedna z odpowiedzi sugeruje użycie składni .extra obiektu QuerySet. Wolałem tego uniknąć...

Druga - każe się zastanowić nad denormalizacją bazy - i dodaniem do modelu A dodatkowego elemrntu, albo wskazującego na ilość linkujących do niego B, albo zawierających po prostu boolowską zmienną mówiącą czy jest zero lub jeden takich B, że b.a_id = a.id.

No i mamy denormalizację. Podręcznikowy przykład, przy którym należy sobie zadać pytanie o dwie sprawy:

  • wydajność - czy aktualizacja obiektów jest częsta? Czy narzut związany z dodatkowym parametrem i każdorazowym jego przeliczaniem rekompensuje trochę szybsze wykonanie zapytania wyświetlającego nasze dane? Jest to więc pytanie o to, która z operacji wykonywana jest częściej.

  • zachowanie spójności bazy - jeśli skasuję obiekt b, wskazujący na obiekt a, muszę mieć pewność, że licznik a zmieni się odpowiednio. Podobnie - przy dodawaniu nowego obiektu.


Pierwszy przypadek jest bardzo zależny od tego, gdzie i w jakich okolicznościach używane jest to zapytanie, od obciążenia serwera i, jestem pewien, może być różny dla różnych silników bazodanowych. Dlatego najprostszą analizą jest zapytanie - czy wyświetlanie jest częstsze niż aktualizacja? W moim przypadku jest. score.

Druga sprawa jest na szczęście dość dobrze obsługiwana przez modele w Django.

Po chwili poszukiwań znalazłem sporo materiałów na temat możliwości dawanych przez nadpisanie domyślnej funkcji models.Model.save() dostarczonej przez django. Jedną z lepszych jest wyszukane przez Google (i już niestety nieistniejące poza światem cache'a googla) Django Signals vs. Custom Save()-Method.

W moim wypadku to przesądza. Zrobię składny, szybszy model, co z tego, że nieznormalizowany?