Wyobraź sobie, że masz następującą strukturę tabeli:
LogId | ProductId | FromPositionId | ToPositionId | Date | Quantity
-----------------------------------------------------------------------------------
1 | 123 | 0 | 10002 | 2018-01-01 08:10:22 | 5
2 | 123 | 0 | 10003 | 2018-01-03 15:15:10 | 9
3 | 123 | 10002 | 10004 | 2018-01-07 21:08:56 | 3
4 | 123 | 10004 | 0 | 2018-02-09 10:03:23 | 1
FromPositionId
i ToPositionId
są pozycjami giełdowymi. Niektóre identyfikatory pozycji mają na przykład specjalne znaczenie 0
. Zdarzenie z lub do 0
oznacza, że zapas został utworzony lub usunięty. Od 0
może być zapas z dostawy i 0
może być wysyłanym zamówieniem.
Ta tabela zawiera obecnie około 5,5 miliona wierszy. Obliczamy wartość zapasów dla każdego produktu i pozycji w tabeli pamięci podręcznej według harmonogramu, używając zapytania wyglądającego mniej więcej tak:
WITH t AS
(
SELECT ToPositionId AS PositionId, SUM(Quantity) AS Quantity, ProductId
FROM ProductPositionLog
GROUP BY ToPositionId, ProductId
UNION
SELECT FromPositionId AS PositionId, -SUM(Quantity) AS Quantity, ProductId
FROM ProductPositionLog
GROUP BY FromPositionId, ProductId
)
SELECT t.ProductId, t.PositionId, SUM(t.Quantity) AS Quantity
FROM t
WHERE NOT t.PositionId = 0
GROUP BY t.ProductId, t.PositionId
HAVING SUM(t.Quantity) > 0
Chociaż kończy się to w rozsądnym czasie (około 20 sekund), wydaje mi się, że jest to dość nieefektywny sposób obliczania wartości zapasów. Rzadko robimy nic opróczINSERT
: s w tej tabeli, ale czasami wchodzimy i dostosowujemy ilość lub ręcznie usuwamy wiersz z powodu błędów osób generujących te wiersze.
Wpadłem na pomysł utworzenia „punktów kontrolnych” w osobnej tabeli, obliczenia wartości do określonego momentu w czasie i wykorzystania jej jako wartości początkowej podczas tworzenia naszej tabeli pamięci podręcznej ilości zapasów:
ProductId | PositionId | Date | Quantity
-------------------------------------------------------
123 | 10002 | 2018-01-07 21:08:56 | 2
Fakt, że czasami zmieniamy wiersze, stanowi problem, w takim przypadku musimy również pamiętać o usunięciu dowolnego punktu kontrolnego utworzonego po zmianie wiersza dziennika. Można to rozwiązać, nie obliczając punktów kontrolnych do tej pory, ale pozostawiając miesiąc między teraz a ostatnim punktem kontrolnym (bardzo rzadko dokonujemy zmian tak daleko wstecz).
Trudno uniknąć faktu, że czasami musimy zmieniać wiersze i chciałbym móc to nadal robić, nie jest to pokazane w tej strukturze, ale zdarzenia dziennika są czasami powiązane z innymi rekordami w innych tabelach i dodają kolejny wiersz dziennika uzyskanie odpowiedniej ilości jest czasami niemożliwe.
Tabela dzienników rośnie, jak można sobie wyobrazić, dość szybko, a czas obliczeń wydłuża się z czasem.
Więc na moje pytanie, jak byś to rozwiązał? Czy istnieje bardziej skuteczny sposób obliczania bieżącej wartości zapasów? Czy mój pomysł na punkty kontrolne jest dobry?
Prowadzimy SQL Server 2014 Web (12.0.5511)
Plan wykonania: https://www.brentozar.com/pastetheplan/?id=Bk8gyc68Q
Właściwie podałem niepoprawny czas wykonania powyżej, 20s był czasem, który zajęła pełna aktualizacja pamięci podręcznej. Uruchomienie tego zapytania zajmuje około 6-10 sekund (8 sekund, kiedy utworzyłem ten plan zapytań). W tym zapytaniu jest również złączenie, którego nie było w pierwotnym pytaniu.