Połączenia są dla leniwych?

169

Niedawno rozmawiałem z innym programistą, który twierdził, że JOIN (SQL) jest bezużyteczny. Z technicznego punktu widzenia jest to prawda, ale dodał, że używanie złączeń jest mniej wydajne niż wykonywanie kilku żądań i łączenia tabel w kodzie (C # lub Java).

Dla niego przyłączenia są dla leniwych ludzi, którym nie zależy na wydajności. Czy to prawda? Czy powinniśmy unikać łączenia?

Bastien Vandamme
źródło
114
Nie. Bazy danych są zoptymalizowane pod kątem łączenia, są niezwykle szybkie, zwłaszcza w przypadku dużych zbiorów danych. Nie chcesz, aby Twoja aplikacja ładowała dziesiątki tysięcy wierszy i łączyła je ręcznie.
halfdan
91
Języki programowania są dla leniwych; są mniej wydajne niż ręczne kodowanie instrukcji procesora. :)
Michael McGowan
76
Jak nazywa się programista? Chcę się upewnić, że nigdy go nie zatrudnię.
Joe
39
@Michael meh, prawdziwi programiści używają motyli ...
Marc Gravell
14
Re Twoje „to prawda” - nie, nie jest. Bazy danych działają w oparciu o teorię mnogości; łączenia na planie działają bardzo ładnie i pożytecznie ...
Marc Gravell

Odpowiedzi:

188

Nie, powinniśmy unikać programistów, którzy mają tak niewiarygodnie błędne opinie.

W wielu przypadkach łączenie bazy danych jest o kilka rzędów wielkości szybsze niż cokolwiek zrobione za pośrednictwem klienta, ponieważ pozwala uniknąć obiegów DB, a DB może użyć indeksów do wykonania sprzężenia.

Nie mogę sobie nawet wyobrazić jednego scenariusza, w którym poprawnie użyte sprzężenie byłoby wolniejsze niż równoważna operacja po stronie klienta.

Edycja: Istnieją rzadkie przypadki, w których niestandardowy kod klienta może działać wydajniej niż zwykłe łączenie DB (patrz komentarz meriton). Ale to bardzo wyjątek.

Michael Borgwardt
źródło
1
A co z łączeniami trójstronnymi? Czy nie ma przypadków, w których lepiej byłoby zrobić to „w kodzie”?
julien_c
56
Łączenie na serwerze aplikacji może być bardziej wydajne, jeśli dołączanie do bazy danych powoduje poważną redundancję w zestawie wyników wysyłanym przez sieć. Rozważmy tabele A i B, w których każdy wiersz w A jest powiązany z 20 wierszami w B, B ma tylko 100 wierszy, a my chcemy pobrać pierwsze 1000 wierszy z A wraz z powiązanymi wierszami z B. Połączenie w bazie danych spowoduje 20 * 1000 krotek wysłanych przez sieć. Jeśli łączenie jest wykonywane na serwerze aplikacji (najpierw pobiera całą tabelę B do pamięci), przez sieć jest wysyłanych zaledwie 100 + 1000 wierszy.
meriton
7
Jednak z pewnością masz rację, że łączenia w bazie danych są w większości przypadków znacznie szybsze, a zatem nie tylko kwestią wygody, ale także konieczności.
meriton
13
Miałem szczęście rozmawiać z niektórymi programistami, którzy pracują nad SQL Server w firmie Microsoft. Przyprawi Cię o zawrót głowy, słysząc optymalizacje, które wykonują w zapytaniach. Każdy, kto myśli, że jest mądrzejszy, musi zostać uderzony.
riwalk
2
@meriton Jestem trochę zaskoczony; Spodziewałbym się, że biblioteka klienta zoptymalizuje sprzężenia krzyżowe.
Phil Lello
83

Wydaje mi się, że twój kolega poradziłby sobie dobrze z bazą danych dokumentów bez sql lub magazynem wartości klucza. Które same w sobie są bardzo dobrymi narzędziami i dobrze nadają się do wielu problemów.

Jednak relacyjna baza danych jest mocno zoptymalizowana do pracy z zestawami. Istnieje wiele, wiele sposobów sprawdzania danych w oparciu o sprzężenia, które są znacznie bardziej wydajne niż wiele podróży w obie strony. Stąd bierze się wszechstronność Rdbms. Możesz osiągnąć to samo w sklepie nosql, ale często kończy się to budowaniem oddzielnej struktury dostosowanej do każdego rodzaju zapytania.

