Myślałem o tym kilka dni temu po optymalizacji SQL. Myślę, że możemy się zgodzić, że SQL jest „językiem deklaratywnym” w definicji Wikipedii:
Paradygmat programowania, który wyraża logikę obliczeń bez opisywania przepływu sterowania
Jeśli zastanawiasz się, ile rzeczy wykonano za zasłonami (patrząc na statystyki, decydując, czy indeks jest przydatny, wybierając zagnieżdżone, scalone lub łączenie mieszające itp. Itd.), Musimy przyznać, że dajemy tylko wysoki poziom logika, a baza danych zajęła się całą logiką przepływu kontroli niskiego poziomu.
Również w tym scenariuszu czasami optymalizator bazy danych potrzebuje pewnych „wskazówek” od użytkownika, aby uzyskać najlepsze wyniki.
Inną popularną definicją języka „deklaratywnego” jest (nie mogę znaleźć autorytatywnego źródła):
Paradygmat programowania, który wyraża pożądany wynik obliczeń bez opisywania kroków do jego osiągnięcia (w skrócie „opisz co, a nie jak”)
Jeśli zaakceptujemy tę definicję, napotkamy problemy opisane przez PO.
Pierwszym problemem jest to, że SQL daje nam wiele równoważnych sposobów definiowania „tego samego wyniku”. Prawdopodobnie jest to zło konieczne: im bardziej ekspresyjną moc dajemy językowi, tym bardziej prawdopodobne jest, że będą mieli różne sposoby wyrażania tego samego.
Na przykład poproszono mnie kiedyś o optymalizację tego zapytania:
SELECT Distinct CT.cust_type, ct.cust_type_description
from customer c
INNER JOIN
Customer_type CT on c.cust_type=ct.cust_type;
Ponieważ typy były znacznie mniejsze niż klient, a cust_type
na stole klienta znajdował się indeks , osiągnąłem wielką poprawę, przepisując go jako:
SELECT CT.cust_type, ct.cust_type_description
from Customer_type CT
Where exists ( select 1 from customer c
Where c.cust_type=ct.cust_type);
W tym konkretnym przypadku, gdy zapytałem programistę, co chciał osiągnąć, powiedział mi: „Chciałem wszystkich typów klientów, dla których miałem co najmniej jednego klienta”, tak przy okazji, dokładnie tak można opisać zapytanie optymalizatora.
Jeśli więc mogę znaleźć równoważne i bardziej wydajne zapytanie, dlaczego optymalizator nie może zrobić tego samego?
Domyślam się, że dzieje się tak z dwóch głównych powodów:
SQL wyraża logikę:
skoro SQL wyraża logikę wysokiego poziomu, czy naprawdę chcielibyśmy, aby optymalizator „przechytrzył” nas i naszą logikę? Z entuzjazmem wykrzykiwałbym „tak”, gdyby nie tyle razy musiałem zmusić optymalizator do wybrania najbardziej wydajnej ścieżki wykonania. Myślę, że pomysł może polegać na tym, aby optymalizator działał jak najlepiej (również zmieniając naszą logikę), ale dać nam „mechanizm podpowiedzi”, który przyjdzie nam na ratunek, gdy coś zwariuje (byłoby jak włączenie koła + hamulca w samochód autonomiczny).
Więcej możliwości = więcej czasu
Nawet najlepszy optymalizator RDBMS nie testuje WSZYSTKICH możliwych ścieżek wykonania, ponieważ muszą one być naprawdę szybkie: jak dobrze byłoby zoptymalizować zapytanie od 100 ms do 10 ms, jeśli muszę spędzać za każdym razem 100 ms na wyborze najlepszej ścieżki? I to w przypadku optymalizatora respektującego naszą „logikę wysokiego poziomu”. Gdyby również przetestował wszystkie równoważne zapytania SQL, czas optymalizatora mógłby wzrosnąć wiele razy.
Innym dobrym przykładem przepisania zapytania, którego nie jest w stanie wykonać żaden RDBMS, jest (z tego interesującego posta na blogu )
SELECT t1.id, t1.value, SUM(t2.value)
FROM mytable t1
JOIN mytable t2
ON t2.id <= t1.id
GROUP BY t1.id, t1.value;
niż można zapisać w ten sposób (wymagane funkcje analityczne)
SELECT id, value, SUM(t1.value) OVER (ORDER BY id)
FROM mytable
select whatever from sometable where FKValue in (select FKValue from sometable_2 where other_value = :param)
. To powinno być trywialne, aby zobaczyć, jak przekształcić to za pomocąexists
lubjoin
.