Zduplikowana kolumna dla szybszych zapytań?


30

Tytuł nie ma zbyt wielkiego sensu, ale nie mogłem wymyślić lepszego tytułu dla tego problemu.

Mam następujące tabele

Projektowanie

  • ID
  • imię

Klienci

  • ID
  • projekt id
  • imię

Płatności

  • ID
  • id_customer
  • data
  • suma

Gdy użytkownik wejdzie do systemu, będzie miał dostęp do określonego projektu. Teraz chcę wymienić wszystkie płatności dla tego projektu i powinno to być dość łatwe:

SELECT FROM payments where id_customer in (SELECT id from customers where id_project = 5)

Moje pytanie brzmi: jeśli nie jest lepiej dodać kolumnę id_project do tabeli płatności w ten sposób, zapytania będą łatwiejsze i szybsze.


1
więc zapytanie nie stanowi problemu dla nowoczesnych RDBMS (lub lepiej, użyj sprzężenia).
garik

4
Zgadzam się, uzyskaj plan zapytania dla podselekcji vs przyłączenia i zobacz, który jest lepszy
Gaius

1
Myślę, że ten post SO jest wart uwagi, ponieważ @igor wspomniał o używaniu JOIN lub IN
CoderHawk

Odpowiedzi:


52

Wygląda na to, że pytasz, czy denormalizacja ma sens.

Denormalizacja to proces próby zoptymalizowania wydajności odczytu bazy danych przez dodanie zbędnych danych lub grupowanie danych. W niektórych przypadkach denormalizacja pomaga ukryć nieefektywności związane z oprogramowaniem do relacyjnych baz danych. Relacyjna znormalizowana baza danych nakłada duże obciążenie na dostęp do fizycznego przechowywania danych, nawet jeśli jest dobrze dostrojona pod kątem wysokiej wydajności.

Odpowiedź zawsze brzmi „to zależy”, więc oto moja ogólna zasada:

Jeśli ...

  • ilość danych nie jest duża
  • jeszcze nie robisz wielu połączeń
  • i / lub wydajność bazy danych nie jest obecnie wąskim gardłem

następnie pozostań znormalizowany . Tak, denormalizacja jest szybsza, ale oznacza to również, że masz nadmiarowe dane w systemie - dane, które muszą być utrzymywane i synchronizowane. Nie ma już „jednego źródła” dla tych danych, ale wiele źródeł, które mogą się różnić. Z czasem jest to ryzykowne, więc nie powinieneś tego robić, chyba że masz bardzo dobre powody, poparte niektórymi testami porównawczymi.

Denormalizowałbym się tylko wtedy, gdy ...

  • ilość danych jest bardzo duża
  • przyłączenia są drogie i musisz zrobić wiele z nich, aby uzyskać nawet trywialne zapytania
  • wydajność bazy danych jest wąskim gardłem i / lub chcesz iść tak szybko, jak to możliwe

Połączenia są bardzo szybkie na nowoczesnym sprzęcie, ale nigdy nie są bezpłatne.


9

Lepiej byłoby przepisać zapytanie jako:

SELECT payments.*
FROM   customers
JOIN   payments 
ON     payments.id_customer = customers.id
WHERE  customers.id_project = 5

Chociaż wydaje się to mniej zwięzłe, a dobry planista zapytań zobaczy, co próbujesz zrobić, i uruchomi skorelowane zapytanie podrzędne, gdy powyższe połączenie zostanie połączone, zły planista zapytań może w końcu wykonać skanowanie indeksu payments.id_customer(zakładając, że masz odpowiedni indeks ) (lub, co gorsza, skanowanie tabeli) zamiast robić rzeczy w bardziej wydajny sposób. Nawet dobry planista zapytań może nie zobaczyć optymalizacji, jeśli układ tego zapytania jest zawinięty w coś bardziej skomplikowanego. Wyrażenie relacji jako połączenia zamiast pod-zapytania może mieć większą różnicę niż zmiana struktury danych.

Jak mówi Jeff, wszelkie denormalizacje powinny być rozważane ostrożnie - może przynieść łatwe zwiększenie wydajności, szczególnie w niektórych celach sprawozdawczych, ale może prowadzić do niespójności z powodu błędów w wspierającej logice biznesowej.

Na marginesie: Oczywiście nie znam twojej firmy, więc mogłem coś przeoczyć, ale twoje relacje przy stole wydają mi się dziwne. Sugerują, że nigdy nie można mieć więcej niż jednego projektu z tym samym klientem, co zwykle nie jest prawdą z mojego doświadczenia, przynajmniej przez długi okres.

customer     project      payment
--------     --------     -------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer     

lub jeśli jestem mniej znormalizowany (choć wątpię, by to było konieczne):

customer     project      payment
--------     --------     --------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer 
           `------------- customer    

Oczywiście to wciąż dyskontuje możliwość wspólnego projektu z dwoma klientami ...


3
Pierwsza zasada wydajności: nigdy nie używaj * w produkcji!
Brian Ballsun-Stanton,

@Brian: bardzo ważny punkt. Oprócz potencjalnego wpływu na wydajność uniknięcie * w wybranych klauzulach pozwala również uniknąć problemów z kolejnością kolumn w widokach w widoku w MSSQL, jeśli sys.depends nie działa prawidłowo z powodu użycia DROP VIEW+ CREATE VIEWzamiast ALTER VIEW.
David Spillett,

@Brian umieściłem * dla łatwego pisania.
Gabriel Solomon

Projekt jest uważany raczej za niezależną aplikację z domeną i należy do różnych klientów, więc klient nie może mieć tego samego konta w różnych projektach
Gabriel Solomon

4

W niektórych bazach danych istnieje możliwość tworzenia „widoków zmaterializowanych” zamiast złożonych widoków z dużą ilością danych, na podstawie złożonego zapytania. Można tego użyć, aby uniknąć denormalizacji w historycznym systemie aplikacji. Jeśli zdecydujesz się użyć „ Widoki zmaterializowane ”musisz mieć jasny obraz metod odświeżania i ilości miejsca, które zostanie wykorzystane przez widok zmaterializowany ...

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.