Krótko mówiąc: nie zgadzam się. W RDBMS łączenia są fundamentalne . Jeśli ich nie używasz, nie używasz ich jako RDBMS.

Marc Gravell
źródło
46

Cóż, w ogólnym przypadku się myli.

Bazy danych są w stanie optymalizować przy użyciu różnych metod, wspomaganych przez wskazówki optymalizatora, indeksy tabel, relacje kluczy obcych i ewentualnie inne informacje specyficzne dla dostawcy bazy danych.

sehe
źródło
1
Muszę przyznać, że kiedy zaczynałem pracę z bazami danych, miałem takie samo przekonanie, że mogę pokonać wydajność łączenia. Jednak szybko zdałem sobie sprawę, jak niesamowicie szybkie łączenia są wykonywane przez DB. Właściwie powiedziałbym, że w tej sytuacji lepiej omówić to z pracownikiem w otwarty sposób, niż lekceważyć go jako idiotę.
LegendLength
1
@LegendLength Powiedziałbym, że to prawda, nawet jeśli nie są tak inteligentni. Nie musisz zakładać sprytu, ponieważ popełniają te same błędy, które popełniliśmy (w rzeczywistości dla mnie może to oznaczać, że nie są tak sprytni ...) To prostsze: rzadko pomaga lekceważenie. Dobrze jest się mylić, raz na jakiś czas!
sehe
24

Nie, nie powinieneś.

Bazy danych są specjalnie zaprojektowane do manipulowania zbiorami danych (oczywiście…). Dlatego są w tym niezwykle skuteczni. Robiąc to, co zasadniczo jest ręcznym dołączaniem do własnego kodu, próbuje przejąć rolę czegoś specjalnie zaprojektowanego do tego zadania. Szanse na to, że jego kod kiedykolwiek będzie tak wydajny, jak ten w bazie danych, są bardzo odległe.

Na marginesie, bez łączeń, jaki jest sens w korzystaniu z bazy danych? równie dobrze może po prostu używać plików tekstowych.

richzilla
źródło
2
Nawet bez połączeń? Automatyczne mapowanie w pamięci, automatyczne buforowanie zapytań, wiele innych automagicznych rzeczy, które nie występują w większości systemów plików. Och, czy wspomniałem o transakcjach, które można dokładnie kontrolować?
Piskvor opuścił budynek
19

Jeśli „leniwy” to ludzie, którzy chcą pisać mniej kodu, to zgadzam się. Jeśli „leniwy” to ludzie, którzy chcą, aby narzędzia robiły to, w czym są dobrzy, zgadzam się. Więc jeśli on po prostu zgadza się z Larrym Wallem (co do cech dobrych programistów), to ja się z nim zgadzam.

MJB
źródło
Dodałem precyzję leniwych: dla leniwych ludzi, którzy nie dbają o wydajność i wolą pisać mniej kodu. Myślę, że łączenia są dla leniwych ludzi, ale w tym przypadku łączenia są również lepsze niż kilka żądań.
Bastien Vandamme
3
@Dran Dane: Połączenia są dla leniwych, tak. Fakt, że prawdopodobnie będą one dobrze działać, jest ortogonalny.
Piskvor opuścił budynek
16

Ummm, łączenia to sposób, w jaki relacyjne bazy danych wiążą ze sobą tabele. Nie jestem pewien, do czego zmierza.

W jaki sposób wykonanie kilku wywołań bazy danych może być bardziej wydajne niż jedno wywołanie? Dodatkowo silniki sql są zoptymalizowane do robienia tego typu rzeczy.

Może twój współpracownik jest zbyt leniwy, by nauczyć się SQL.

Giovanni Galbo
źródło
12

Tak, powinieneś.

Ze względu na wydajność powinieneś używać C ++ zamiast C #. C # jest dla leniwych.

Nie nie nie. Ze względu na wydajność powinieneś używać C zamiast C ++. C ++ jest dla leniwych.

Nie nie nie. Ze względu na wydajność należy używać assemblera zamiast C. C jest dla leniwych.

