Czy SQL jest deklaratywny?

22

Pytam, ponieważ tak wiele pytań, które widzę w języku SQL, brzmi: „To jest powolne. Jak to przyspieszyć”? Lub są tutoriale z informacją: „Zrób to w ten sposób, a nie w ten sposób, ponieważ jest szybszy”.

Wydaje mi się, że duża część SQL zna się na tym, jak zostanie wykonane wyrażenie, i na podstawie tej wiedzy wybiera style wyrażeń, które działają lepiej. Nie jest to zgodne z jednym aspektem deklaratywnego programowania - pozostawieniem systemu, aby zdecydował, jak najlepiej wykonać obliczenia, a Ty sam określisz, co powinno dać obliczenie.

Nie należy silnik SQL nie obchodzi, jeśli jest używana in, existslub joinjeśli jest to naprawdę deklaratywne nie należy po prostu dać poprawną odpowiedź w rozsądnym czasie, jeśli to możliwe przez jedną z trzech metod? Ten ostatni przykład jest podpowiedzi przez ten ostatni post, który jest typu wymienionego w moim pierwszym akapicie.

Indeksy

Wydaje mi się, że najłatwiejszy przykład, jaki mogłem zastosować, dotyczy utworzenia indeksu dla tabeli. Gumph tutaj na w3schools.com próbuje nawet wyjaśnić to jako coś, czego użytkownik nie widział ze względu na wydajność. Ich opis wydaje się umieszczać indeksy SQL w obozie nie deklaratywnym i są one rutynowo dodawane ręcznie ze względów czysto wydajnościowych.

Czy to prawda, że ​​jest to gdzieś idealna baza danych SQL, która jest znacznie bardziej deklaratywna niż cała reszta, ale ponieważ jest tak dobra, że ​​się o niej nie słyszy?

Paddy3118
źródło
@FrustratedWithFormsDesigner: Wiem dokładnie, co to znaczy. 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ą existslub join.
Mason Wheeler
Używając podobnego rozumowania, wydaje mi się, że wyrażenia regularne są bardziej deklaratywną metodą wyrażania, ponieważ rzadko widzę pytania dotyczące wydajności, na które odpowiedzi brzmi „powinieneś napisać to w ten sposób, aby uzyskać lepszą wydajność”. Tracę rozum i do połowy pamiętam jakieś pytanie związane z negatywnymi stwierdzeniami z wyprzedzeniem lub z wyprzedzeniem w powolnym wyrażeniu regularnym, w którym odpowiedzią było przepisanie wyrażenia regularnego w inny sposób, aby zrobić to samo w krótszym czasie.
Paddy3118
Wydajność jest szczegółem implementacji. Wydajność prawie każdej implementacji IN może być porównywalna lub lepsza niż EXISTS i JOIN, jeśli programiści procesorów zapytań uważają, że jest to priorytet.
JustinC
1
@JustinC, wydaje się, że jest to coś więcej niż szczegół, biorąc pod uwagę przewagę pytań SQL zorientowanych na wydajność i wskazówek dotyczących rzekomo deklaratywnego języka?
Paddy3118
Nie ma jasnej definicji deklaratywnego języka programowania, dlatego nie ma sensu o tym mówić. Niektóre języki są na wyższym poziomie niż inne, to wszystko.
ogrodnik

Odpowiedzi:

21

SQL jest teoretycznie deklaratywny. Ale wiesz, co mówią o różnicy między teorią a praktyką ...

U jej podstaw koncepcja „programowania deklaratywnego” nigdy nie była naprawdę skuteczna i prawdopodobnie nigdy nie będzie, dopóki nie będziemy mieli kompilatora opartego na sztucznej inteligencji, który jest w stanie patrzeć na kod i odpowiadać na pytanie „jaka jest intencja tego kodu?”. inteligentnie, w taki sam sposób, jak zrobiłby to autor. Sercem każdego języka deklaratywnego jest cała masa imperatywnego kodu, który próbuje gorączkowo rozwiązać ten problem bez pomocy sztucznej inteligencji.

Często działa zaskakująco dobrze, ponieważ najczęstsze przypadki to zwykłe przypadki , o których ludzie, którzy napisali implementację języka, wiedzieli i znaleźli dobre sposoby radzenia sobie. Ale wtedy natrafisz na przypadek krawędzi, którego implementator nie wziął pod uwagę, i widzisz, że wydajność spada szybko, ponieważ interpreter jest zmuszony wziąć kod znacznie bardziej dosłownie i obsługiwać go w mniej wydajny sposób.

Mason Wheeler
źródło
3
Nigdy nie jest tak naprawdę skuteczny? Język SQL, LINQ, Knockout.js, Prolog, ELM. Możesz sprawdzić ponownie. Obecnie używam głównie deklaratywnych technologii.
brian
5
@brian: Wszystkie z nich dość szybko ulegają degeneracji, gdy zdarza się przypadkowa sprawa, o której nikt nie pomyślał. Przypuszczam, że powinienem był powiedzieć „nigdy tak naprawdę skuteczny w ogólnym przypadku ”.
Mason Wheeler
Kiedy twoja odpowiedź ulegnie pogorszeniu, widząc, jak jest przechowywana w bazie danych SQL Server? :) Rzadko trafiam na przypadek krawędziowy w żadnym z nich, którego nie można rozwiązać w ramach. Widzę, skąd pochodzisz, ale przypadki skrajne naprawdę nie przysparzają mi wiele bólu z powodu tego, jak korzystny i łatwy do uzasadnienia jest około 99% deklaratywnego kodu. To tak, jakby powiedzieć, że Clojure lub F # są złe, ponieważ musiałeś użyć zmiennego typu, aby rozwiązać swój problem.
brian
11
@brian: I rarely hit an edge case in any of them that couldn't be solved within the framework.Tak, właśnie o to chodzi: konieczność wymyślenia sposobu ich rozwiązania w ramach, ponieważ framework nie jest wystarczająco inteligentny, aby rozwiązać go tak, jak go pierwotnie zadeklarowałeś.
Mason Wheeler
Co z wyborem ... do aktualizacji? Wydaje się to nakazem bezwzględnym.
Jesvin Jose
6

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_typena 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
Insac
źródło
1
Ciekawy jest przykład przepisywania sprzężenia na „istnieje”. Jedną z podstawowych zasad, którą staram się wywrzeć na programistach SQL, jest to, że użycie DISTINCT jest zapachem kodu - albo zapytanie, albo model danych, jest bardzo możliwe, że jest niewłaściwe, i należy szukać innego podejścia.
David Aldridge,