Zrobiłem skrzypek SQL dla tego pytania, jeśli to ułatwia każdemu.
Mam bazę danych o sportach fantasy i próbuję dowiedzieć się, jak wymyślić dane o „bieżącej serii” (np. „W2”, jeśli drużyna wygrała ostatnie 2 pojedynki, lub „L1”, jeśli przegrali) ich ostatni pojedynek po wygraniu poprzedniego pojedynku - lub „T1”, jeśli remisowali ostatni mecz).
Oto mój podstawowy schemat:
CREATE TABLE FantasyTeams (
team_id BIGINT NOT NULL
)
CREATE TABLE FantasyMatches(
match_id BIGINT NOT NULL,
home_fantasy_team_id BIGINT NOT NULL,
away_fantasy_team_id BIGINT NOT NULL,
fantasy_season_id BIGINT NOT NULL,
fantasy_league_id BIGINT NOT NULL,
fantasy_week_id BIGINT NOT NULL,
winning_team_id BIGINT NULL
)
Wartość NULL
w winning_team_id
kolumnie wskazuje remis dla tego dopasowania.
Oto przykładowa instrukcja DML z niektórymi przykładowymi danymi dla 6 drużyn i 3 tygodniowych pojedynków:
INSERT INTO FantasyTeams
SELECT 1
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 6
INSERT INTO FantasyMatches
SELECT 1, 2, 1, 2, 4, 44, 2
UNION
SELECT 2, 5, 4, 2, 4, 44, 5
UNION
SELECT 3, 6, 3, 2, 4, 44, 3
UNION
SELECT 4, 2, 4, 2, 4, 45, 2
UNION
SELECT 5, 3, 1, 2, 4, 45, 3
UNION
SELECT 6, 6, 5, 2, 4, 45, 6
UNION
SELECT 7, 2, 6, 2, 4, 46, 2
UNION
SELECT 8, 3, 5, 2, 4, 46, 3
UNION
SELECT 9, 4, 1, 2, 4, 46, NULL
GO
Oto przykład pożądanego wyniku (na podstawie powyższego DML), z którym mam problem, nawet zaczynam wymyślać, jak uzyskać:
| TEAM_ID | STEAK_TYPE | STREAK_COUNT |
|---------|------------|--------------|
| 1 | T | 1 |
| 2 | W | 3 |
| 3 | W | 3 |
| 4 | T | 1 |
| 5 | L | 2 |
| 6 | L | 1 |
Próbowałem różnych metod przy użyciu podkwerend i CTE, ale nie mogę tego połączyć. Chciałbym uniknąć używania kursora, ponieważ mógłbym mieć duży zestaw danych, aby uruchomić to w przyszłości. Wydaje mi się, że może istnieć sposób angażowania zmiennych tabeli, które w jakiś sposób łączą te dane z sobą, ale wciąż nad tym pracuję.
Informacje dodatkowe: Może być różna liczba drużyn (dowolna liczba parzysta od 6 do 10), a łączna liczba pojedynków wzrośnie o 1 dla każdej drużyny co tydzień. Wszelkie pomysły, jak to zrobić?
źródło
bigint
dla tylu kolumn, gdzieint
prawdopodobnie by to zrobił 3) dlaczego wszystkie te_
?! 4) Wolę, aby nazwy tabel były pojedyncze, ale potwierdzam, że nie wszyscy się ze mną zgadzają // ale te poza tym, co tu pokazałeś, wyglądają spójnie, takOdpowiedzi:
Ponieważ korzystasz z programu SQL Server 2012, możesz użyć kilku nowych funkcji okienkowania.
SQL Fiddle
C1
obliczastreak_type
dla każdej drużyny i meczu.C2
znajduje poprzedniestreak_type
uporządkowane przezmatch_id desc
.C3
generuje sumę bieżącąstreak_sum
uporządkowaną,match_id desc
zachowując0
długi, ponieważstreak_type
jest taki sam jak ostatnia wartość.Główne zapytanie podsumowuje serie, gdzie
streak_sum
jest0
.źródło
LEAD()
. Niewiele osób wie o nowych funkcjach okienkowania w 2012 r.FantasyTeams JOIN FantasyMatches
zFantasyMatches CROSS APPLY (VALUES (home_fantasy_team_id), (away_fantasy_team_id))
a tym samym potencjalnie zwiększyć wydajność.FantasyTeams
, prawdopodobnie lepiej zamiast tego dołączyć do głównego zapytania.Jednym z intuicyjnych sposobów rozwiązania tego problemu jest:
Strategia ta może wygrać z rozwiązaniem funkcji okna (które wykonuje pełne skanowanie danych) w miarę powiększania się tabeli, przy założeniu, że strategia rekurencyjna jest skutecznie wdrażana. Kluczem do sukcesu jest zapewnienie wydajnych indeksów w celu szybkiego zlokalizowania wierszy (za pomocą wyszukiwań) i uniknięcia sortowania. Potrzebne indeksy to:
Aby pomóc w optymalizacji zapytań, użyję tabeli tymczasowej do przechowywania wierszy określonych jako część bieżącej serii. Jeśli smugi są zazwyczaj krótkie (jak to jest w przypadku zespołów, które śledzę, niestety), ta tabela powinna być dość mała:
Moje rozwiązanie do zapytań rekurencyjnych jest następujące ( tutaj Fiddle SQL ):
Tekst T-SQL jest dość długi, ale każda sekcja zapytania ściśle odpowiada ogólnemu zarysowi procesu podanemu na początku tej odpowiedzi. Kwerenda jest dłuższa z powodu potrzeby użycia pewnych sztuczek, aby uniknąć sortowania i wygenerowania
TOP
rekurencyjnej części zapytania (co zwykle nie jest dozwolone).Plan wykonania jest stosunkowo niewielki i prosty w porównaniu z zapytaniem. Zacieniowałem obszar zakotwiczenia na żółto, a część rekurencyjną na zielonym zrzucie ekranu:
Dzięki zarejestrowaniu wierszy pasm w tabeli tymczasowej łatwo jest uzyskać wymagane podsumowanie wyników. (Użycie tabeli tymczasowej pozwala również uniknąć wycieku sortowania, który mógłby wystąpić, gdyby poniższe zapytanie zostało połączone z głównym zapytaniem rekurencyjnym)
To samo zapytanie może być wykorzystane jako podstawa do aktualizacji
FantasyTeams
tabeli:Lub, jeśli wolisz
MERGE
:Każde z tych podejść tworzy efektywny plan wykonania (na podstawie znanej liczby wierszy w tabeli tymczasowej):
Wreszcie, ponieważ metoda rekurencyjna naturalnie obejmuje
match_id
przetwarzanie, łatwo jest dodać listę wynikówmatch_id
tworzących każdą serię do wyniku:Wynik:
Plan wykonania:
źródło
EXISTS (... INTERSECT ...)
zamiast po prostuStreaks.streak_type = CASE ...
? Wiem, że poprzednia metoda może być przydatna, gdy trzeba dopasować wartości NULL po obu stronach, a także wartości, ale nie jest tak, jakby odpowiednia część mogła w tym przypadku wygenerować wartości NULL, więc ...CASE
jest używany, optymalizator nie może użyć konkatenacji scalającej (która zachowuje porządek klucza unii) i zamiast tego używa konkatenacji plus sortowania.Innym sposobem na uzyskanie wyniku jest rekurencyjne CTE
Demo SQLFiddle
źródło