Tak, żartuję. możesz tworzyć szybsze programy bez łączeń i możesz tworzyć programy wykorzystujące mniej pamięci bez łączeń. ALE w wielu przypadkach czas tworzenia oprogramowania jest ważniejszy niż czas procesora i pamięć. Zrezygnuj z występów i ciesz się życiem. Nie trać czasu na małą wydajność. I powiedz mu: „Dlaczego nie zrobisz prostej autostrady ze swojego mieszkania do swojego biura?”

RedPain
źródło
1
Przejrzałem wszystkie Twoje odpowiedzi do tej pory i są one bardzo zabawne. Proszę, nie przerywajcie ich. Albo to, albo gdzie mogę zasubskrybować twojego bloga?
Gerry
11

„To technicznie prawda” - podobnie baza danych SQL jest bezużyteczna: po co jej używać, skoro ten sam wynik można uzyskać, używając wielu plików CSV i skorelując je w kodzie? Heck, każda abstrakcja jest dla leniwych, wróćmy do programowania w kodzie maszynowym bezpośrednio na sprzęcie! ;)

Ponadto, jego asssertion jest nieprawdą we wszystkich, ale najbardziej zawiłe przypadkach: RDBMSs są mocno zoptymalizowany do make JOIN szybko . Systemy zarządzania relacyjnymi bazami danych, prawda?

Piskvor opuścił budynek
źródło
2
+1 Wyrażenie „… technicznie prawdziwe” działałoby lepiej, gdyby PO użył sformułowania unnecessaryraczej uselessw poprzednim zdaniu. Mówienie, że łączenia są bezużyteczne, jest ewidentnie nieprawdziwe i nie wymaga rozważenia żadnych kwestii technicznych. W każdym razie niezrozumienie przez OP i kolegę sensu RDBMS nie jest niczym niezwykłym: stackoverflow.com/q/5575682/47550
Paul Sasik
7

Ostatnia firma, dla której pracowałem, również nie używała sprzężeń SQL. Zamiast tego przenieśli tę pracę do warstwy aplikacji, która jest zaprojektowana do skalowania w poziomie. Uzasadnieniem tego projektu jest uniknięcie pracy w warstwie bazy danych. Zwykle wąskim gardłem staje się baza danych. Łatwiej replikować warstwę aplikacji niż bazę danych. Powody mogą być inne. Ale to jest ten, który teraz sobie przypominam.

Tak, zgadzam się, że połączenia wykonywane w warstwie aplikacji są nieefektywne w porównaniu do połączeń wykonywanych przez bazę danych. Więcej komunikacji sieciowej również.

Zwróć uwagę, że nie zajmuję twardych stanowisk w kwestii unikania złączeń SQL.

Srikanth
źródło
Cóż, to brzmi jak racjonalny argument przeciwko JOIN w twoim konkretnym przypadku. Pamiętam, że FB Engineering zamieściło coś podobnego na swoim blogu - skalowanie było również ich głównym priorytetem. Niestety, tylko niewielki% programistów kiedykolwiek będzie musiał to zrobić, ale wielu uważa , że tak robi „ponieważ OMG Facebook też to robi”;)
Piskvor opuścił budynek
w porządku, w rozwiązaniu korporacyjnym, w którym ruch jest wystarczający do przeciążenia serwera bazy danych, warto to rozważyć, ale bardziej prawdopodobne jest, że będzie to raportowanie procedury składowanej lub zaplanowana kopia zapasowa, która ma na celu osiągnięcie wydajności. Bazy danych są dobre w łączeniach, zwłaszcza jeśli są niezdatne do pomocy
Jodrell
@Jodrell: Tak, są dobrzy w łączeniach; znowu są przypadki narożne, w których musisz porzucić elegancję połączeń, aby uzyskać większą moc. Spotkałem się z jedną taką sytuacją; wypróbowaliśmy każde możliwe rozwiązanie i rzeczywiście rozwiązanie bez łączenia było najszybsze w tej jednej bardzo specyficznej sytuacji . I nie, nic innego nie działało na tym konkretnym serwerze; procedury składowane nie mogą Cię spowolnić, jeśli ich nie masz;)
Piskvor opuścił budynek 08.04.11
5

W jaki sposób bez łączenia chcesz powiązać pozycje zamówienia z zamówieniami? To jest cały punkt systemu zarządzania relacyjnymi bazami danych. Bez złączeń nie ma danych relacyjnych i równie dobrze możesz używać plików tekstowych do przetwarzania danych.

