Mam tabelę, w której chcę uzyskać najnowszy wpis dla każdej grupy. Oto tabela:
DocumentStatusLogs
Stół
|ID| DocumentID | Status | DateCreated |
| 2| 1 | S1 | 7/29/2011 |
| 3| 1 | S2 | 7/30/2011 |
| 6| 1 | S1 | 8/02/2011 |
| 1| 2 | S1 | 7/28/2011 |
| 4| 2 | S2 | 7/30/2011 |
| 5| 2 | S3 | 8/01/2011 |
| 6| 3 | S1 | 8/02/2011 |
Tabela zostanie pogrupowana DocumentID
i posortowana według DateCreated
malejącej kolejności. Dla każdego DocumentID
chcę uzyskać najnowszy status.
Moja preferowana wydajność:
| DocumentID | Status | DateCreated |
| 1 | S1 | 8/02/2011 |
| 2 | S3 | 8/01/2011 |
| 3 | S1 | 8/02/2011 |
Czy jest jakaś funkcja agregująca, która pozwala uzyskać tylko szczyt z każdej grupy? Zobacz pseudo-kod
GetOnlyTheTop
poniżej:SELECT DocumentID, GetOnlyTheTop(Status), GetOnlyTheTop(DateCreated) FROM DocumentStatusLogs GROUP BY DocumentID ORDER BY DateCreated DESC
Jeśli taka funkcja nie istnieje, czy jest jakiś sposób na osiągnięcie pożądanej wydajności?
- A może po pierwsze, może to być spowodowane nienormalizowaną bazą danych? Zastanawiam się, skoro szukam tylko jednego wiersza, czy powinien on
status
również znajdować się w tabeli nadrzędnej?
Więcej informacji znajduje się w tabeli nadrzędnej:
Aktualna Documents
tabela
| DocumentID | Title | Content | DateCreated |
| 1 | TitleA | ... | ... |
| 2 | TitleB | ... | ... |
| 3 | TitleC | ... | ... |
Czy tabela nadrzędna powinna być taka, aby móc łatwo uzyskać dostęp do jej statusu?
| DocumentID | Title | Content | DateCreated | CurrentStatus |
| 1 | TitleA | ... | ... | s1 |
| 2 | TitleB | ... | ... | s3 |
| 3 | TitleC | ... | ... | s1 |
AKTUALIZACJA Właśnie nauczyłem się używać „aplikuj”, co ułatwia rozwiązywanie takich problemów.
Odpowiedzi:
Jeśli oczekujesz 2 wpisów dziennie, to wybierze jeden. Aby uzyskać oba wpisy na jeden dzień, użyj zamiast tego DENSE_RANK
Jeśli chodzi o znormalizowane, czy nie, zależy to od tego, czy chcesz:
Na obecnym etapie zachowujesz historię statusu. Jeśli chcesz mieć również najnowszy status w tabeli nadrzędnej (czyli denormalizacji), potrzebujesz wyzwalacza, aby zachować „status” w rodzicu. lub upuść tę tabelę historii statusu.
źródło
Partition By
?With
jest dla mnie również nowy :( I tak używam mssql 2005.ROW_NUMBER
jakieś podkwerenda dla każdego wiersza?Właśnie nauczyłem się korzystać
cross apply
. Oto jak go użyć w tym scenariuszu:źródło
Dokonałem tutaj pewnych korekt w stosunku do różnych zaleceń tutaj, a wyniki naprawdę zależą od wielkości zaangażowanej tabeli, ale najbardziej spójnym rozwiązaniem jest użycie APLIKACJI KRZYŻOWEJ. Te testy zostały uruchomione na SQL Server 2008-R2, przy użyciu tabeli z 6500 rekordów i kolejny (identyczny schemat) z 137 milionami rekordów. Zapytane kolumny są częścią klucza podstawowego tabeli, a szerokość tabeli jest bardzo mała (około 30 bajtów). Czasy są raportowane przez SQL Server z rzeczywistego planu wykonania.
Myślę, że naprawdę niesamowitą rzeczą było to, jak konsekwentny był czas na APLIKACJĘ KRZYŻOWĄ, niezależnie od liczby zaangażowanych wierszy.
źródło
Wiem, że to stary wątek, ale
TOP 1 WITH TIES
rozwiązania są całkiem fajne i mogą być pomocne w lekturze tych rozwiązań.Więcej informacji o klauzuli TOP można znaleźć tutaj .
źródło
Jeśli martwisz się wydajnością, możesz to zrobić za pomocą MAX ():
ROW_NUMBER () wymaga rodzaju wszystkich wierszy w instrukcji SELECT, podczas gdy MAX nie. Powinno drastycznie przyspieszyć zapytanie.
źródło
row_number()
nawet przy właściwym indeksowaniu. Uważam to za szczególnie cenne w scenariuszach z samozłączeniem. Należy jednak pamiętać, że ta metoda często zapewnia wyższą liczbę logicznych odczytów i zliczeń skanów, pomimo zgłaszania niskich kosztów poddrzewa. Musisz rozważyć koszty / korzyści w konkretnym przypadku, aby ustalić, czy rzeczywiście jest to lepsze.Jaki serwer bazy danych? Ten kod nie działa na wszystkich z nich.
Jeśli chodzi o drugą połowę twojego pytania, wydaje mi się rozsądne, aby dołączyć status jako kolumnę. Możesz wyjść
DocumentStatusLogs
jako dziennik, ale nadal przechowywać najnowsze informacje w głównej tabeli.BTW, jeśli masz już
DateCreated
kolumnę w tabeli Dokumentów, możesz po prostu dołączyć,DocumentStatusLogs
używając tej (o ileDateCreated
jest to unikalne wDocumentStatusLogs
).Edycja: MsSQL nie obsługuje USING, więc zmień na:
źródło
max(DateCreated)
To jedno z najłatwiejszych pytań na ten temat, dlatego chciałem udzielić na nie nowoczesnej odpowiedzi (zarówno w celach informacyjnych, jak i pomocy innym). Używając
first_value
iover
możesz wykonać krótką pracę z powyższym zapytaniem:Powinno to działać w Sql Server 2008 i nowszych wersjach.
First_value
może być traktowany jako sposób na osiągnięcie celuSelect Top 1
przy użyciuover
klauzuli.Over
umożliwia grupowanie na liście wyboru, więc zamiast pisać zagnieżdżone podzapytania (jak robi to wiele istniejących odpowiedzi), robi to w bardziej czytelny sposób. Mam nadzieję że to pomoże.źródło
To dość stary wątek, ale pomyślałem, że wrzucę moje dwa centy tak samo, ponieważ zaakceptowana odpowiedź nie zadziałała szczególnie dobrze. Wypróbowałem rozwiązanie gbn na dużym zbiorze danych i okazało się, że jest on bardzo powolny (> 45 sekund na 5 milionach rekordów w SQL Server 2012). Patrząc na plan wykonania jest oczywiste, że problem polega na tym, że wymaga operacji SORT, która znacznie spowalnia działanie.
Oto alternatywa, którą usunąłem ze struktury encji, która nie wymaga operacji SORT i wykonuje wyszukiwanie w indeksie nieklastrowanym. Skraca to czas wykonania do <2 sekund we wspomnianym zestawie rekordów.
Teraz zakładam coś, co nie jest całkowicie określone w pierwotnym pytaniu, ale jeśli projekt tabeli jest taki, że kolumna identyfikatora jest identyfikatorem automatycznego przyrostu, a funkcja DateCreated jest ustawiona na bieżącą datę dla każdej wstawki, to nawet bez uruchamiania powyższego zapytania można uzyskać znaczny wzrost wydajności rozwiązania gbn (około połowy czasu wykonania) po prostu z zamówienia na ID zamiast z DateCreated, ponieważ zapewni to identyczną kolejność sortowania i jest to szybsze sortowanie.
źródło
Mój kod do wyboru 1 z każdej grupy
źródło
Weryfikacja niesamowitej i poprawnej odpowiedzi Clinta z góry:
Wydajność między dwoma poniższymi zapytaniami jest interesująca. 52% jest najlepszym. A 48% to drugi. 4% poprawa wydajności dzięki DISTINCT zamiast ORDER BY. Ale ORDER BY ma tę zaletę, że sortuje według wielu kolumn.
Opcja 1:
Opcja 2:
Management Studio firmy M $: Po podświetleniu i uruchomieniu pierwszego bloku zaznacz zarówno opcję 1, jak i opcję 2, kliknij prawym przyciskiem myszy -> [Wyświetl szacowany plan wykonania]. Następnie uruchom całą rzecz, aby zobaczyć wyniki.
Wyniki opcji 1:
Wyniki opcji 2:
Uwaga:
Unikam również podzapytań EXISTS / IN w klauzuli WHERE lub ON, ponieważ doświadczyłem, że powoduje to okropne plany wykonania. Ale przebieg jest różny. Przejrzyj plan wykonania i wydajność profilu tam, gdzie i kiedy jest to potrzebne!
źródło
Tego rozwiązania można użyć, aby uzyskać TOP N najnowszych wierszy dla każdej partycji (w przykładzie N wynosi 1 w instrukcji WHERE, a partycja to doc_id):
źródło
Jeśli chcesz zwrócić tylko ostatnie zamówienie dokumentu według DateCreated, zwróci tylko 1 pierwszy dokument według DocumentID
źródło
CROSS APPLY
była to metoda, którą zastosowałem dla mojego rozwiązania, ponieważ zadziałało dla mnie i dla potrzeb moich klientów. I z tego, co przeczytałem, powinien zapewnić najlepszą ogólną wydajność, jeśli ich baza danych znacznie się powiększy.źródło
Oto 3 osobne podejścia do problemu wraz z najlepszym wyborem indeksowania dla każdego z tych zapytań (proszę wypróbować samodzielnie indeksy i zobaczyć logiczny odczyt, upływ czasu, plan wykonania. Podałem sugestie z mojego doświadczenia na temat takie zapytania bez wykonywania tego konkretnego problemu).
Podejście 1 : używając ROW_NUMBER (). Jeśli indeks magazynu wierszy nie jest w stanie zwiększyć wydajności, możesz wypróbować nieklastrowy / klastrowany indeks magazynu kolumn, jak w przypadku zapytań z agregacją i grupowaniem oraz dla tabel, które są uporządkowane według różnych kolumn przez cały czas, indeks magazynu kolumn jest zwykle najlepszym wyborem.
Podejście 2 : używając FIRST_VALUE. Jeśli indeks magazynu wierszy nie jest w stanie zwiększyć wydajności, możesz wypróbować nieklastrowy / klastrowany indeks magazynu kolumn, jak w przypadku zapytań z agregacją i grupowaniem oraz dla tabel, które są uporządkowane według różnych kolumn przez cały czas, indeks magazynu kolumn jest zwykle najlepszym wyborem.
Podejście 3 : Używanie aplikacji CROSS. Utworzenie indeksu magazynu wierszy w tabeli DocumentStatusLogs obejmującego kolumny użyte w zapytaniu powinno wystarczyć do pokrycia zapytania bez potrzeby korzystania z indeksu magazynu kolumn.
źródło
Wierzę, że można to zrobić w ten sposób. Może to wymagać drobnych poprawek, ale możesz po prostu wybrać maksimum z grupy.
Te odpowiedzi to przesada.
źródło
W scenariuszach, w których chcesz uniknąć używania row_count (), możesz również użyć lewego sprzężenia:
W przykładowym schemacie można również użyć „nie w podzapytaniu”, które generalnie kompiluje się do tego samego wyniku, co lewe złączenie:
Uwaga: wzorzec podzapytania nie działałby, gdyby tabela nie zawierała co najmniej jednego unikatowego klucza / ograniczenia / indeksu jednokolumnowego, w tym przypadku klucza podstawowego „Id”.
Oba te zapytania są zwykle „droższe” niż zapytanie row_count () (mierzone przez Query Analyzer). Można jednak spotkać się ze scenariuszami, w których wyniki zwracają się szybciej lub włączyć inne optymalizacje.
źródło
źródło
Spróbuj tego:
źródło
To najbardziej waniliowy TSQL, jaki mogę wymyślić
źródło
W SQLite jest sprawdzone, że można użyć następującego prostego zapytania w GROUP BY
Tutaj MAX pomaga uzyskać maksymalną datę utworzenia z każdej grupy.
Ale wygląda na to, że MYSQL nie kojarzy * -kolumn z wartością max DateCreated :(
źródło