Według MSDN Mediana nie jest dostępna jako funkcja agregująca w Transact-SQL. Chciałbym jednak dowiedzieć się, czy można utworzyć tę funkcję (za pomocą funkcji Utwórz agregację, funkcji zdefiniowanej przez użytkownika lub innej metody).
Jaki byłby najlepszy sposób (jeśli to możliwe), aby to zrobić - pozwolić na obliczenie wartości mediany (przy założeniu liczbowego typu danych) w zapytaniu zagregowanym?
sql
sql-server
aggregate-functions
median
Yaakov Ellis
źródło
źródło
Odpowiedzi:
AKTUALIZACJA 2019: W ciągu 10 lat od napisania tej odpowiedzi odkryto więcej rozwiązań, które mogą przynieść lepsze wyniki. Ponadto wersje SQL Server od tego czasu (zwłaszcza SQL 2012) wprowadziły nowe funkcje T-SQL, których można używać do obliczania median. Wersje programu SQL Server poprawiły również optymalizator zapytań, który może wpływać na różne rozwiązania mediany. Net-net, mój oryginalny post z 2009 roku jest nadal OK, ale mogą istnieć lepsze rozwiązania dla nowoczesnych aplikacji SQL Server. Spójrz na ten artykuł z 2012 roku, który jest świetnym źródłem: https://sqlperformance.com/2012/08/t-sql-queries/median
W tym artykule stwierdzono, że następujący wzorzec jest znacznie, znacznie szybszy niż wszystkie inne alternatywy, przynajmniej na testowanym prostym schemacie. To rozwiązanie było 373 razy szybsze (!!!) niż
PERCENTILE_CONT
testowane najwolniejsze ( ) rozwiązanie. Pamiętaj, że ta sztuczka wymaga dwóch osobnych zapytań, które mogą nie być praktyczne we wszystkich przypadkach. Wymaga również SQL 2012 lub nowszego.Oczywiście tylko dlatego, że jeden test na jednym schemacie w 2012 roku przyniósł świetne wyniki, twój przebieg może się różnić, szczególnie jeśli korzystasz z SQL Server 2014 lub nowszej wersji. Jeśli perf jest ważne dla obliczenia mediany, zdecydowanie sugeruję wypróbowanie i przetestowanie kilku opcji zalecanych w tym artykule, aby upewnić się, że znalazłeś najlepszą dla swojego schematu.
Byłbym również szczególnie ostrożny przy użyciu funkcji (nowość w SQL Server 2012),
PERCENTILE_CONT
która jest zalecana w jednej z pozostałych odpowiedzi na to pytanie, ponieważ w powyższym artykule stwierdzono, że ta wbudowana funkcja jest 373 razy wolniejsza niż najszybsze rozwiązanie. Możliwe, że różnica ta uległa poprawie w ciągu 7 lat, ale osobiście nie użyłbym tej funkcji na dużym stole, dopóki nie zweryfikuję jej wydajności w porównaniu z innymi rozwiązaniami.POCZĄTEK ORYGINALNY 2009 JEST PONIŻEJ:
Można to zrobić na wiele sposobów, z radykalnie różną wydajnością. Oto jedno szczególnie dobrze zoptymalizowane rozwiązanie od median, ROW_NUMBER i wydajności . Jest to szczególnie optymalne rozwiązanie, jeśli chodzi o rzeczywiste operacje we / wy generowane podczas wykonywania - wygląda na droższe niż inne rozwiązania, ale w rzeczywistości jest znacznie szybsze.
Ta strona zawiera także omówienie innych rozwiązań i szczegóły testowania wydajności. Zwróć uwagę na użycie unikatowej kolumny jako elementu ujednoznaczniającego w przypadku, gdy istnieje wiele wierszy o tej samej wartości środkowej kolumny.
Podobnie jak w przypadku wszystkich scenariuszy wydajności bazy danych, zawsze staraj się przetestować rozwiązanie z prawdziwymi danymi na prawdziwym sprzęcie - nigdy nie wiesz, kiedy zmiana optymalizatora SQL Server lub osobliwość w twoim środowisku spowolnią normalnie szybkie rozwiązanie.
źródło
Jeśli używasz SQL 2005 lub nowszego, jest to ładne, proste obliczenie mediany dla pojedynczej kolumny w tabeli:
źródło
select gid, median(score) from T group by gid
. Jak . Czy potrzebujesz do tego skorelowanego podzapytania?W SQL Server 2012 należy użyć PERCENTILE_CONT :
Zobacz także: http://blog.sqlauthority.com/2011/11/20/sql-server-introduction-to-percentile_cont-analytic-functions-introduced-in-sql-server-2012/
źródło
DISTINCT
lubGROUPY BY SalesOrderID
? W przeciwnym razie będziesz mieć wiele zduplikowanych wierszy.PERCENTILE_DISC
Moja oryginalna szybka odpowiedź brzmiała:
To da ci medianę i zakres międzykwartylowy za jednym zamachem. Jeśli naprawdę chcesz tylko jednego wiersza, który jest medianą, usuń komentarz z klauzuli where.
Gdy włożysz to w plan wyjaśniania, 60% pracy polega na sortowaniu danych, co jest nieuniknione przy obliczaniu takich statystyk zależnych od pozycji.
Zmieniłem odpowiedź, aby zastosować się do doskonałej sugestii Roberta Ševčíka-Robajza w poniższych komentarzach:
To powinno obliczyć prawidłowe wartości mediany i percentyla, gdy masz parzystą liczbę elementów danych. Ponownie odkomentuj klauzulę końcową gdzie, jeśli chcesz tylko mediany, a nie całego rozkładu percentyla.
źródło
Nawet lepiej:
Od samego mistrza, Itzika Ben-Gana !
źródło
MS SQL Server 2012 (i nowsze wersje) ma funkcję PERCENTILE_DISC, która oblicza określony percentyl dla posortowanych wartości. PERCENTILE_DISC (0.5) obliczy medianę - https://msdn.microsoft.com/en-us/library/hh231327.aspx
źródło
Prosty, szybki, dokładny
źródło
Jeśli chcesz użyć funkcji Utwórz agregację w programie SQL Server, oto jak to zrobić. Robienie tego w ten sposób ma tę zaletę, że można pisać czyste zapytania. Należy zauważyć, że ten proces można dostosować do dość łatwego obliczania wartości procentowej.
Utwórz nowy projekt Visual Studio i ustaw platformę docelową na .NET 3.5 (dotyczy SQL 2008, może być inaczej w SQL 2012). Następnie utwórz plik klasy i wstaw następujący kod lub równoważnik c #:
Następnie skompiluj go i skopiuj plik DLL i PDB na maszynę SQL Server i uruchom następującą komendę w SQL Server:
Następnie możesz napisać zapytanie, aby obliczyć medianę w następujący sposób: WYBIERZ dbo.Median (pole) Z tabeli
źródło
Właśnie natknąłem się na tę stronę, szukając rozwiązania mediany opartego na zestawie. Po zapoznaniu się z niektórymi rozwiązaniami tutaj wymyśliłem następujące. Nadzieja pomaga / działa.
źródło
Poniższe zapytanie zwraca medianę z listy wartości w jednej kolumnie. Nie można go używać jako funkcji agregującej lub razem z nią, ale nadal można jej używać jako zapytania podrzędnego z klauzulą WHERE w wewnętrznej selekcji.
SQL Server 2005+:
źródło
Chociaż rozwiązanie przyznane przez Justina wydaje się solidne, zauważyłem, że gdy masz wiele zduplikowanych wartości w danym kluczu partycji, numery wierszy dla duplikatów wartości ASC kończą się poza kolejnością, więc nie są odpowiednio wyrównane.
Oto fragment z mojego wyniku:
Użyłem kodu Justina jako podstawy tego rozwiązania. Chociaż nie jest tak wydajny, biorąc pod uwagę użycie wielu tabel pochodnych, rozwiązuje napotkany problem porządkowania wierszy. Wszelkie ulepszenia byłyby mile widziane, ponieważ nie mam doświadczenia w T-SQL.
źródło
Powyższy przykład Justina jest bardzo dobry. Ale ta potrzeba klucza podstawowego powinna być jasno określona. Widziałem ten kod na wolności bez klucza, a wyniki są złe.
Skarga, którą otrzymuję na temat Percentile_Cont, polega na tym, że nie da ona rzeczywistej wartości z zestawu danych. Aby dostać się do „mediany”, która jest rzeczywistą wartością z zestawu danych, użyj Percentile_Disc.
źródło
W UDF napisz:
źródło
Mediana Finding
Jest to najprostsza metoda znalezienia mediany atrybutu.
źródło
Zobacz inne rozwiązania do obliczania mediany w SQL tutaj: „ Prosty sposób na obliczenie mediany za pomocą MySQL ” (rozwiązania są w większości niezależne od dostawcy).
źródło
Dla zmiennej ciągłej / miary „col1” z „table1”
źródło
Za pomocą agregatu COUNT możesz najpierw policzyć, ile jest wierszy, i zapisać w zmiennej o nazwie @cnt. Następnie możesz obliczyć parametry dla filtra OFFSET-FETCH, aby określić, w oparciu o kolejność według ilości, liczbę wierszy do pominięcia (wartość przesunięcia) i liczbę do filtrowania (wartość pobierania).
Liczba wierszy do pominięcia wynosi (@cnt - 1) / 2. Jest oczywiste, że dla liczby nieparzystej obliczenia są poprawne, ponieważ najpierw odejmuje się 1 dla pojedynczej wartości środkowej, a następnie dzieli się przez 2.
Działa to również poprawnie dla liczenia parzystego, ponieważ użytym w wyrażeniu podziałem jest dzielenie całkowite; więc odejmując 1 od liczby parzystej, otrzymujesz nieparzystą wartość.
Dzieląc tę nieparzystą wartość przez 2, ułamkowa część wyniku (.5) jest obcinana. Liczba wierszy do pobrania wynosi 2 - (@cnt% 2). Chodzi o to, że gdy liczba jest nieparzysta, wynikiem operacji modulo jest 1, a ty musisz pobrać 1 wiersz. Gdy liczba jest nawet wynikiem operacji modulo, wynosi 0 i musisz pobrać 2 wiersze. Odejmując wynik 1 lub 0 operacji modulo od 2, otrzymujesz odpowiednio 1 lub 2 odpowiednio. Na koniec, aby obliczyć medianę, weź jedną lub dwie wielkości wynikowe i zastosuj średnią po konwersji wejściowej wartości całkowitej na liczbową w następujący sposób:
źródło
Chciałem sam wypracować rozwiązanie, ale mój mózg potknął się i upadł. Myślę, że to działa, ale nie proś mnie o wyjaśnienie rano. : P
źródło
źródło
Działa to z SQL 2000:
źródło
Dla początkujących, takich jak ja, którzy uczą się podstaw, osobiście uważam, że ten przykład jest łatwiejszy do naśladowania, ponieważ łatwiej jest dokładnie zrozumieć, co się dzieje i skąd pochodzą wartości mediany ...
Jednak pod absolutnym podziwem niektóre z powyższych kodów !!!
źródło
To najprostsza odpowiedź, jaką mogłem wymyślić. Działa dobrze z moimi danymi. Jeśli chcesz wykluczyć pewne wartości, po prostu dodaj klauzulę where do wewnętrznego wyboru.
źródło
Poniższe rozwiązanie działa przy tych założeniach:
Kod:
źródło
źródło
Próbuję z kilkoma alternatywami, ale ponieważ moje rekordy danych mają powtarzające się wartości, wydaje się, że wersje ROW_NUMBER nie są dla mnie wyborem. Więc tutaj użyłem zapytania (wersja z NTILE):
źródło
Opierając się na powyższej odpowiedzi Jeffa Atwooda, jest to z GROUP BY i skorelowanym podzapytaniem, aby uzyskać medianę dla każdej grupy.
źródło
Często możemy potrzebować obliczyć Medianę nie tylko dla całej tabeli, ale dla agregatów w odniesieniu do niektórych ID. Innymi słowy, oblicz medianę dla każdego identyfikatora w naszej tabeli, gdzie każdy identyfikator ma wiele rekordów. (w oparciu o rozwiązanie edytowane przez @gdoron: dobra wydajność i działa w wielu SQL)
Mam nadzieję, że to pomoże.
źródło
Na twoje pytanie Jeff Atwood podał już proste i skuteczne rozwiązanie. Ale jeśli szukasz alternatywnego podejścia do obliczenia mediany, pomoże ci poniższy kod SQL.
Jeśli chcesz obliczyć medianę w MySQL, ten link github będzie przydatny.
źródło
Jest to najbardziej optymalne rozwiązanie do znajdowania median, jakie mogę wymyślić. Nazwy w przykładzie oparte są na przykładzie Justina. Upewnij się, że istnieje indeks dla tabeli Sales.SalesOrderHeader z kolumnami indeksów CustomerId i TotalDue w tym zamówieniu.
AKTUALIZACJA
Nie byłem pewien, która metoda ma najlepszą wydajność, więc porównałem moją metodę Justin Grants i Jeffa Atwooda, uruchamiając zapytanie oparte na wszystkich trzech metodach w jednej partii, a koszt partii dla każdego zapytania wynosił:
Bez indeksu:
I z indeksem
Próbowałem zobaczyć, jak dobrze skalują się zapytania, jeśli masz indeks, tworząc więcej danych z około 14 000 wierszy od 2 do 512 razy, co oznacza w końcu około 7,2 miliona wierszy. Uwaga: Upewniłem się, że pole CustomeId jest unikalne dla każdego wykonania pojedynczej kopii, więc proporcja wierszy w porównaniu do unikalnego wystąpienia CustomerId była stała. Podczas gdy to robiłem, uruchomiłem wykonanie, w którym później odbudowałem indeks, i zauważyłem, że wyniki ustabilizowały się na poziomie około 128 przy danych, które miałem do tych wartości:
Zastanawiałem się, w jaki sposób mogło wpłynąć na wydajność poprzez skalowanie liczby wierszy, ale utrzymanie stałej unikalnej wartości CustomerId, więc skonfigurowałem nowy test, w którym właśnie to zrobiłem. Teraz zamiast się ustabilizować, stosunek kosztów partii wciąż się rozchodził, również zamiast około 20 wierszy na CustomerId średnio miałem na koniec około 10000 wierszy na taki unikalny identyfikator. Liczby, w których:
Upewniłem się, że zaimplementowałem każdą metodę poprawnie, porównując wyniki. Mój wniosek jest taki, że zastosowana metoda jest generalnie szybsza, dopóki istnieje indeks. Zauważyłem również, że ta metoda jest zalecana dla tego konkretnego problemu w tym artykule https://www.microsoftpressstore.com/articles/article.aspx?p=2314819&seqNum=5
Sposobem, aby jeszcze bardziej poprawić wydajność kolejnych wywołań tego zapytania, jest utrwalenie informacji o liczbie w tabeli pomocniczej. Można go nawet utrzymać, uruchamiając aktualizację wyzwalacza i przechowującą informacje dotyczące liczby wierszy SalesOrderHeader zależnych od CustomerId, oczywiście wtedy można również po prostu zapisać medianę.
źródło
W przypadku zestawów danych na dużą skalę możesz wypróbować ten GIST:
https://gist.github.com/chrisknoll/1b38761ce8c5016ec5b2
Działa poprzez agregację odrębnych wartości, które można znaleźć w zestawie (takich jak wiek, rok urodzenia itp.), I wykorzystuje funkcje okna SQL do zlokalizowania dowolnej pozycji percentyla określonej w zapytaniu.
źródło