Wyobraź sobie następującą tabelę (zwaną TestTable
):
id somedate somevalue
-- -------- ---------
45 01/Jan/09 3
23 08/Jan/09 5
12 02/Feb/09 0
77 14/Feb/09 7
39 20/Feb/09 34
33 02/Mar/09 6
Chciałbym zapytać, które zwraca sumę bieżącą w kolejności dat, na przykład:
id somedate somevalue runningtotal
-- -------- --------- ------------
45 01/Jan/09 3 3
23 08/Jan/09 5 8
12 02/Feb/09 0 8
77 14/Feb/09 7 15
39 20/Feb/09 34 49
33 02/Mar/09 6 55
Wiem, że w SQL Server 2000/2005/2008 można to zrobić na różne sposoby .
Szczególnie interesuje mnie tego rodzaju metoda, która wykorzystuje sztuczkę agregującą zestaw instrukcji:
INSERT INTO @AnotherTbl(id, somedate, somevalue, runningtotal)
SELECT id, somedate, somevalue, null
FROM TestTable
ORDER BY somedate
DECLARE @RunningTotal int
SET @RunningTotal = 0
UPDATE @AnotherTbl
SET @RunningTotal = runningtotal = @RunningTotal + somevalue
FROM @AnotherTbl
... jest to bardzo wydajne, ale słyszałem, że istnieją problemy z tym związane, ponieważ nie zawsze można zagwarantować, że UPDATE
instrukcja przetworzy wiersze we właściwej kolejności. Może uda nam się uzyskać ostateczne odpowiedzi na ten temat.
Ale może istnieją inne sposoby, które ludzie mogą zasugerować?
edycja: Teraz z SqlFiddle z konfiguracją i przykładem „sztuczki z aktualizacją” powyżej
sql
sql-server
tsql
running-total
codeulike
źródło
źródło
Odpowiedzi:
Aktualizacja , jeśli używasz programu SQL Server 2012, zobacz: https://stackoverflow.com/a/10309947
Problem polega na tym, że implementacja klauzuli Over w SQL Server jest nieco ograniczona .
Oracle (i ANSI-SQL) pozwalają na:
SQL Server nie zapewnia prostego rozwiązania tego problemu. Moje przeczucie podpowiada mi, że jest to jeden z tych rzadkich przypadków, w których kursor jest najszybszy, chociaż będę musiał przeprowadzić pewne testy porównawcze dla dużych wyników.
Sztuczka aktualizacji jest przydatna, ale czuję, że jest dość delikatna. Wygląda na to, że jeśli aktualizujesz pełną tabelę, będzie to postępować w kolejności klucza podstawowego. Więc jeśli ustawisz datę jako klucz podstawowy rosnąco, będziesz
probably
bezpieczny. Ale polegasz na nieudokumentowanych szczegółach implementacji SQL Server (również jeśli zapytanie zostanie wykonane przez dwa procesy, zastanawiam się, co się stanie, zobacz: MAXDOP):Pełna próbka robocza:
Poprosiłeś o test porównawczy, to jest podsumowanie.
Najszybszym BEZPIECZNYM sposobem zrobienia tego byłby Cursor, jest to o rząd wielkości szybsze niż skorelowane zapytanie podrzędne przy sprzężeniu krzyżowym.
Absolutnie najszybszym sposobem jest sztuczka UPDATE. Obawiam się tylko, że nie jestem pewien, czy w każdych okolicznościach aktualizacja będzie przebiegać liniowo. W zapytaniu nie ma nic, co wyraźnie to mówi.
Podsumowując, w przypadku kodu produkcyjnego podążałbym za kursorem.
Dane testowe:
Test 1:
Test 2:
Test 3:
Test 4:
źródło
W SQL Server 2012 można używać funkcji SUM () z klauzulą OVER () .
SQL Fiddle
źródło
Chociaż Sam Saffron wykonał świetną robotę, nadal nie dostarczył rekurencyjnego wspólnego kodu wyrażeń tabelowych dla tego problemu. A dla nas, którzy pracujemy z SQL Server 2008 R2, a nie z Denali, jest to nadal najszybszy sposób na uzyskanie całkowitej liczby danych, jest około 10 razy szybszy niż kursor na moim komputerze roboczym dla 100000 wierszy, a także jest to zapytanie wbudowane.
A więc oto jest (przypuszczam, że
ord
w tabeli jest kolumna i jest to numer kolejny bez przerw, dla szybkiego przetwarzania również powinno być unikalne ograniczenie dla tej liczby):sql fiddle demo
aktualizacja Byłem również ciekawy tej aktualizacji ze zmienną lub dziwaczną aktualizacją . Czyli zwykle działa dobrze, ale skąd możemy mieć pewność, że działa za każdym razem? cóż, oto mała sztuczka (znalazłem ją tutaj - http://www.sqlservercentral.com/Forums/Topic802558-203-21.aspx#bm981258 ) - po prostu sprawdzasz bieżące i poprzednie
ord
i używasz1/0
przypisania na wypadek, gdyby różniły się od tego spodziewasz się:Z tego, co widziałem, jeśli masz odpowiedni indeks klastrowy / klucz podstawowy na swojej tabeli (w naszym przypadku byłby to indeks przez
ord_id
), aktualizacja będzie przebiegać w sposób liniowy przez cały czas (nigdy nie napotkano dzielenia przez zero). To powiedziawszy, to do Ciebie należy decyzja, czy chcesz użyć go w kodzie produkcyjnym :)aktualizacja 2 Łączę tę odpowiedź, ponieważ zawiera przydatne informacje o zawodności dziwacznej aktualizacji - niewytłumaczalne zachowanie nvarchar concatenation / index / nvarchar (max) .
źródło
W tym celu działa operator APPLY w SQL 2005 i nowszych wersjach:
źródło
Możesz również użyć funkcji ROW_NUMBER () i tabeli tymczasowej, aby utworzyć dowolną kolumnę do użycia w porównaniu w wewnętrznej instrukcji SELECT.
źródło
Użyj skorelowanego zapytania podrzędnego. Bardzo proste, proszę bardzo:
Kod może nie być dokładnie poprawny, ale jestem pewien, że tak jest.
GROUP BY jest w przypadku, gdy data pojawia się więcej niż raz, chciałbyś zobaczyć ją tylko raz w zestawie wyników.
Jeśli nie masz nic przeciwko wyświetlaniu powtarzających się dat lub chcesz zobaczyć oryginalną wartość i identyfikator, to chcesz:
źródło
Możesz również zdenormalizować - przechowywać bieżące sumy w tej samej tabeli:
http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx
Wybory działają znacznie szybciej niż jakiekolwiek inne rozwiązania, ale modyfikacje mogą być wolniejsze
źródło
Zakładając, że okienkowanie działa na SQL Server 2008 tak samo jak gdzie indziej (co próbowałem), spróbuj:
MSDN twierdzi, że jest dostępny w SQL Server 2008 (a może także 2005?), Ale nie mam instancji, aby ją wypróbować.
EDYCJA: cóż, najwyraźniej SQL Server nie zezwala na specyfikację okna („OVER (...)”) bez określenia „PARTITION BY” (dzielenie wyniku na grupy, ale nie agregowanie w taki sposób, jak robi to GROUP BY). Irytujące - odwołanie do składni MSDN sugeruje, że jest to opcjonalne, ale w tej chwili mam tylko wystąpienia SqlServer 2000.
Zapytanie, które podałem, działa zarówno w Oracle 10.2.0.3.0, jak i PostgreSQL 8.4-beta. Więc powiedz MS, żeby nadrobiło zaległości;)
źródło
1 partitionme
i podziel według tego. Ponadto podział według jest prawdopodobnie potrzebny w sytuacjach rzeczywistych podczas tworzenia raportów.Jeśli używasz Sql Server 2008 R2 powyżej. Wtedy byłaby to najkrótsza droga;
LAG służy do uzyskania wartości poprzedniego wiersza. Możesz zrobić Google, aby uzyskać więcej informacji.
[1]:
źródło
SUM(somevalue) OVER(...)
co wydaje mi się dużo czystszeUważam, że bieżącą sumę można osiągnąć za pomocą prostej operacji INNER JOIN poniżej.
źródło
Poniżej przedstawiono wymagane wyniki.
Posiadanie indeksu klastrowego w SomeDate znacznie poprawi wydajność.
źródło
Używanie złączenia Inną odmianą jest użycie złączenia. Teraz zapytanie mogłoby wyglądać następująco:
po więcej możesz odwiedzić ten link http://askme.indianyouth.info/details/calculating-simple-running-totals-in-sql-server-12
źródło
Chociaż najlepszym sposobem jest użycie funkcji okna, można to również zrobić za pomocą prostego skorelowanego zapytania podrzędnego .
źródło
źródło
Oto 2 proste sposoby obliczenia sumy bieżącej:
Podejście 1 : Można to zapisać w ten sposób, jeśli Twój DBMS obsługuje funkcje analityczne
Podejście 2 : Możesz skorzystać z APLIKACJI ZEWNĘTRZNEJ, jeśli Twoja wersja bazy danych / sam DBMS nie obsługuje funkcji analitycznych
Uwaga: - Jeśli musisz obliczyć sumę bieżącą dla różnych partycji osobno, można to zrobić w sposób opisany tutaj: Obliczanie sum bieżących w wierszach i grupowanie według identyfikatora
źródło