select *

Trafiłem niedawno na artykuł "The Case Against SELECT *", w którym autor podważa zasadność stosowania * w zapytaniach SQL.

You may be in the habit of using SELECT * to fetch fields in your SQL queries. It may seem like a great, flexible solution, but there are several very good reasons to break this habit and enumerate the fields in any query.

Zakładając, że select * from to najczęstsza sekwencja w całej historii SQL-a, zanegowanie jej sensu brzmi wręcz obrazoburczo i dosyć odważnie. Jednak obojętnie jak bardzo "genialnie oczywista" byłaby ta myśl lub jak bardzo pociągająca byłaby postawa wywrotowca, należy pamiętać, że zdanie "select * is evil" to uogólnienie. A każde uogólnienie na pewno ma jakieś słabe punkty. Wystarczy poszukać...

Self-Documentation Lost

Kod samodokumentujący to piękne i niewątpliwie cenne zjawisko. Zapewne łatwiej jest analizować zapytania SQL zawierające wyszczególnienie wszystkich interesujących pól, np.

select id, timestamp, title, author, text
from post
where id = @n;

niż zapytania ogólne typu

select *
from post
where id = @n;

Zwłaszcza w sytuacji braku aktualnej dokumentacji bazy (czyli zawsze), kiedy trudno na szybko stwierdzić co się kryje pod *.

Jednak istnieją takie zastosowania, gdzie * ma za zadanie specjalnie ukryć szczegóły kolumn tabeli. Najczęściej potrzeba taka występuje podczas definiowania view[1] ograniczającego zawartość tabeli do wierszy spełniających określony warunek, lub będących w jakiejś relacji, np.

create view commented_post as
select *
from post
where post.id in
(select postid from comment where hidden=0);

W dalszej kolejności będą wykonywane zapytania o view (select ... from commented_post). Dzięki zastosowaniu gwiazdki zmiana definicji tabeli nie pociąga za sobą konieczności zmiany opartego na niej view — jak dla mnie to zysk. Paradoksalnie w takiej sytuacji gwiazdka jest dobrą dokumentacją — "bierz wszystko z tabeli, cokolwiek tam jest, ale pod warunkiem, że...".

Catching Errors

Zmiany w strukturach tabel występują zawsze zbyt często i prawie zawsze powodują jakieś problemy — to jasne. Jeżeli odpytujemy bazę za pomocą select * zakładając, że w tabeli są określone pola, to:

The query will run absolutely fine, even though the coder's assumption is now wrong. [...] The database doesn't know anything is wrong because you're just saying "give me everything you've got" and it will comply.

Natomiast nie do końca zgadzam się ze zdaniem:

What we want to do is have a contract with the database. We want to tell it exactly what we want and it will tell us whether or not it's there.

Takie przeniesienie odpowiedzialności za poprawność zapytania na serwer SQL wcale nie musi być opłacalne. W złożonych aplikacjach "wyłapanie" błędu powstającego w momencie odwołania do nieistniejącego pola tabeli wydaje mi się tak samo proste (lub tak samo trudne, zależnie od punktu widzenia) przy stosowaniu zapytania select * jak przy zapytaniu select nie_istniejace_pole. A nawet powiedziałbym, że przy obsłudze po wykonaniu zapytania istnieje więcej możliwości.

Można się na przykład pokusić o jakąś próbę obejścia błędnej sytuacji lub jej "ładnego" obsłużenia, co przy zapytaniu wprost o nieistniejące pole jest co najmniej trudne (dostajemy błąd z bazy i koniec). Natomiast jeśli brak danego pola w tabeli jest absolutnie niedopuszczalny, to obsługa w kodzie programu może wygenerować błąd/wyjątek z komunikatem niosącym dokładnie taką samą informację jaka byłaby zwrócona przez serwer SQL wykonujący błędne zapytanie. Fakt, że będzie to obsłużone chwilę później (w kodzie przetwarzającym wynik, a nie w samym momencie uruchomienia zapytania na bazie) nie ma żadnego znaczenia. Jeśli znika z bazy pole, które uważamy za konieczne to i tak mamy wielki kłopot, wykrycie tego podczas testu sekundę wcześniej nie ratuje niczego.

Inefficiency

Why ask for everything when you'd be happy with less? Why send out a request for 10 fields, have the database do that extract work retrieving them, sending them back over the wire and processed and then not use them?

Jasne — to stara zasada dotycząca nie tylko zapytań do bazy, ale praktycznie dowolnego przypadku pobierania danych z jakiegokolwiek źródła.

Jednak potrafię sobie wyobrazić sytuacje, w których nieefektywność zapytania select * przestaje być oczywista. Na przykład w pewnej aplikacji WWW prezentacja danych z najróżniejszych tabel odbywa się zawsze na jednej stronie. Strona ta jest mocno konfigurowalna za pomocą ustawień np. w pliku XML, który określa sposób wyświetlania (w tym widoczność) każdej kolumny wybranej tabeli[2]. Uzyskanie informacji o tym, które kolumny są w danym momencie potrzebne, a które nie, wymaga analizy tego XML-a przed wykonaniem zapytania i później powtórnej jego analizy w momencie wyświetlania tabeli. Zakładając, że w tabelach obsługiwanych takim automatem nie ma wielu pól, które chcielibyśmy przemilczeć, może się okazać, że pociągnięcie dodatkowych danych z serwera bazy do serwera WWW będzie (paradoksalnie) mniej kosztowne (szybsze i mniej pamięciożerne) niż dodatkowe wstępne zastanawianie się nad konfiguracją.

Order Matters

Any time you fetch an array from a resultset, that is, you depend on the columns to be in a certain order... if the columns ever change your code will break and it will be very difficult to track down the problem.

Albo i nie... Stwierdzenie to może być boleśnie prawdziwe w sytuacji, kiedy zakłada się konkretną kolejność kolumn w wyniku — bez dwóch zdań. Jednak przy zastosowaniu jakiegoś w miarę rozsądnego języka programowania wynik zapytania SQL może być odczytywany jako kolekcja dostępna poprzez nazwy kolumn. Przykładowo: w PHP metoda mysql_fetch_assoc() tworzy tablicę asocjacyjną gdzie kluczami są nazwy kolumn, w C# metoda DbDataAdapter.Fill() wypełnia DataSet zachowując nazwy kolumn, itd. itp. W takiej sytuacji kolejność kolumn nie ma najmniejszego znaczenia.

Podsumowując: z pewnością przedstawione założenia warto mieć na uwadze. Jednak sprowadza się to po prostu do zasady "myśl co robisz i dlaczego". Jeśli zmienisz wszystkie swoje select * kierowany prostą myślą "słyszałem, że podobno select * is evil", możesz sobie napytać tylko więcej kłopotów.

[1] Słowa "perspektywa" czy "widok" zupełnie mi się nie podobają. Nie wspominając nawet o "tabeli wirtualnej"...

[2] Przykład autentyczny.

Komentarze

Brak komentarzy do tego wpisu.

 

Uwaga: Ze względu na bardzo intensywną działalność spambotów komentowanie zostało wyłączone po 60 dniach od opublikowania wpisu. Jeżeli faktycznie chcesz jeszcze skomentować skorzystaj ze strony kontaktowej.