Wygląda na to, że nie rozumie tej koncepcji, więc stara się sprawiać wrażenie, że są bezużyteczne. To ten sam typ osoby, która uważa, że ​​program Excel to aplikacja bazodanowa. Uderz go głupio i powiedz, żeby przeczytał więcej o bazach danych. Wykonywanie wielu połączeń i pobieranie danych oraz scalanie danych za pośrednictwem C # to niewłaściwy sposób robienia rzeczy.

JonH
źródło
5

Nie rozumiem logiki stwierdzenia „łączenia w SQL są bezużyteczne”. Czy warto filtrować i ograniczać dane przed rozpoczęciem pracy nad nimi? Jak powiedzieliście inni respondenci, właśnie to robią silniki baz danych, w tym powinny być dobre.

Być może leniwy programista trzymałby się technologii, z którymi był zaznajomiony i unikał innych możliwości z powodów nietechnicznych.

Decyzję pozostawiam Tobie.

Jodrell
źródło
5

Rozważmy przykład: tabela z rekordami faktur i powiązana tabela z rekordami pozycji faktury. Rozważ pseudokod klienta:

for each (invoice in invoices)
    let invoiceLines = FindLinesFor(invoice)
...

Jeśli masz 100 000 faktur z 10 wierszami każda, ten kod wyszuka 10 wierszy faktur z tabeli zawierającej 1 milion i zrobi to 100 000 razy. Wraz ze wzrostem rozmiaru tabeli rośnie liczba operacji wyboru i koszt każdej operacji wyboru.

Ponieważ komputery są szybkie, możesz nie zauważyć różnicy w wydajności między tymi dwoma podejściami, jeśli masz kilka tysięcy rekordów lub mniej. Ponieważ wzrost kosztów jest bardziej niż liniowy, wraz ze wzrostem liczby rekordów (powiedzmy do milionów) zaczniesz zauważać różnicę, a różnica stanie się mniej akceptowalna wraz ze wzrostem rozmiaru zestawu danych.

Jednak połączenie. użyje indeksów tabeli i połączy dwa zestawy danych. Oznacza to, że efektywnie skanujesz drugą tabelę raz, zamiast losowego dostępu do niej N razy. Jeśli zdefiniowano klucz obcy, baza danych zawiera już łącza między powiązanymi rekordami przechowywanymi wewnętrznie.

Wyobraź sobie, że robisz to sam. Masz alfabetyczną listę uczniów i notatnik z raportami wszystkich uczniów (jedna strona na zajęcia). Notatnik jest posortowany według nazwisk uczniów, w tym samym porządku co lista. Jak wolałbyś kontynuować?

  1. Przeczytaj nazwisko z listy.
  2. Otwórz notatnik.
  3. Znajdź nazwisko ucznia.
  4. Przeczytaj oceny ucznia, przewracaj strony, aż dotrzesz do następnego ucznia lub do ostatniej strony.
  5. Zamknij notatnik.
  6. Powtarzać.

Lub:

  1. Otwórz notatnik na pierwszej stronie.
  2. Przeczytaj nazwisko z listy.
  3. Przeczytaj wszystkie oceny dla tego nazwiska z notatnika.
  4. Powtarzaj kroki 2-3, aż dojdziesz do końca
  5. Zamknij notatnik.
phoog
źródło
5

Brzmi jak klasyczny przypadek „ Mogę to lepiej napisać ”. Innymi słowy, widzi coś, co postrzega jako ból w gardle (pisząc kilka złączeń w SQL) i mówi: „Jestem pewien, że mogę napisać to lepiej i uzyskać lepszą wydajność”. Powinieneś zapytać go, czy jest a) mądrzejszy i b) bardziej wykształcony niż typowa osoba, która jest po kolana w kodzie optymalizacji Oracle lub SQL Server. Możliwe, że nie jest.

jcollum
źródło
3

Z całą pewnością się myli. Chociaż istnieją pewne zalety manipulacji danymi w językach takich jak C # czy Java, sprzężenia są najszybsze w bazie danych ze względu na naturę samego języka SQL.

