CodeGym /Kursy /SQL SELF /Balans między normalizacją a wydajnością

Balans między normalizacją a wydajnością

SQL SELF
Poziom 26, Lekcja 3
Dostępny

Kiedy idealnie normalizujemy dane, każda tabela staje się maksymalnie kompaktowa i informacje w niej odpowiadają tylko jednej zasadzie. Ale żeby wykonać realne zapytania (np. "Którzy studenci zapisali się na kurs SQL?") trzeba połączyć masę tabel. Im więcej tabel, tym bardziej skomplikowane zapytania, tym więcej system "kopie łopatą".

Pewnie już znasz JOIN-y z poprzednich wykładów. Oto przykład zapytania, które może się przydać przy pracy z dobrze zaprojektowaną bazą danych:

SELECT students.name, courses.title
FROM students
JOIN enrollments ON students.id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.id
WHERE courses.title = 'SQL';

Brzmi prosto, ale pod maską serwer robi piekielną robotę: czyta każdą tabelę, łączy dane, filtruje... A co jeśli tabele są naprawdę ogromne? Logiczne, że wydajność będzie spadać.

Wojny: normalizacja kontra szybkość

Na szczęście (albo niestety?), w prawdziwym życiu bazy danych to kompromis. Pełna normalizacja daje spójność danych, ale spowalnia złożone zapytania. Jeśli baza będzie używana do analityki i raportów, czasem lepiej ją zdenormalizować. To jak zamienić 10 małych pudełek na jedną dużą skrzynię: szybciej wyciągniesz dane, ale trudniej je potem rozłożyć z powrotem.

Kiedy można "odpuścić" z normalizacją?

Są scenariusze, gdzie denormalizacja jest lepsza:

Często używane agregaty

Na przykład wyobraź sobie, że system codziennie liczy ilu studentów jest na każdym kursie. W znormalizowanej strukturze trzeba ciągle robić JOIN i COUNT(). Zamiast tego można dodać do tabeli "Courses" kolumnę student_count, która aktualizuje się automatycznie przy dodaniu/usunięciu wpisów.

-- Zdenormalizowana kolumna
UPDATE courses
SET student_count = (
    SELECT COUNT(*)
    FROM enrollments
    WHERE enrollments.course_id = courses.id
);

Często generowane raporty

Jeśli twój klient codziennie chce raport "Kto, co, kiedy kupił?", łatwiej trzymać zdenormalizowaną tabelę z gotowymi wierszami typu "Imię klienta, produkt, data". Główna tabela będzie większa, ale szybkość pobierania danych wzrośnie.

Dużo odczytów, mało zapisów

Kiedy baza jest głównie do odczytu (np. analityka), warto poświęcić normalizację dla szybkości.

Minimalizacja joinów przy złożonych relacjach

Jeśli między tabelami są wielowarstwowe (nested) relacje i JOIN to koszmar, usuń kilka poziomów normalizacji.

Przykład: jak denormalizacja przyspiesza działanie?

Mamy znormalizowane tabele sklepu internetowego:

Tablica products Tablica orders Tablica order_items
id id id
name date order_id
price customer_id product_id
quantity

Każde zamówienie (orders) składa się z pozycji zamówienia (order_items). Policzmy ile pieniędzy zarobił sklep:

SELECT SUM(order_items.quantity * products.price) AS total_revenue
FROM order_items
JOIN products ON order_items.product_id = products.id;

Proces łączenia order_items i products spowolni zapytanie przy dużych ilościach danych.

Zdenormalizowana struktura

Wyobraź sobie teraz, że w tabeli order_items jest "dodatkowa" kolumna total_price (denormalizacja):

Tablica order_items
id
order_id
product_id
quantity
total_price

Teraz zapytanie jest banalne:

SELECT SUM(total_price) AS total_revenue
FROM order_items;

Unikamy JOIN, więc przyspieszamy wykonanie.

Zadanie praktyczne: optymalizacja bazy "Sprzedaż"

Dane: znormalizowane tabele

Tablica products Tablica sales
id id
name product_id
price date
quantity

Zadanie: przyspieszyć częste zapytania typu "Ile zarobiliśmy na każdym produkcie?".

Krok 1: dodajemy kolumnę total_price do tabeli sales:

ALTER TABLE sales ADD COLUMN total_price NUMERIC;

Krok 2: wypełniamy tę kolumnę dla istniejących danych:

UPDATE sales
SET total_price = quantity * (
    SELECT price
    FROM products
    WHERE products.id = sales.product_id
);

Krok 3: zapytania są szybsze:

SELECT product_id, SUM(total_price) AS total_revenue
FROM sales
GROUP BY product_id;

Ale! Denormalizacja ma swoją cenę

Wiesz, że "szybciej" nie zawsze znaczy "lepiej". Przy denormalizacji pojawiają się problemy:

Nadmiarowe przechowywanie

Kolumna total_price to kopia danych, która zajmuje dodatkowe miejsce.

Trudność aktualizacji

Jeśli cena produktu zmieni się w tabeli products, musimy ręcznie zaktualizować odpowiednią kolumnę total_price. To może prowadzić do niespójności.

Anomalie przy wstawianiu, aktualizacji i usuwaniu

Informacje łatwo się "rozjeżdżają", jeśli zapomnisz zaktualizować zdenormalizowane dane. Na przykład, jeśli zmienia się cena produktu, nie dzieje się to automatycznie.

Balans: jak znaleźć złoty środek?

Wybierz, co ważniejsze: wydajność czy struktura? Jeśli baza jest mocno czytana, dostosuj się do zapytań.

Dodawaj denormalizację punktowo. Na przykład tylko dla kluczowych liczb i raportów.

Automatyzuj aktualizacje zdenormalizowanych danych. Używaj triggerów lub zadań, żeby uniknąć niespójności.

2
Zadanie
SQL SELF, poziom 26, lekcja 3
Niedostępne
Denormalizacja dla przyspieszenia obliczeń
Denormalizacja dla przyspieszenia obliczeń
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION