Mam następujące zapytanie i ze względu na wiele SUM
wywołań funkcji moje zapytanie działa zbyt wolno. Mam dużo danych w swojej bazie danych i chciałbym otrzymać raport z bieżącego roku i zeszłego roku (Ostatnie 30 dni, Ostatnie 90 dni i ostatnie 365 dni) dla każdego:
SELECT
b.id as [ID]
,d.[Title] as [Title]
,e.Class as [Class]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 30 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 30 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 90 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 90 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 365 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 365 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 30 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 30 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 90 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 90 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 365 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 365 Days Col2]
FROM
tb1 a
INNER JOIN
tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN
tb3 c on b.fid = c.col5
INNER JOIN
tb4 d on c.id = d.col6
INNER JOIN
tb5 e on c.col7 = e.id
GROUP BY
b.id, d.Title, e.Class
Czy ktoś ma pojęcie, jak mogę ulepszyć moje zapytanie, aby działało szybciej?
EDYCJA: Zachęcono mnie do przeniesienia DATEADD
wywołania funkcji do where
instrukcji i załadowania najpierw dwóch lat, a następnie filtrowania ich w kolumnach, ale nie jestem pewien, czy sugerowana odpowiedź została wykonana i działa, można ją znaleźć tutaj: https: // stackoverflow. com / a / 59944426/12536284
Jeśli zgadzasz się z powyższym rozwiązaniem, pokaż mi, jak mogę zastosować go w bieżącym zapytaniu?
Po prostu, używam tego SP w C #, Entity Framework (DB-First), coś takiego:
var result = MyDBEntities.CalculatorSP();
Execution Plan
. Proszę zamieścićOdpowiedzi:
Jak już wspomniano, plan wykonania będzie w tym przypadku naprawdę pomocny. Na podstawie tego, co pokazałeś, wydaje się, że wyodrębniłeś 12 kolumn z 15 wszystkich kolumn
tb1 (a)
, więc możesz spróbować uruchomić zapytanie bez żadnego łączenia i tylko wtb1
celu sprawdzenia, czy zapytanie działa zgodnie z oczekiwaniami. Ponieważ nie widzę nic złego w twoich wywołaniach funkcji SUM, wydaje mi się, że masz problem ze swoimi połączeniami, sugeruję wykonanie następujących czynności. Możesz zacząć od wykluczenia na przykład ostatniego złączeniaINNER JOIN tb5 e on c.col7 = e.id
i wszelkich powiązanych z nim zastosowań, takich jake.Class as [Class]
ie.Class
w twojej grupie według oświadczenia. Nie zamierzamy go całkowicie wykluczyć, to tylko test, aby upewnić się, czy problem jest z tym, czy nie, jeśli twoje zapytanie działa lepiej i zgodnie z oczekiwaniami możesz spróbować użyć tabeli tymczasowej jako obejścia zamiast ostatniego łączenia , coś takiego:W rzeczywistości tabele tymczasowe to tabele tymczasowo istniejące na serwerze SQL. Tabele tymczasowe są przydatne do przechowywania bezpośrednich zestawów wyników, do których dostęp jest uzyskiwany wielokrotnie. Możesz przeczytać więcej na ten temat tutaj https://www.sqlservertutorial.net/sql-server-basics/sql-server-temporary-tables/ A tutaj https://codingsight.com/introduction-to-temporary-tables-in -sql-server /
Również gorąco polecam, jeśli używasz procedura przechowywana, ustaw
NOCOUNT
naON
, może też stanowić znaczący wzrost wydajności, ponieważ ruch w sieci jest znacznie zmniejszona:W oparciu o to :
źródło
tb5
do#Temp
tabeli i dołączanie do tabeli tymczasowej działa szybciej niż łączenietb5
bezpośrednie? niestety zawierają te same dane (i#Temp
może brakować indeksu, jeśli on istniałtb5
). Naprawdę nie rozumiem, dlaczego jest to bardziej wydajne (z tego co wiem, powinno być mniej wydajne kopiowanie wszystkich danych i dołączanie).tb5
znajduje się na innym serwerze? W takim przypadku użycie tabeli tymczasowej jest zdecydowanie szybsze niż bezpośrednie dołączenie do innego serwera. To była tylko propozycja przetestowania i sprawdzenia, czy coś się zmieniło. Miałem podobną sytuację w przeszłości i wydaje się na szczęście, że tabela temp pomogła OP również w tym przypadku.Najlepszym rozwiązaniem jest wstawienie do tabeli zmiennej / tabeli skrótów (jeśli liczba wierszy jest mała, użyj zmiennej tabeli lub użyj tabeli skrótów, jeśli liczba wierszy jest dość duża). Następnie zaktualizuj agregację, a następnie w końcu wybierz ze zmiennej tabeli lub tabeli mieszającej. Konieczne jest sprawdzenie planu zapytań.
źródło
Zakładam, że tb1 jest dużą tabelą (względem tb2, tb3, tb4 i tb5).
Jeśli tak, sensowne jest tutaj ograniczenie wyboru tej tabeli (z klauzulą WHERE).
Jeśli używana jest tylko niewielka część tb1, na przykład ponieważ połączenia za pomocą tb2, tb3, tb4 i tb5 zmniejszają potrzebne wiersze do zaledwie kilku procent, należy sprawdzić, czy tabele są indeksowane w kolumnach używanych w złączeniach .
Jeśli używana jest duża część tb1, warto zgrupować wyniki przed dołączeniem do tb2, tb3, tb4 i tb5. Poniżej znajduje się przykład tego.
źródło
Wystarczy użyć obliczonych kolumn
Przykład
Określ kolumny obliczane w tabeli
źródło
Aby zoptymalizować takie obliczenia, należy rozważyć wstępne obliczenie niektórych wartości. Ideą wstępnych obliczeń jest zmniejszenie liczby wierszy, które należy odczytać lub kontynuować.
Jednym ze sposobów osiągnięcia tego jest użycie widoku indeksowanego i pozostawienie silnika, aby sam wykonał obliczenia. Ponieważ tego typu widoki mają pewne ograniczenia, możesz stworzyć prostą tabelę i zamiast tego wykonać obliczenia. Zasadniczo zależy to od potrzeb biznesowych.
Tak więc, w przykładzie poniżej tworzę tabelę z
RowID
iRowDatetime
kolumn oraz wstawienie 1 milion wierszy. Korzystam z widoku indeksowanego do zliczania jednostek na dzień, więc zamiast przesyłać zapytania do 1 miliona wierszy rocznie, wykonuję zapytania do 365 wierszy rocznie, aby policzyć te metryki.Sukces takiego rozwiązania zależy w dużej mierze od sposobu dystrybucji danych i liczby posiadanych wierszy. Na przykład, jeśli masz jeden wpis dziennie dla każdego dnia roku, widok i tabela będą miały takie same dopasowanie wierszy, więc operacje we / wy nie zostaną zmniejszone.
Ponadto powyższe jest tylko przykładem zmaterializowania danych i ich odczytania. W twoim przypadku może być konieczne dodanie większej liczby kolumn definicji widoku.
źródło
Użyłbym tabeli przeglądowej „Daty”, aby połączyć moje dane z indeksem na DatesId. Używam dat jako filtra, gdy chcę przeglądać dane historyczne. Sprzężenie jest szybkie, a więc filtrowanie, ponieważ DatesId jest klastrowanym indeksem podstawowym (kluczem podstawowym). Dodaj także kolumnę z datą (jako kolumnę dołączoną) do tabeli danych.
Tabela dat zawiera następujące kolumny:
DatyId, Data, Rok, Kwartał, Rok Kwartał, MiesiącNum, MonthName Krótki, Rok Tydzień, WeekNum, DayOfYear, DayOfMonth, DayNumOfWeek, DayName
Przykładowe dane: 20310409 2031-04-09 2031 2 2031-Q2 4 kwietnia 2031_15 15 99 9 3 środa
Możesz wysłać mi wiadomość e-mail, jeśli chcesz mieć plik CSV, aby móc go zaimportować do bazy danych, ale jestem pewien, że możesz łatwo znaleźć coś takiego online i stworzyć własny.
Dodam również kolumnę tożsamości, aby można było uzyskać liczbę całkowitą dla każdej daty. To sprawia, że nieco łatwiej jest pracować, ale nie jest to wymagane.
To pozwala mi łatwo wrócić do pewnego okresu. Tworzenie własnych poglądów na ten temat jest dość łatwe. Możesz oczywiście użyć funkcji ROW_NUMBER (), aby zrobić to również przez lata, tygodnie itp.
Kiedy już będę mieć potrzebne dane, dołączam do danych. Działa bardzo szybko!
źródło
Ponieważ zawsze grupujesz wartości w oparciu o całą liczbę miesięcy, najpierw pogrupowałbym według miesięcy w podzapytanie w klauzuli from. Jest to podobne do korzystania z tabeli tymczasowej. Nie jestem pewien, czy rzeczywiście przyspieszyłoby to zapytanie.
źródło
Aby poprawić szybkość zapytania SQL, musisz dodać indeksy. Do każdej połączonej tabeli musisz dodać jeden indeks.
Podobnie jak w tym przykładzie kodu dla Oracle:
źródło