SQL prowadzi szczegółowe statystyki dotyczące danych i jeśli poprawnie utworzyłeś swoje indeksy, może bardzo szybko znaleźć jeden rekord na kilka milionów. Poza tym, dlaczego miałbyś chcieć przeciągać wszystkie swoje dane do C #, aby wykonać sprzężenie, skoro możesz to zrobić bezpośrednio na poziomie bazy danych?

Zalety korzystania z języka C # wchodzą w grę, gdy trzeba wykonać coś iteracyjnie. Jeśli musisz wykonać jakąś funkcję dla każdego wiersza, prawdopodobnie jest to szybsze w C #, w przeciwnym razie łączenie danych jest optymalizowane w bazie danych.

Mike M.
źródło
3

Powiem, że trafiłem na przypadek, w którym szybciej było rozbić zapytanie i wykonać złączenia w kodzie. Mając to na uwadze, musiałem to zrobić tylko z jedną konkretną wersją MySQL. Wszystko inne, baza danych prawdopodobnie będzie szybsza (pamiętaj, że być może będziesz musiał zoptymalizować zapytania, ale nadal będzie szybsza).

JaCraig
źródło
3

Podejrzewam, że ma ograniczony pogląd na to, do czego należy używać baz danych. Jednym ze sposobów maksymalizacji wydajności jest wczytanie całej bazy danych do pamięci. W tej sytuacji możesz uzyskać lepszą wydajność i możesz chcieć wykonać sprzężenia, jeśli pamięć jest wydajna. Jednak tak naprawdę nie jest to korzystanie z bazy danych, jako bazy danych IMHO.

