Powiedzmy na przykład, że chcę pobrać użytkownika oraz wszystkie jego numery telefonów i adresy e-mail. Numery telefonów i e-maile są przechowywane w osobnych tabelach, jeden użytkownik do wielu telefonów / e-maili. Mogę to zrobić dość łatwo:
SELECT * FROM users user
LEFT JOIN emails email ON email.user_id=user.id
LEFT JOIN phones phone ON phone.user_id=user.id
Problem * polega na tym, że zwraca nazwę użytkownika, DOB, ulubiony kolor i wszystkie inne informacje przechowywane w tabeli użytkowników w kółko dla każdego rekordu (użytkownicy wysyłają e-maile z rekordów przez telefon), prawdopodobnie pochłaniając przepustowość i spowalniając w dół wyników.
Czy nie byłoby lepiej, gdyby zwrócił jeden wiersz dla każdego użytkownika, a w tym rekordzie znajdowała się lista e-maili i lista telefonów? Ułatwi to również pracę z danymi.
Wiem, że możesz uzyskać takie wyniki za pomocą LINQ lub być może innych frameworków, ale wydaje się, że jest to słabość w projekcie baz relacyjnych baz danych.
Możemy to obejść za pomocą NoSQL, ale czy nie powinno być czegoś pośredniego?
Czy coś brakuje? Dlaczego to nie istnieje?
* Tak, jest zaprojektowany w ten sposób. Rozumiem. Zastanawiam się, dlaczego nie ma alternatywy, z którą łatwiej byłoby pracować. SQL może nadal robić to, co robi, ale może dodać słowo kluczowe lub dwa, aby wykonać trochę przetwarzania końcowego, które zwraca dane w formacie zagnieżdżonym zamiast produktu kartezjańskiego.
Wiem, że można to zrobić w wybranym przez Ciebie języku skryptowym, ale wymaga to, aby serwer SQL wysłał zbędne dane (przykład poniżej) lub abyś mógł zadawać wiele takich zapytań SELECT email FROM emails WHERE user_id IN (/* result of first query */)
.
Zamiast zwracania przez MySQL czegoś podobnego do tego:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "[email protected]",
},
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "[email protected]",
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"email": "[email protected]",
}
]
A następnie trzeba zgrupować na jednym unikalnym identyfikatorze (co oznacza, że ja też muszę go pobrać!) Po stronie klienta, aby sformatować zestaw wyników w odpowiedni sposób, po prostu zwróć to:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"emails": ["[email protected]", "[email protected]"]
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"emails": ["[email protected]"],
}
]
Alternatywnie mogę zadać 3 zapytania: 1 dla użytkowników, 1 dla e-maili i 1 dla numerów telefonów, ale następnie zestawy wyników dla adresu e-mail i numeru telefonu muszą zawierać identyfikator_użytkownika, aby móc dopasować je z powrotem do użytkowników Wcześniej ściągałem. Znowu zbędne dane i niepotrzebne przetwarzanie końcowe.
Odpowiedzi:
Głęboko, w trzewiach relacyjnej bazy danych, wszystkie wiersze i kolumny. Jest to struktura, z którą relacyjna baza danych jest zoptymalizowana do pracy. Kursory pracują jednocześnie w poszczególnych rzędach. Niektóre operacje tworzą tabele tymczasowe (znowu muszą to być wiersze i kolumny).
Pracując tylko z wierszami i zwracając tylko te wiersze, system jest w stanie lepiej radzić sobie z pamięcią i ruchem sieciowym.
Jak wspomniano, pozwala to na pewne optymalizacje (indeksy, złączenia, związki itp.)
Jeśli chcemy mieć zagnieżdżoną strukturę drzewa, wymaga to pobrania wszystkich danych naraz. Zniknęły optymalizacje kursorów po stronie bazy danych. Podobnie ruch w sieci staje się jedną dużą serią, która może trwać znacznie dłużej niż powolny strużka rząd po rzędzie (jest to coś, co czasami jest tracone w dzisiejszym świecie internetowym).
Każdy język ma w sobie tablice. Są to łatwe w obsłudze elementy i interfejs. Dzięki zastosowaniu bardzo prymitywnej struktury sterownik między bazą danych a programem - bez względu na język - może działać we wspólny sposób. Gdy zaczniesz dodawać drzewa, struktury w języku stają się bardziej złożone i trudniejsze do przejścia.
Język programowania nie jest tak trudny do konwersji wierszy zwróconych do innej struktury. Zrób z niego drzewo lub zestaw skrótów lub pozostaw go jako listę wierszy, nad którymi możesz iterować.
Działa tu także historia. Przenoszenie uporządkowanych danych było czymś brzydkim w dawnych czasach. Spójrz na format EDI, aby dowiedzieć się, o co możesz prosić. Drzewa sugerują również rekurencję - której niektóre języki nie obsługiwały (dwa najważniejsze języki dawnych czasów nie obsługiwały rekurencji - rekursja nie weszła do Fortran aż do F90, a epoki COBOL też nie).
I chociaż dzisiejsze języki obsługują rekurencję i bardziej zaawansowane typy danych, tak naprawdę nie ma dobrego powodu, aby to zmieniać. Działają i działają dobrze. Te, które są zmienne rzeczy są NoSQL baz danych. Możesz przechowywać drzewa w dokumentach w dokumencie opartym na dokumencie. LDAP (tak naprawdę stary) jest również systemem opartym na drzewach (choć prawdopodobnie nie jest tym, czego szukasz). Kto wie, może następną rzeczą w bazach nosql będzie ta, która zwróci zapytanie jako obiekt json.
Jednak „stare” relacyjne bazy danych ... pracują z wierszami, ponieważ w tym są dobrzy i wszystko może z nimi rozmawiać bez problemów lub tłumaczenia.
Od RFC 1925 - Dwanaście prawd sieciowych
źródło
Zwraca dokładnie to, o co prosiłeś: pojedynczy zestaw rekordów zawierający iloczyn kartezjański zdefiniowany przez złączenia. Istnieje wiele prawidłowych scenariuszy, w których dokładnie tego byś chciał, więc powiedzenie, że SQL daje zły wynik (a tym samym sugeruje, że byłoby lepiej, gdybyś go zmienił) faktycznie sprowadziłoby wiele zapytań.
To, czego doświadczasz, jest znane jako „ niedopasowanie obiektów / relacyjnych impedancji ”, trudności techniczne wynikające z faktu, że model danych obiektowych i model danych relacyjnych różnią się zasadniczo na kilka sposobów. LINQ i inne frameworki (znane jako ORM, obiektowe / relacyjne mapowania, nieprzypadkowo) nie magicznie „omijają to”; po prostu wydają różne zapytania. Można to zrobić również w języku SQL. Oto jak bym to zrobił:
Iteruj listę użytkowników i stwórz listę identyfikatorów.
A potem robisz dołączanie po stronie klienta. W ten sposób robią to LINQ i inne frameworki. Nie ma w tym żadnej prawdziwej magii; tylko warstwa abstrakcji.
źródło
Możesz użyć wbudowanej funkcji, aby połączyć rekordy razem. W MySQL możesz użyć tej
GROUP_CONCAT()
funkcji, aw Oracle możesz użyć tejLISTAGG()
funkcji.Oto przykład, jak mogłoby wyglądać zapytanie w MySQL:
To zwróci coś w rodzaju
źródło
Problem polega na tym, że nie jesteś wystarczająco selektywny. Prosiłeś o wszystko, kiedy mówiłeś
... i masz to (w tym DOB i ulubione kolory).
Prawdopodobnie powinieneś być trochę bardziej (ahm) ... selektywny i powiedział coś w stylu:
Możliwe jest również, że zobaczysz rekordy, które wyglądają jak duplikaty, ponieważ
user
mogą dołączyć do wieluemail
rekordów, ale pole, które je rozróżnia, nie znajduje się wSelect
wyciągu, więc możesz chcieć powiedzieć coś w rodzajuZauważam też, że robisz
LEFT JOIN
. Spowoduje to połączenie wszystkich rekordów po lewej stronie sprzężenia (tj.users
) Do wszystkich rekordów po prawej stronie lub innymi słowy:( http://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join )
Kolejnym pytaniem jest to, czy faktycznie potrzebujesz lewego złączenia, czy może
INNER JOIN
wystarczyłoby? Są to bardzo różne rodzaje złączeń.Jeśli chcesz, aby pojedyncza kolumna w zestawie wyników zawierała listę generowaną w locie, można to zrobić, ale różni się ona w zależności od używanej bazy danych. Oracle ma tę
listagg
funkcję .Ostatecznie myślę, że Twój problem może zostać rozwiązany, jeśli przepiszesz zapytanie blisko czegoś takiego:
źródło
left join
nainner join
. W takim przypadku nie ograniczy to „powtórzeń”, na które skarży się użytkownik; po prostu pomija użytkowników, którzy nie mają telefonu ani e-maila. prawie żadna poprawa. również przy interpretacji „wszystkich rekordów po lewej stronie do wszystkich rekordów po prawej stronie” pomijaON
kryteria, które oczyszczają wszystkie „złe” relacje właściwe dla produktu kartezjańskiego, ale zachowują wszystkie powtarzające się pola.Kwerendy zawsze tworzą prostokątny (nie strzępiony) zestaw danych tabelarycznych. W zestawie nie ma zagnieżdżonych podzestawów. W świecie zestawów wszystko jest czystym nie zagnieżdżonym prostokątem.
Możesz pomyśleć o złączeniu jako umieszczeniu 2 zestawów obok siebie. Warunkiem włączenia jest dopasowanie rekordów w każdym zestawie. Jeśli użytkownik ma 3 numery telefonów, w informacjach o użytkowniku zobaczysz 3-krotne duplikowanie. Kwerenda musi wygenerować prostokątny zestaw bez postrzępień. To po prostu natura łączenia zestawów z relacją jeden do wielu.
Aby uzyskać to, czego potrzebujesz, musisz użyć osobnego zapytania, takiego jak opisany Mason Wheeler.
Wynikiem tego zapytania jest wciąż prostokątny zbiór bez postrzępień. Jak wszystko w świecie zestawów.
źródło
Musisz zdecydować, gdzie istnieją wąskie gardła. Przepustowość między bazą danych a aplikacją jest zwykle dość duża. Nie ma powodu, dla którego większość baz danych nie mogła zwrócić 3 oddzielnych zestawów danych w ramach jednego połączenia i żadnych połączeń. Następnie możesz dołączyć do tego wszystkiego razem w swojej aplikacji, jeśli chcesz.
W przeciwnym razie chcesz, aby baza danych połączyła ten zestaw danych, a następnie usunęła wszystkie powtarzające się wartości w każdym wierszu, które są wynikiem złączeń i niekoniecznie same wiersze zawierające zduplikowane dane, takie jak dwie osoby o tej samej nazwie lub numerze telefonu. Wydaje się, że dużo narzutów pozwala zaoszczędzić na przepustowości. Lepiej skoncentruj się na zwracaniu mniejszej ilości danych dzięki lepszemu filtrowaniu i usuwaniu niepotrzebnych kolumn. Ponieważ Select * nigdy nie jest stosowany w studniach produkcyjnych, które zależą.
źródło
Bardzo prosto, nie dołączaj swoich danych, jeśli chcesz uzyskać odrębne wyniki dla zapytania użytkownika i zapytania numeru telefonu, w przeciwnym razie, jak zauważyli inni, „Zestaw” lub dane będą zawierać dodatkowe pola dla każdego wiersza.
Zadaj 2 różne zapytania zamiast jednego ze złączeniem.
W procedurze przechowywanej lub sparametryzowanej kwerendie sql craft 2 zapytania i zwróć wyniki obu z powrotem. Większość baz danych i języków obsługuje wiele zestawów wyników.
Na przykład SQL Server i C # realizują tę funkcję za pomocą
IDataReader.NextResult()
.źródło
Coś brakuje. Jeśli chcesz zdenormalizować swoje dane, musisz to zrobić sam.
źródło
Pojęcie zamknięcia relacyjnego zasadniczo oznacza, że wynikiem każdego zapytania jest relacja, która może być używana w innych zapytaniach, tak jakby była tabelą podstawową. Jest to potężna koncepcja, ponieważ sprawia, że zapytania można komponować.
Jeśli SQL pozwoli ci pisać zapytania, które generują zagnieżdżone struktury danych, złamiesz tę zasadę. Zagnieżdżona struktura danych nie jest relacją, więc potrzebujesz nowego języka zapytań lub złożonych rozszerzeń SQL, aby móc dalej wyszukiwać lub dołączać do innych relacji.
Zasadniczo zbudowałbyś hierarchiczny DBMS na bazie relacyjnego DBMS. Będzie to znacznie bardziej skomplikowane dla wątpliwej korzyści, a ty stracisz zalety konsekwentnie relacyjnego systemu.
Rozumiem, dlaczego czasami wygodniej jest wyprowadzać hierarchicznie ustrukturyzowane dane z SQL, ale koszt dodatkowej złożoności w całym systemie DBMS z pewnością nie jest tego wart.
źródło
Pls odnoszą się do użycia funkcji STUFF, która grupuje wiele wierszy (numery telefonów) kolumny (kontaktu), które można wyodrębnić jako pojedynczą komórkę z ograniczonymi wartościami wiersza (użytkownika).
Dzisiaj intensywnie z tego korzystamy, ale napotykamy na problemy z wysoką wydajnością i procesorem. Typ danych XML jest inną opcją, ale jest zmianą projektu, a nie poziomem zapytania.
źródło
STUFF
to, że jest podobny do łączenia. Nie jestem pewien, jak to odnosi się do mojego pytania.