Wczoraj rozmawiałem z programistą „hobby” (sam jestem profesjonalnym programistą). Spotkaliśmy się z niektórymi jego pracami i powiedział, że zawsze odpytuje wszystkie kolumny w swojej bazie danych (nawet na / w serwerze / kodzie produkcyjnym).
Próbowałem go przekonać, żeby tego nie robił, ale nie odniosłem jeszcze takiego sukcesu. Moim zdaniem programista powinien zapytać tylko, co jest faktycznie potrzebne ze względu na „ładność”, wydajność i ruch. Czy mylę się z moim poglądem?
Odpowiedzi:
Pomyśl o tym, co otrzymujesz i jak powiążesz je ze zmiennymi w kodzie.
Zastanów się teraz, co się stanie, gdy ktoś zaktualizuje schemat tabeli, aby dodać (lub usunąć) kolumnę, nawet taką, z której nie korzystasz bezpośrednio.
Używanie select * podczas pisania zapytań ręcznie jest w porządku, a nie podczas pisania zapytań o kod.
źródło
Zmiany schematu
foo
, a inna tabela w zapytaniu dodaje kolumnęfoo
, sposób, w jaki jest ona obsługiwana, może powodować problemy przy próbie uzyskania właściwejfoo
kolumny.Tak czy inaczej zmiana schematu może powodować problemy z wyodrębnieniem danych.
Ponadto rozważ, czy używana kolumna została usunięta z tabeli.
select * from ...
Nadal działa, ale błędy się, kiedy próbuje wyciągnąć dane z tabeli wynikowej. Jeśli kolumna jest podana w zapytaniu, zapytanie spowoduje błąd, dając wyraźne wskazanie, co i gdzie jest problem.Obciążenie danych
Niektóre kolumny mogą być powiązane z dużą ilością danych. Wybranie z powrotem
*
spowoduje pobranie wszystkich danych. Tak, otovarchar(4096)
to na 1000 wierszy, które wybrałeś z powrotem, dając ci dodatkowe 4 megabajty danych, których nie potrzebujesz, ale i tak są przesyłane przez sieć.W związku ze zmianą schematu, ten varchar może nie istnieć tam, kiedy tworzyłeś tabelę po raz pierwszy, ale teraz ona tam jest.
Brak przekazania zamiaru
Gdy wybierzesz z powrotem
*
i otrzymasz 20 kolumn, ale potrzebujesz tylko 2 z nich, nie przekazujesz zamiaru kodu. Patrząc na zapytanie, któreselect *
je wykonuje, nie wiadomo, jakie są jego ważne części. Czy mogę zmienić zapytanie, aby użyć tego innego planu, aby przyspieszyć, nie uwzględniając tych kolumn? Nie wiem, ponieważ cel tego, co zwraca zapytanie, nie jest jasny.Przyjrzyjmy się niektórym skrzypkom SQL, które eksplorują nieco zmiany schematu .
Po pierwsze, początkowa baza danych: http://sqlfiddle.com/#!2/a67dd/1
DDL:
SQL:
A kolumny wrócisz to
oneid=1
,data=42
,twoid=2
, iother=43
.Co się stanie, jeśli dodam kolumnę do tabeli pierwszej? http://sqlfiddle.com/#!2/cd0b0/1
I moje wyniki z tego samego zapytania, jak wcześniej to
oneid=1
,data=42
,twoid=2
, iother=foo
.Zmiana w jednej z tabel zaburza wartości a
select *
i nagle twoje powiązanie „innego” z int spowoduje zgłoszenie błędu i nie wiesz dlaczego.Jeśli zamiast tego twoja instrukcja SQL była
Zmiana w tabeli pierwszej nie zakłóciłaby danych. To zapytanie działa tak samo przed zmianą i po zmianie.
Indeksowanie
Kiedy robisz a
select * from
, ciągniesz wszystkie wiersze z wszystkich tabel, które pasują do warunków. Nawet stoły, na których tak naprawdę nie zależy. Chociaż oznacza to, że przesyłanych jest więcej danych, w dalszej części stosu czai się inny problem z wydajnością.Indeksy (powiązane z SO: Jak używać indeksu w instrukcji select? )
Jeśli wycofujesz wiele kolumn, optymalizator planu bazy danych może zignorować użycie indeksu, ponieważ i tak będziesz musiał pobrać wszystkie te kolumny, a użycie indeksu i pobranie wszystkich kolumn w zapytaniu zajęłoby więcej czasu. niż byłoby po prostu wykonać pełne skanowanie tabeli.
Jeśli po prostu wybierasz, powiedzmy, nazwisko użytkownika (które często robisz, a więc masz na nim indeks), baza danych może wykonać skanowanie tylko indeksu (skanowanie tylko indeksu postgres wiki , pełne skanowanie tabeli mysql vs pełne skanowania indeksu , indeksu Tylko scan: Unikanie tabeli programu Access ).
Jeśli jest to możliwe, istnieje sporo optymalizacji dotyczących odczytu tylko z indeksów. Informacje mogą być pobierane szybciej na każdej stronie indeksu, ponieważ również pobierasz mniej - nie pobierasz wszystkich innych kolumn dla
select *
. Możliwe jest, że skanowanie tylko indeksu zwraca wyniki 100 razy szybciej (źródło: Wybierz * jest złe ).Nie oznacza to, że pełne skanowanie indeksu jest świetne, nadal jest to pełne skanowanie - ale jest lepsze niż skanowanie pełnego stołu. Kiedy zaczniesz ścigać wszystkie sposoby, które
select *
szkodzą wydajności, wciąż znajdujesz nowe.Powiązane czytanie
źródło
select *
?Kolejny problem: jeśli jest to
JOIN
zapytanie, a wyniki zapytania są pobierane do tablicy asocjacyjnej (jak w przypadku PHP), jest podatne na błędy.Chodzi o to, że
foo
ma kolumnyid
iname
bar
ma kolumnyid
iaddress
,SELECT * FROM foo JOIN bar ON foo.id = bar.id
zgadnij, co się stanie, gdy ktoś doda kolumnę
name
dobar
tabeli.Kod nagle przestanie działać poprawnie, ponieważ teraz
name
kolumna pojawia się w wynikach dwa razy, a jeśli przechowujesz wyniki w tablicy, dane z secondname
(bar.name
) zastąpią pierwsząname
(foo.name
)!To dość paskudny błąd, ponieważ jest bardzo nieoczywisty. To może chwilę potrwać, a osoba dodająca kolejną kolumnę do stołu nie mogła przewidzieć takiego niepożądanego efektu ubocznego.
(Prawdziwa historia).
Więc nie używaj
*
, kontroluj, które kolumny pobierasz i używaj aliasów tam, gdzie jest to właściwe.źródło
SELECT
klauzuli, i wtedy, gdy masz nadzieję, że zauważysz, że nazwa nie jest unikalna. BTW Nie sądzę, że jest to tak rzadkie w systemach z dużymi bazami danych. Jak powiedziałem, kiedyś spędziłem kilka godzin na polowaniu na tego błędu w wielkim błocie kodu PHP. I właśnie znalazłem inny przypadek: stackoverflow.com/q/17715049/168719W wielu przypadkach zapytanie każdej kolumny może być całkowicie uzasadnione.
Zawsze zapytanie o każdą kolumnę nie jest.
Jest to więcej pracy dla silnika bazy danych, który musi się uruchomić i przeszukiwać wewnętrzne metadane, aby dowiedzieć się, z którymi kolumnami musi się uporać, zanim będzie mógł zająć się prawdziwym biznesem polegającym na otrzymywaniu danych i wysyłaniu ich z powrotem do ciebie. OK, nie jest to największy narzut na świecie, ale katalogi systemowe mogą być znaczącym wąskim gardłem.
To więcej pracy dla Twojej sieci, ponieważ wycofujesz dowolną liczbę pól, gdy możesz chcieć tylko jednego lub dwóch z nich. Jeśli ktoś [inny] pójdzie i doda kilka tuzinów dodatkowych pól, z których wszystkie zawierają duże fragmenty tekstu, twoja przepustowość nagle przejdzie przez podłogę - bez wyraźnego powodu. Sytuacja staje się jeszcze gorsza, jeśli twoja klauzula „where” nie jest szczególnie dobra, a także wycofujesz wiele wierszy - to potencjalnie dużo danych wędruje przez sieć do ciebie (tj. Będzie wolno).
To więcej pracy dla Twojej aplikacji, konieczność wycofania i zapisania wszystkich tych dodatkowych danych, które prawdopodobnie nie obchodzą.
Ryzykujesz, że kolumny zmienią ich kolejność. OK, nie powinieneś się tym martwić (i nie zrobisz tego, jeśli wybierzesz tylko kolumny, których potrzebujesz), ale jeśli przejdziesz do nich wszystkie naraz, a ktoś [inny] zdecyduje się zmienić kolejność kolumn w tabeli , ten starannie spreparowany eksport CSV, który przekazujesz kontom w korytarzu, nagle przechodzi do puli - znowu, bez wyraźnego powodu.
BTW, kilka razy mówiłem „ktoś [jeszcze]”. Pamiętaj, że bazy danych są z natury wieloużytkownikowe; możesz nie mieć nad nimi kontroli, tak jak myślisz.
źródło
TOP
ograniczenie; Nie jestem pewien, jak ważne jest to, że kod odczytuje tyle, ile chce wyświetlić, a następnie usuwa zapytanie. Myślę, że odpowiedzi na zapytania są przetwarzane nieco leniwie, chociaż nie znam szczegółów. W każdym razie uważam, że zamiast mówić, że „nie jest uzasadniony”, lepiej byłoby powiedzieć „… jest uzasadniony w znacznie mniejszej liczbie”; w zasadzie podsumowałbym uzasadnione przypadki jako te, w których użytkownik miałby lepszy pomysł, co jest znaczący niż programista.Krótka odpowiedź brzmi: zależy od tego, jakiej bazy danych używają. Relacyjne bazy danych są zoptymalizowane do wydobywania potrzebnych danych w szybki, niezawodny i atomowy sposób. W przypadku dużych zestawów danych i złożonych zapytań jest to znacznie szybsze i prawdopodobnie bezpieczniejsze niż WYBIERANIE * i wykonuje równoważenie złączeń po stronie „kodu”. Magazyny klucz-wartość mogą nie mieć zaimplementowanych takich funkcji lub mogą nie być wystarczająco dojrzałe, aby można je było wykorzystać w produkcji.
To powiedziawszy, nadal możesz wypełnić dowolną strukturę danych za pomocą SELECT * i wypracować resztę kodu, ale znajdziesz wąskie gardła wydajności, jeśli chcesz skalować.
Najbliższym porównaniem jest sortowanie danych: możesz użyć szybkiego sortowania lub bąbelkowego, a wynik będzie poprawny. Ale nie zostanie zoptymalizowany i na pewno będą mieć problemy, gdy wprowadzisz współbieżność i będziesz musiał sortować atomowo.
Oczywiście taniej jest dodawać pamięć RAM i procesory niż inwestować w programistę, który potrafi wykonywać zapytania SQL, a nawet ma niejasne zrozumienie, czym jest JOIN.
źródło
Customer customer = this._db.Customers.Where( “it.ID = @ID”, new ObjectParameter( “ID”, id ) ).First();
Zobacz czas na obrazę na stronie 2.var cmd = db.CreateCommand(); cmd.CommandText = "SELECT TOP 1 * FROM Customers WHERE ID = @ID"; cmd.Parameters.AddWithValue("@ID", id); var result = cmd.ExecuteReader();
.... a następnie przejdź do tworzenia klienta z każdego wiersza. LINQ pokonuje spodnie.var customer = _db.Customers.Where(it => it.id == id).First();
.IMO polega na byciu jawnym a niejawnym. Kiedy piszę kod, chcę, żeby działał, ponieważ sprawiłem, że działał, a nie tylko dlatego, że wszystkie części po prostu tam były. Jeśli przeszukujesz wszystkie rekordy, a Twój kod działa, będziesz miał tendencję do przechodzenia dalej. Później, jeśli coś się zmieni, a teraz twój kod nie działa, królewskim problemem jest debugowanie wielu zapytań i funkcji szukających wartości, która powinna tam być, a jedynymi wartościami odniesienia są *.
Również w podejściu wielopoziomowym nadal najlepiej jest izolować zakłócenia schematu bazy danych w warstwie danych. Jeśli warstwa danych przechodzi * do logiki biznesowej i najprawdopodobniej na warstwę prezentacji, rozszerzasz zakres debugowania wykładniczo.
źródło
select *
jest znacznie gorsze!ponieważ jeśli tabela otrzyma nowe kolumny, otrzymasz je wszystkie, nawet jeśli ich nie potrzebujesz. dzięki
varchars
temu może stać się wiele dodatkowych danych, które muszą być przesyłane z bazy danychniektóre optymalizacje DB mogą również wyodrębnić rekordy o nieokreślonej długości do osobnego pliku, aby przyspieszyć dostęp do części o stałej długości, używając select * pokonuje cel tego
źródło
Oprócz narzutu, czegoś, czego przede wszystkim chcesz uniknąć, powiedziałbym, że jako programista nie polegasz na kolejności kolumn zdefiniowanej przez administratora bazy danych. Wybierz każdą kolumnę, nawet jeśli potrzebujesz ich wszystkich.
źródło
Nie widzę żadnego powodu, dla którego nie powinieneś używać go do celu, w którym jest budowany - pobierz wszystkie kolumny z bazy danych. Widzę trzy przypadki:
Kolumna jest dodawana do bazy danych i chcesz ją również w kodzie. a) Z * zakończy się niepowodzeniem z prawidłowym komunikatem. b) Bez * będzie działać, ale nie zrobi tego, czego oczekujesz, co jest dość złe.
Kolumna jest dodawana do bazy danych i nie jest wymagana w kodzie. a) Z * zawiedzie; oznacza to, że * nie ma już zastosowania, ponieważ jego semantyka oznacza „pobierz wszystko”. b) Bez * będzie działać.
Usunięto kolumnę Kod nie powiedzie się w żaden sposób.
Teraz najczęstszym przypadkiem jest przypadek 1 (ponieważ użyłeś *, co oznacza, że najprawdopodobniej chcesz wszystkiego); bez * możesz mieć kod, który działa dobrze, ale nie działa zgodnie z oczekiwaniami, co jest znacznie, a najgorsze, kod, który nie działa z odpowiednim komunikatem błędu .
Nie biorę pod uwagę kodu, który pobiera dane kolumny na podstawie indeksu kolumny, który moim zdaniem jest podatny na błędy. Odzyskiwanie go na podstawie nazwy kolumny jest znacznie bardziej logiczne.
źródło
Select *
był przeznaczony raczej jako wygoda do tworzenia zapytań ad hoc, a nie do celów tworzenia aplikacji. Lub do użycia w konstrukcjach statystycznych, takich jakselect count(*)
który pozwala silnikowi zapytań zdecydować, czy użyć indeksu, którego indeksu użyć itd. I nie zwracasz żadnych rzeczywistych danych kolumny. Lub do użycia w klauzulach takich jakwhere exists( select * from other_table where ... )
, które ponownie są zaproszeniem do silnika zapytań, aby sam wybrał najbardziej wydajną ścieżkę, a podzapytanie służy tylko do ograniczenia wyników z głównego zapytania. Itd.select *
zawiera semantykę pobierania wszystkich kolumn; jeśli twoja aplikacja naprawdę tego potrzebuje, nie widzę powodów, dla których miałbyś z niej korzystać. Czy możesz wskazać jakieś odniesienie (Oracle, IBM, Microsoft itp.), Które wspomina, że celemselect *
kompilacji nie było pobranie wszystkich kolumn?select *
istnieje możliwość pobierania wszystkich kolumn ... jako wygodnej funkcji, do zapytań ad hoc, nie dlatego, że jest to świetny pomysł w oprogramowaniu produkcyjnym. Przyczyny są już dość dobrze omówione w odpowiedziach na tej stronie, dlatego nie stworzyłem własnej szczegółowej odpowiedzi: •) Problemy z wydajnością, wielokrotne zestawianie danych w sieci, których nigdy nie używasz, •) problemy z aliasingiem kolumn, •) awarie optymalizacji planu zapytań (w niektórych przypadkach nieużywanie indeksów), •) nieefektywne operacje we / wy serwera w przypadkach, w których ograniczony wybór mógł wykorzystywać wyłącznie indeksy itp.select *
w rzeczywistej aplikacji produkcyjnej, ale natura przypadku krawędzi jest taka, że nie jest to powszechny przypadek. :-)select *
; co mówiłem, jeśli naprawdę potrzebujesz wszystkich kolumn, nie widzę powodu, dla którego nie powinieneś tego używaćselect *
; choć niewiele musi istnieć scenariuszy, w których potrzebne są wszystkie kolumny.Pomyśl o tym w ten sposób ... jeśli przeszukujesz wszystkie kolumny z tabeli zawierającej tylko kilka małych ciągów lub pól numerycznych, to łącznie 100 000 danych. Zła praktyka, ale się spełni. Teraz dodaj jedno pole, które zawiera, powiedzmy, obraz lub dokument tekstowy o wielkości 10 MB. teraz twoje szybko wykonujące zapytanie natychmiast i w tajemniczy sposób zaczynają słabo działać, tylko dlatego, że pole zostało dodane do tabeli ... możesz nie potrzebować tego ogromnego elementu danych, ale ponieważ już to
Select * from Table
zrobiłeś.źródło