Peter Lawrey
źródło
3
Większość silników baz danych i tak zrobi to za Ciebie za kulisami; i np. w MySQL można utworzyć tabelę czysto pamięciową ( MEMORYsilnik). Ponowne wdrożenie funkcjonalności bazy danych bez bazy danych jest zwykle oznaką poważnego przypadku NIH;)
Piskvor opuścił budynek 08.04.11
@phoog: Not Invented Here - innymi słowy: „Nie pomyślałem o tym, więc to nie istnieje”. Z tego powodu wiele kwadratowych kół zostało ponownie wynalezionych. (i tak, czasami ponowne wynalezienie koła jest przydatne, np. jeśli tworzysz samochody wyścigowe; ponowne wymyślanie „tylko dlatego” raczej nie
zapewni
Innymi słowy: „Nie udało mi się, więc to musi być bzdura”. To ma ziarno prawdy tylko do tego stopnia, że ​​„nie testowałem tego, więc może nie nadawać się do moich celów”, więc przetestuj to, zanim to ocenisz.
Peter Lawrey
@Piskvor: Niekoniecznie, baza danych może korzystać tylko z pamięci systemu, na którym działa, podczas gdy aplikacja może korzystać z pamięci serwera aplikacji. Mówiąc inaczej: jeśli baza danych znajduje się na dedykowanym hoście, dostęp do tej pamięci podręcznej nadal wymaga przepustowości sieci i podlega opóźnieniom sieci, ale każda pamięć podręczna przechowywana przez aplikację może być odpytywana z szybkością i małym opóźnieniem dostępu do pamięci.
meriton
2

Nie, nie tylko połączenia są lepiej zoptymalizowane w kodzie bazy danych, który ad-hoc C # / Java; ale zwykle można zastosować kilka technik filtrowania, co zapewnia jeszcze lepszą wydajność.

Jonas Byström
źródło
2

Myli się, łączenia są tym, czego używają kompetentni programiści. Może istnieć kilka ograniczonych przypadków, w których proponowana przez niego metoda jest bardziej wydajna (i prawdopodobnie korzystałbym z bazy danych Documant), ale nie widzę tego, jeśli masz jakąś oszukańczą ilość danych. Na przykład weź to zapytanie:

select t1.field1 
from table1 t1
join table2 t2 
    on t1.id = t2.id
where t1.field2 = 'test'

Załóżmy, że masz 10 milionów rekordów w tabeli 1 i 1 milion rekordów w tabeli 2. Załóżmy, że 9 milionów rekordów w tabeli 1 spełnia klauzulę where. Załóżmy, że tylko 15 z nich znajduje się również w tabeli 2. Możesz uruchomić tę instrukcję sql, która po prawidłowym indeksowaniu zajmie milisekundy i zwróci 15 rekordów w sieci z tylko 1 kolumną danych. Możesz też wysłać dziesięć milionów rekordów z 2 kolumnami danych i osobno wysłać kolejny 1 milion rekordów z jedną kolumną danych przez sieć i połączyć je na serwerze internetowym.

Lub oczywiście możesz przechowywać całą zawartość bazy danych na serwerze sieciowym przez cały czas, co jest po prostu głupie, jeśli masz więcej niż trywialną ilość danych i danych, które ciągle się zmieniają. Jeśli nie potrzebujesz cech relacyjnej bazy danych, nie używaj jej. Ale jeśli to zrobisz, użyj go poprawnie.

HLGEM
źródło
2

Często słyszałem ten argument w swojej karierze programisty. Niemal za każdym razem, gdy zostało to powiedziane, osoba składająca roszczenie nie miała zbyt dużej wiedzy na temat systemów relacyjnych baz danych, ich działania i sposobu, w jaki takie systemy powinny być używane.

Tak, nieprawidłowo użyte połączenia wydają się bezużyteczne lub nawet niebezpieczne. Ale jeśli jest używany we właściwy sposób, implementacja bazy danych ma duży potencjał, aby przeprowadzić optymalizacje i „pomóc” programiście w najbardziej efektywnym uzyskaniu poprawnych wyników.

Nie zapominaj, że korzystając z narzędzia, JOINmożesz poinformować bazę danych o sposobie, w jaki spodziewasz się, że fragmenty danych będą ze sobą powiązane, a tym samym przekaż bazie danych więcej informacji o tym , co próbujesz zrobić, a tym samym sprawi, że będzie ona lepiej dopasowana do Twoich potrzeb.

Więc odpowiedź brzmi zdecydowanie: nie, JOINSwcale nie są bezużyteczne!

perdian
źródło
0

Jest to „technicznie prawdziwe” tylko w jednym przypadku, który nie jest często używany w aplikacjach (gdy wszystkie wiersze wszystkich tabel w złączeniach są zwracane przez zapytanie). W większości zapytań zwracany jest tylko ułamek wierszy każdej tabeli. Silnik bazy danych często używa indeksów w celu wyeliminowania niechcianych wierszy, czasem nawet bez odczytywania rzeczywistego wiersza, ponieważ może używać wartości przechowywanych w indeksach. Sam silnik bazy danych jest napisany w językach C, C ++ itp. I jest co najmniej tak wydajny, jak kod napisany przez programistę.

fredt
źródło
0

O ile poważnie nie zrozumiałem, logika tego pytania jest bardzo błędna

Jeśli jest 20 wierszy w B dla każdego A, 1000 wierszy w A oznacza 20 tys. Wierszy w B. Nie może być tylko 100 wierszy w B, chyba że istnieje wiele-wiele tabeli „AB” z 20 000 wierszami zawierającymi odwzorowanie .

Aby uzyskać wszystkie informacje o tym, które 20 ze 100 wierszy B jest mapowanych na każdy wiersz A, również tabela AB. Więc to byłoby albo:

  • 3 zestawy wyników po 100, 1000 i 20 tys. Wierszy oraz klient JOIN
  • pojedynczy zestaw wyników JOINed A-AB-B z 20 tys. wierszy

Tak więc „JOIN” w kliencie nie dodaje żadnej wartości podczas badania danych. Nie żeby to nie był zły pomysł. Gdybym pobierał jeden obiekt z bazy danych, może bardziej sensowne byłoby rozbicie go na oddzielne zestawy wyników. W przypadku połączenia typu raportu prawie zawsze spłaszczam go w jeden.

W każdym razie powiedziałbym, że połączenie krzyżowe tej wielkości prawie nie ma sensu. To kiepski przykład.

Musisz gdzieś DOŁĄCZYĆ i właśnie w tym RDBMS są dobre. Nie chciałbym pracować z żadną małpą kodu klienta, która uważa, że ​​może zrobić lepiej.

Refleksja:

Aby dołączyć do klienta, wymagane są trwałe obiekty, takie jak DataTables (w .net). Jeśli masz jeden spłaszczony zestaw wyników, można go zużyć za pomocą czegoś lżejszego, takiego jak DataReader. Duża ilość = dużo zasobów klienta używanych w celu uniknięcia JOIN bazy danych.

gbn
źródło