Niedawno wyjaśniłem kolegom, jak ważne jest posiadanie kolumny do sortowania danych w tabeli bazy danych, jeśli jest to konieczne, na przykład w przypadku danych uporządkowanych chronologicznie. Okazało się to nieco trudne, ponieważ mogli po prostu ponownie uruchomić zapytanie pozornie bez końca i zawsze zwracałby ten sam zestaw wierszy w tej samej kolejności.
Zauważyłem to wcześniej i wszystko, co naprawdę mogłem zrobić, to nalegać, aby mi zaufali, a nie tylko zakładać, że tabela bazy danych będzie zachowywać się jak tradycyjny plik CSV lub Excel.
Na przykład wykonanie zapytania (PostgreSQL)
create table mytable (
id INTEGER PRIMARY KEY,
data TEXT
);
INSERT INTO mytable VALUES
(0, 'a'),
(1, 'b'),
(2, 'c'),
(3, 'd'),
(4, 'e'),
(5, 'f'),
(6, 'g'),
(7, 'h'),
(8, 'i'),
(9, 'j');
utworzy tabelę z wyraźnym porządkiem pojęciowym. Wybór tych samych danych w najprostszy sposób to:
SELECT * FROM mytable;
Zawsze daje mi następujące wyniki:
id | data
----+------
0 | a
1 | b
2 | c
3 | d
4 | e
5 | f
6 | g
7 | h
8 | i
9 | j
(10 rows)
Mogę to robić w kółko i zawsze będą mi zwracać te same dane w tej samej kolejności. Wiem jednak, że ten dorozumiany porządek może zostać złamany, widziałem go już wcześniej, szczególnie w dużych zestawach danych, gdzie jakaś losowa wartość zostanie najwyraźniej wyrzucona w „niewłaściwe” miejsce po wybraniu. Ale przyszło mi do głowy, że nie wiem, jak to się dzieje i jak to odtworzyć. Trudno mi uzyskać wyniki w Google, ponieważ wyszukiwane hasło zwykle zwraca ogólną pomoc w sortowaniu zestawów wyników.
Tak więc moje pytania są zasadniczo następujące:
Jak mogę w sposób wyraźny i konkretny udowodnić, że kolejność zwrotu wierszy z zapytania bez
ORDER BY
instrukcji nie jest wiarygodna, najlepiej przez spowodowanie i pokazanie podziału niejawnej kolejności, nawet jeśli tabela nie jest aktualizowana ani edytowana ?Czy w ogóle ma to znaczenie, jeśli dane są wstawiane tylko raz masowo, a następnie nigdy nie aktualizowane?
Wolałbym odpowiedź opartą na postgresie, ponieważ jest to ta, którą znam najlepiej, ale bardziej interesuje mnie sama teoria.
order by
klauzuli do swoich zapytań? Czy próbują zaoszczędzić na pamięci kodu źródłowego? zużycie klawiatury? ile czasu zajmuje wpisanie przerażającej klauzuli?Odpowiedzi:
Widzę trzy sposoby, aby przekonać ich:
Pozwól im wypróbować to samo zapytanie, ale z większą tabelą (większa liczba wierszy) lub gdy tabela jest aktualizowana między wykonaniami. Lub wstawiane są nowe wiersze, a niektóre stare są usuwane. Lub indeks jest dodawany lub usuwany między wykonaniami. Lub stół jest odkurzany (w Postgres). Lub indeksy są przebudowywane (w SQL Server). Lub tabela zostanie zmieniona z klastrowej na stertę. Lub usługa bazy danych jest restartowana.
Możesz zasugerować, że udowodnią, że różne egzekucje zwrócą to samo zamówienie. Czy mogą to udowodnić? Czy mogą zapewnić serię testów, które dowodzą, że każde zapytanie da wynik w tej samej kolejności, bez względu na to, ile razy zostanie wykonane?
Dostarcz dokumentację różnych DBMS w tym zakresie. Na przykład:
PostgreSQL :
SQL Server :
Oracle :
źródło
ORDER BY
, który zagwarantuje zamówienie, bez względu na to, jak zmieni się stół ? Dlaczego nie dodać sejfu, który nie szkodzi?To znowu historia czarnego łabędzia. Jeśli jeszcze go nie widziałeś, to nie znaczy, że nie istnieją. Mamy nadzieję, że w twoim przypadku nie doprowadzi to do kolejnego ogólnoświatowego kryzysu finansowego, a jedynie do kilku niezadowolonych klientów.
Dokumentacja Postgres mówi to wyraźnie:
„System” w tym przypadku obejmuje samego demona Postgres (w tym implementację jego metod dostępu do danych i optymalizatora zapytań), podstawowy system operacyjny, logiczny i fizyczny układ pamięci bazy danych, a nawet pamięć podręczną procesora. Ponieważ jako użytkownik bazy danych nie masz kontroli nad tym stosem, nie powinieneś polegać na tym, że będzie zachowywał się tak samo, jak w tej chwili.
Twoi koledzy popełniają pospieszny błąd generalizacji . Aby obalić ich punkt wystarczy wykazać, że ich założenie jest błędne tylko raz, np. Przez ten dbfiddle .
źródło
Rozważ następujący przykład, w którym mamy trzy powiązane tabele. Zamówienia, użytkownicy i szczegóły zamówienia. OrderDetails jest powiązany z kluczami obcymi do tabeli Zamówienia i tabeli użytkowników. Jest to zasadniczo bardzo typowa konfiguracja relacyjnych baz danych; prawdopodobnie cały cel relacyjnego DBMS.
Tutaj sprawdzamy tabelę OrderDetails, gdzie identyfikator użytkownika to 15:
Dane wyjściowe zapytania wyglądają następująco:
Jak widać, kolejność wyjściowa wierszy nie zgadza się z kolejnością wierszy w tabeli OrderDetails.
Dodanie jawnego
ORDER BY
gwarantuje, że wiersze zostaną zwrócone do klienta w żądanej kolejności:Jeśli kolejność wierszy jest bezwzględna, a inżynierowie wiedzą, że porządek jest bezwzględny, powinni zawsze chcieć użyć
ORDER BY
instrukcji, ponieważ może oznaczać koszt ich oznaczenia, jeśli wystąpi awaria związana z nieprawidłową kolejnością.Drugi, być może bardziej pouczający przykład, wykorzystując
OrderDetails
tabelę z góry, dokąd nie dołączeniem innych tabel, ale mają prosty wymóg, aby znaleźć wiersze spełniające zarówno OrderID oraz identyfikator użytkownika, widzimy problem.Stworzymy indeks do obsługi zapytania, tak jak w prawdziwym życiu, gdyby wydajność była w jakikolwiek sposób ważna (kiedy nie jest?).
Oto zapytanie:
A wyniki:
Dodanie
ORDER BY
klauzuli z pewnością zapewni nam również prawidłowy sort.Te makiety to tylko proste przykłady, w których nie można zagwarantować, że wiersze są „w porządku” bez wyraźnego
ORDER BY
oświadczenia. Jest o wiele więcej takich przykładów, a ponieważ kod silnika DBMS zmienia się dość często, określone zachowanie może z czasem ulec zmianie.źródło
Jako praktyczny przykład w Postgres kolejność zmienia się w chwili aktualizacji wiersza:
Nie sądzę, aby zasady tego istniejącego niejawnego zamówienia były nigdzie udokumentowane, z pewnością mogą ulec zmianie bez powiadomienia i zdecydowanie nie są przenośnym zachowaniem w silnikach DB.
źródło
nie do końca demo, ale za długo na komentarz.
Na dużych tabelach niektóre bazy danych wykonują przeplatane równoległe skanowanie:
Jeśli dwa zapytania chcą zeskanować tę samą tabelę i dotrzeć prawie w tym samym czasie, pierwsze może być częścią tabeli, gdy rozpocznie się drugie.
Drugie zapytanie może odbierać rekordy od środka tabeli (gdy kończy się pierwsze zapytanie), a następnie otrzymywać rekordy od początku tabeli.
źródło
Utwórz indeks klastrowy, który ma „niewłaściwą” kolejność. Na przykład włącz klaster
ID DESC
. Często będzie to generować odwrotną kolejność (chociaż nie jest to również gwarantowane).źródło