Rozrost złączenia i powielanie wierszy
Dlaczego złączenie może zwrócić więcej wierszy niż którakolwiek z tabel i jak rekruterzy to sprawdzają
Rozrost złączenia i powielanie wierszy to bezpłatna lekcja Coding Interview Prep na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Coding Interview Prep, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Coding Interview Prep zawiera 4 lekcji w sumie.
Gdy złączenie zwraca zbyt wiele wierszy
Jedno z najbardziej odkrywczych pytań rekrutacyjnych brzmi niewinnie: "czy złączenie może zwrócić więcej wierszy niż większa tabela?" Odpowiedź brzmi: tak, a zjawisko to nazywa się fan-out lub mnożeniem wierszy.
Kandydaci, którzy mówią "złączenie po prostu łączy tabele", nie dostrzegają tego problemu. Zatrudnienie zdobywają kandydaci, którzy potrafią przewidzieć dokładną liczbę wierszy. Ta lekcja rozwija tę umiejętność.
Przyczyna: dopasowania jeden-do-wielu
Fan-out występuje, gdy jeden wiersz z lewej strony pasuje do wielu wierszy z prawej strony. Każde dopasowanie tworzy osobny wiersz wyniku.
W przypadku customers i orders Ada (jedna klientka) ma dwa zamówienia. Złączenie generuje po jednym wierszu dla każdego zamówienia, więc Ada pojawia się dwukrotnie. Kolumny klientki się powtarzają, a różnią się tylko kolumny zamówienia.
SELECT c.name, o.amount
FROM customers c
JOIN orders o ON o.customer_id = c.id;
-- Ada appears twice (she has 2 orders)
-- name | amount
-- Ada | 50
-- Ada | 20
-- Bob | 99Zliczanie wierszy wyniku
Liczba wierszy wyniku jest równa sumie dopasowań dla każdego wiersza z lewej strony, a nie liczbie klientów.
- Ada -> 2 zamówienia -> 2 wiersze
- Bob -> 1 zamówienie -> 1 wiersz
- Cleo -> 0 zamówień -> 0 wierszy (usunięta przez INNER JOIN)
Łącznie = 3 wiersze, mimo że tabela customers również ma 3 wiersze. Jeśli Ada będzie mieć 10 zamówień, wynik zwiększy się do 11 wierszy.
Złączenie wiele-do-wielu gwałtownie zwiększa wynik
Fan-out narasta, gdy obie strony mają wiele dopasowań dla tego samego klucza. Jeśli klucz K występuje 3 razy po lewej stronie i 4 razy po prawej, złączenie wygeneruje dla tego klucza 3 x 4 = 12 wierszy.
W ten sposób pozornie niewielkie złączenie może rozrosnąć się do milionów wierszy. Rekruterzy często podają zduplikowane klucze po obu stronach, aby sprawdzić, czy kandydat zauważy mnożenie.
-- left has 3 rows with tag 'A', right has 4 rows with tag 'A'
SELECT l.id, r.id
FROM left_t l
JOIN right_t r ON r.tag = l.tag;
-- tag 'A' alone yields 3 * 4 = 12 output rowsPułapka agregowania
Oto błąd, który rekruterzy najczęściej celowo umieszczają w zadaniu. Łączysz orders z order_items, aby uzyskać szczegóły pozycji, a następnie obliczasz SUM kwoty zamówienia. Ponieważ każde zamówienie rozgałęzia się na wiele wierszy pozycji, kwota zamówienia jest zliczana raz dla każdej pozycji.
Wartość SUM jest teraz znacznie zawyżona. Zapytanie wygląda poprawnie i nawet się wykonuje, co czyni ten błąd niebezpiecznym.
-- BUG: order.amount duplicated across items
SELECT SUM(o.amount) AS total
FROM orders o
JOIN order_items i ON i.order_id = o.id;
-- a 3-item order counts o.amount 3 timesJak powstaje zawyżenie
Załóżmy, że jedno zamówienie ma kwotę 100 i trzy pozycje. Złączenie generuje trzy wiersze, a w każdym z nich znajduje się kwota 100. SUM(o.amount) zwraca 300, a nie 100.
Rozwiązaniem jest agregowanie na właściwym ziarnie: zsumowanie pozycji albo osobne zsumowanie niepowtarzających się zamówień. Nie należy nigdy obliczać SUM wartości nadrzędnej po złączeniu z rozmnożonymi wierszami elementów podrzędnych.
o.id | o.amount | i.id
7 | 100 | 71
7 | 100 | 72
7 | 100 | 73
-- SUM(o.amount) = 300 (WRONG, should be 100)Rozwiązanie 1: najpierw agreguj elementy podrzędne
Najczystsze rozwiązanie polega na wstępnym agregowaniu strony „wiele” w podzapytaniu lub CTE, tak aby każdy element nadrzędny pasował dokładnie do jednego podsumowanego wiersza. Brak fan-outu oznacza brak zawyżenia.
W tym przypadku elementy są redukowane do jednego wiersza na zamówienie przed wykonaniem złączenia, dzięki czemu kwota nadrzędna nigdy nie jest powielana.
SELECT o.id, o.amount, i.item_count
FROM orders o
JOIN (
SELECT order_id, COUNT(*) AS item_count
FROM order_items
GROUP BY order_id
) i ON i.order_id = o.id;Rozwiązanie 2: COUNT(DISTINCT) i sumy warunkowe
Jeśli agregowanie po złączeniu z fan-outem jest konieczne, należy zliczać lub sumować na właściwym ziarnie. Użyj COUNT(DISTINCT o.id), aby zliczać zamówienia, a nie wiersze pozycji.
Uwaga: SUM(DISTINCT o.amount) NIE jest bezpiecznym rozwiązaniem, ponieważ dwa różne zamówienia mogą mieć taką samą kwotę i zostałyby połączone w jedną wartość. Wstępne agregowanie jest bardziej niezawodne.
SELECT COUNT(DISTINCT o.id) AS num_orders,
COUNT(i.id) AS num_items
FROM orders o
JOIN order_items i ON i.order_id = o.id;Wykrywanie fan-outu, zanim spowoduje problem
Szybka metoda diagnostyczna, którą lubią rekruterzy: sprawdź, czy klucz złączenia jest unikatowy po stronie, która powinna być stroną „jeden”. Jeśli liczba unikatowych kluczy jest mniejsza niż liczba wierszy, po tej stronie występują duplikaty i nastąpi fan-out.
-- if this returns rows, order_id is NOT unique in order_items
SELECT order_id, COUNT(*) AS n
FROM order_items
GROUP BY order_id
HAVING COUNT(*) > 1;Weryfikowanie ziarna za pomocą zliczania
Przed uznaniem agregacji wykonanej na połączonym wyniku za poprawną należy sprawdzić liczbę wierszy. Szybka metoda polega na porównaniu liczby wierszy po złączeniu z liczbą wierszy tabeli, która powinna określać ziarno wyniku.
Jeśli COUNT(*) dla złączenia jest większe niż COUNT(*) dla orders, złączenie spowodowało fan-out i każda agregacja wykonywana na poziomie zamówienia jest zagrożona. To jednolinijkowe sprawdzenie pomogło już w wielu odpowiedziach rekrutacyjnych.
-- joined rows should equal order count if no fan-out
SELECT COUNT(*) AS joined_rows
FROM orders o
JOIN order_items i ON i.order_id = o.id;
SELECT COUNT(*) AS order_rows FROM orders;
-- joined_rows > order_rows => fan-out presentFan-out nie zawsze jest błędem
Czasami właśnie chcesz otrzymać jeden wiersz na element podrzędny. Wyświetlenie każdej pozycji wraz z nagłówkiem jej zamówienia jest prawidłowym fan-outem. Umiejętność polega na znajomości docelowego ziarna: ile wierszy powinna generować jedna encja?
Ziarno należy określić przed napisaniem zapytania. Rozróżnienie między „chcę jeden wiersz na pozycję zamówienia” a „chcę jeden wiersz na zamówienie” decyduje o tym, czy fan-out jest funkcją, czy błędem.
Szybki test
Przewidź wynik złączenia jeden-do-wielu.
Podsumowanie: fan-out i mnożenie wierszy
Najważniejsze informacje:
- Złączenie generuje jeden wiersz na każdą dopasowaną parę, więc dopasowania jeden-do-wielu powielają stronę „jeden”.
- Klucze wiele-do-wielu powodują mnożenie: 3 x 4 = 12 wierszy dla danego klucza.
- Agregowanie wartości nadrzędnej po złączeniu z fan-outem zawyża sumy i liczności.
- Rozwiązaniem jest wstępne agregowanie elementów podrzędnych albo zliczanie i sumowanie na właściwym ziarnie, na przykład za pomocą
COUNT(DISTINCT). - Zawsze najpierw określ zamierzone ziarno; fan-out jest błędem tylko wtedy, gdy narusza to założenie.
Często zadawane pytania
Czy lekcja „Rozrost złączenia i powielanie wierszy” jest bezpłatna?
Tak — pełny tekst „Rozrost złączenia i powielanie wierszy” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Coding Interview Prep, przejdź na CoddyKit PRO. Kurs Coding Interview Prep zawiera 4 lekcji w sumie.
Co nauczysz się w „Rozrost złączenia i powielanie wierszy”?
Dlaczego złączenie może zwrócić więcej wierszy niż którakolwiek z tabel i jak rekruterzy to sprawdzają Ćwiczysz Coding Interview Prep z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Coding Interview Prep?
Nie wymagamy żadnego doświadczenia. Coding Interview Prep w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Rozrost złączenia i powielanie wierszy”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Coding Interview Prep?
Tak. Każda lekcja Coding Interview Prep zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Jak INNER JOIN dopasowuje wiersze
- ON a WHERE w złączeniach
- Rozrost złączenia i powielanie wierszy
- Łączenie trzech lub większej liczby tabel