Mam około miliarda wierszy danych w tabeli z nazwą i liczbą całkowitą z zakresu 1-288. Dla danej nazwy , każdy int jest wyjątkowy, a nie każdy możliwy całkowitą w przedziale jest obecny - tak istnieją luki.
To zapytanie generuje przykładowy przypadek:
--what I have:
SELECT *
FROM ( VALUES ('foo', 2),
('foo', 3),
('foo', 4),
('foo', 10),
('foo', 11),
('foo', 13),
('bar', 1),
('bar', 2),
('bar', 3)
) AS baz ("name", "int")
Chciałbym wygenerować tablicę przeglądową z wierszem dla każdej nazwy i sekwencji ciągłych liczb całkowitych. Każdy taki wiersz zawierałby:
Nazwa - wartość nazwa kolumny
początku - pierwszy całkowitą w ciągłej sekwencji
końca - wartość końcowa w ciągłej sekwencji
rozpiętości - koniec - początek + 1
To zapytanie generuje przykładowe dane wyjściowe dla powyższego przykładu:
--what I need:
SELECT *
FROM ( VALUES ('foo', 2, 4, 3),
('foo', 10, 11, 2),
('foo', 13, 13, 1),
('bar', 1, 3, 3)
) AS contiguous_ranges ("name", "start", "end", span)
Ponieważ mam tak wiele wierszy, bardziej wydajne jest lepsze. To powiedziawszy, muszę uruchomić to zapytanie tylko raz, więc nie jest to absolutny wymóg.
Z góry dziękuję!
Edytować:
Powinienem dodać, że rozwiązania PL / pgSQL są mile widziane (proszę wyjaśnić wszelkie fantazyjne sztuczki - wciąż jestem nowy w PL / pgSQL).
źródło
Odpowiedzi:
Co powiesz na używanie
with recursive
widok testu:
pytanie:
wynik:
Byłbym zainteresowany, aby dowiedzieć się, jak to działa w tabeli miliardów wierszy.
źródło
Możesz to zrobić za pomocą funkcji okienkowania. Podstawową ideą jest użycie
lead
ilag
okienkowanie funkcji do ciągnięcia rzędów przed i za bieżącym rzędem. Następnie możemy obliczyć, jeśli mamy początek lub koniec sekwencji:(Użyłem widoku, więc logikę łatwiej będzie śledzić poniżej.) Teraz wiemy, czy rząd jest początkiem czy końcem. Musimy zwinąć to w wiersz:
Dla mnie wygląda poprawnie :)
źródło
Kolejne rozwiązanie funkcji okna. Nie mam pojęcia o wydajności, na końcu dodałem plan wykonania (chociaż z tak małą liczbą wierszy, prawdopodobnie nie ma on dużej wartości). Jeśli chcesz się pobawić: test Fiddle SQL
Tabela i dane:
Pytanie:
Plan zapytań
źródło
Na SQL Server dodałbym jeszcze jedną kolumnę o nazwie previousInt:
Użyłbym ograniczenia CHECK, aby upewnić się, że previousInt <int i ograniczenie FK (name, previousInt) odnoszą się do (name, int) i kilka innych ograniczeń w celu zapewnienia wodoszczelności integralności danych. To zrobione, wybór luk jest banalny:
Aby przyspieszyć, mogę utworzyć filtrowany indeks, który zawierałby tylko luki. Oznacza to, że wszystkie luki są wstępnie obliczone, więc selekcje są bardzo szybkie, a ograniczenia zapewniają integralność wstępnie obliczonych danych. Często używam takich rozwiązań, są one w całym moim systemie.
źródło
Możesz poszukać metody Tabibitosan:
Gruntownie:
Myślę, że ten występ lepiej:
źródło
szorstki plan:
Powtarzaj od 2., aż nie będzie więcej aktualizacji. Stamtąd komplikuje się, Gordian, z grupowaniem powyżej maks. Min i maks. Min. Chyba wybrałbym język programowania.
PS: Ładna tabela próbek z kilkoma przykładowymi wartościami byłaby w porządku, z której mogliby korzystać wszyscy, więc nie wszyscy tworzą swoje dane testowe od zera.
źródło
To rozwiązanie jest inspirowane odpowiedzią Nate'a C przy użyciu funkcji okienkowania i klauzuli OVER. Co ciekawe, odpowiedź ta powraca do podkwerend z referencjami zewnętrznymi. Możliwe jest zakończenie konsolidacji wiersza za pomocą innego poziomu funkcji okienkowania. Może nie wygląda to zbyt ładnie, ale zakładam, że jest bardziej wydajny, ponieważ wykorzystuje wbudowaną logikę potężnych funkcji okienkowania.
Z rozwiązania Nate'a zdałem sobie sprawę, że początkowy zestaw wierszy już wytworzył niezbędne flagi do 1) wybrania początkowych i końcowych wartości zakresu ORAZ 2) w celu wyeliminowania dodatkowych wierszy pomiędzy nimi. W zapytaniu zagnieżdżono podzapytania dwa głębokie tylko ze względu na ograniczenia funkcji okienkowania, które ograniczają sposób użycia aliasów kolumn. Logicznie mogłem uzyskać wyniki tylko z jednym zagnieżdżonym podzapytaniem.
Kilka innych uwag : Poniżej znajduje się kod SQLite3. Dialekt SQLite pochodzi z postgresql, więc jest bardzo podobny i może nawet działać niezmieniony. Dodałem ograniczenie kadrowania do klauzul OVER, ponieważ funkcje
lag()
ilead()
wymagają tylko okna jednorzędowego, odpowiednio przed i po (więc nie było potrzeby utrzymywania domyślnego zestawu wszystkich poprzednich wierszy). Zdecydowałem się również na nazwiska,first
alast
ponieważ słowoend
jest zastrzeżone.Wyniki są takie same jak inne odpowiedzi, jak można się spodziewać:
źródło