Oto model, który pozwoli Ci spełnić określone wymagania.
Link do modelu danych szeregów czasowych
Link do notacji IDEF1X dla tych, którzy nie są zaznajomieni ze standardem modelowania relacyjnego.
-
Znormalizowane do 5NF; brak zduplikowanych kolumn; bez anomalii aktualizacji, bez zer.
-
Gdy zmieni się status produktu, po prostu wstaw wiersz do ProductStatus z bieżącą datą i godziną. Nie trzeba dotykać poprzednich wierszy (które były prawdziwe i pozostają prawdziwe). Nie ma żadnych fikcyjnych wartości, które narzędzia raportujące (inne niż Twoja aplikacja) muszą interpretować.
-
Data i godzina to rzeczywista data i godzina, w której produkt został umieszczony w tym statusie; „Od”, jeśli chcesz. „Do” można łatwo wyprowadzić:jest to data i godzina następnego wiersza (DataCzas> „Od”) dla Produktu; tam, gdzie nie istnieje, wartością jest bieżąca data i godzina (użyj ISNULL).
Pierwszy model jest kompletny; (ProductId, DateTime) wystarczy, aby zapewnić unikatowość klucza podstawowego. Ponieważ jednak żądasz szybkości w określonych warunkach zapytania, możemy ulepszyć model na poziomie fizycznym i zapewnić:
-
Indeks (mamy już indeks PK, więc ulepszymy ten pierwszy, przed dodaniem drugiego indeksu) do obsługi zapytań objętych zakresem (te oparte na dowolnym układzie { ProductId | DateTime | Status } mogą być dostarczone przez indeks, bez konieczności posiadania aby przejść do wierszy danych). Który zmienia relację Status::ProductStatus z Nieidentyfikujące (linia przerywana) na Typ identyfikujący (linia ciągła).
-
Układ PK jest wybierany na podstawie tego, że większość zapytań będzie dotyczyć szeregów czasowych, w oparciu o stan produktu⇢data i godzina⇢.
-
Drugi indeks jest dostarczany w celu zwiększenia szybkości zapytań w oparciu o Status.
-
W układzie alternatywnym jest to odwrócone; tzn. zależy nam głównie na aktualnym stanie wszystkich Produktów.
-
We wszystkich wersjach ProductStatus kolumna DateTime w indeksie wtórnym (nie PK) to DESCending; najnowsza jest pierwsza.
Udostępniłem dyskusję, o którą prosiłeś. Oczywiście musisz poeksperymentować z zestawem danych o rozsądnej wielkości i podjąć własne decyzje. Jeśli jest tu coś, czego nie rozumiesz, zapytaj, a ja rozwinę.
Odpowiedzi na komentarze
Zgłoś wszystkie produkty o aktualnym stanie 2
SELECT ProductId,
Description
FROM Product p,
ProductStatus ps
WHERE p.ProductId = ps.ProductId -- Join
AND StatusCode = 2 -- Request
AND DateTime = ( -- Current Status on the left ...
SELECT MAX(DateTime) -- Current Status row for outer Product
FROM ProductStatus ps_inner
WHERE p.ProductId = ps_inner.ProductId
)
-
ProductId
jest indeksowany, wiodący kolor, obie strony -
DateTime
w indeksie, druga kolumna w Covered Query Option -
StatusCode
jest indeksowany, trzecia kolumna w Covered Query Option -
Od
StatusCode
w indeksie to DESCending, do spełnienia wewnętrznego zapytania wymagane jest tylko jedno pobranie -
wiersze są wymagane w tym samym czasie dla jednego zapytania; są blisko siebie (ze względu na Clustered Index); prawie zawsze na tej samej stronie ze względu na krótki rozmiar wiersza.
Jest to zwykły SQL, podzapytanie, wykorzystujące moc silnika SQL, przetwarzanie zbiorów relacyjnych. Jest to jedna poprawna metoda , nie ma nic szybszego, a każda inna metoda byłaby wolniejsza. Każde narzędzie raportujące wygeneruje ten kod za pomocą kilku kliknięć, bez wpisywania.
Dwie daty w stanie produktu
Kolumny takie jak DateTimeFrom i DateTimeTo są błędami rażącymi. Weźmy to w kolejności ważności.
-
Jest to poważny błąd normalizacji. „DateTimeTo” można łatwo wyprowadzić z pojedynczej DateTime następnego wiersza; dlatego jest zbędna, zduplikowana kolumna.
- Precyzja nie wchodzi w grę:można to łatwo rozwiązać dzięki DataType (DATE, DATETIME, SMALLDATETIME). Niezależnie od tego, czy wyświetlasz jedną sekundę mniej, mikrosekundę, czy nanosekundę, to decyzja biznesowa; nie ma to nic wspólnego z przechowywanymi danymi.
-
Implementacja kolumny DateTo jest 100% duplikatem (z DateTime następnego wiersza). Zajmuje to dwa razy więcej miejsca na dysku . W przypadku dużego stołu byłoby to znaczne niepotrzebne marnotrawstwo.
-
Biorąc pod uwagę, że jest to krótki rząd, będziesz potrzebować dwa razy więcej logicznych i fizycznych operacji we/wy czytać tabelę przy każdym dostępie.
-
I dwa razy więcej miejsca w pamięci podręcznej (lub ujmując to inaczej, tylko o połowę mniej wierszy zmieściłoby się w danej przestrzeni pamięci podręcznej).
-
Wprowadzając zduplikowaną kolumnę, wprowadziłeś możliwość wystąpienia błędu (wartość można teraz wyprowadzić na dwa sposoby:ze zduplikowanej kolumny DateTimeTo lub DateTimeFrom następnego wiersza).
-
Jest to również Anomalia aktualizacji . Po zaktualizowaniu dowolnego DateTimeFrom jest aktualizowany, należy pobrać DateTimeTo z poprzedniego wiersza (nic wielkiego, ponieważ jest blisko) i zaktualizować (duża sprawa, ponieważ jest to dodatkowy czasownik, którego można uniknąć).
-
„Krótsze” i „skróty kodowania” są nieistotne, SQL jest niewygodnym językiem do manipulacji danymi, ale SQL to wszystko, co mamy (Licz sie z tym). Każdy, kto nie potrafi zakodować podzapytania, tak naprawdę nie powinien kodować. Każdy, kto duplikuje kolumnę w celu ułatwienia „trudności” kodowania, tak naprawdę nie powinien modelować baz danych.
Zauważ dobrze, że jeśli została zachowana reguła najwyższego rzędu (Normalizacja), cały zestaw problemów niższego rzędu zostanie wyeliminowany.
Myśl w kategoriach zestawów
-
Każdy, kto ma „trudności” lub doświadcza „bólu” podczas pisania prostego SQL, ma problemy z wykonywaniem swojej funkcji. Zazwyczaj programista nie myślenie w kategoriach zestawów a relacyjna baza danych jest modelem zorientowanym na zbiór .
-
Dla powyższego zapytania potrzebujemy Current DateTime; ponieważ ProductStatus to zestaw stanów produktów w porządku chronologicznym, potrzebujemy po prostu najnowszego lub MAX(DateTime) zestawu należące do Produktu.
-
Teraz spójrzmy na coś rzekomo „trudnego” w kategoriach zestawów . W przypadku raportu na temat czasu trwania każdego produktu w określonym stanie:kolumna DateTimeFrom jest dostępną kolumną i definiuje poziome odcięcie, pod zestaw (możemy wykluczyć wcześniejsze wiersze); DateTimeTo jest najwcześniejszym z pod zestawu stanów produktów.
SELECT ProductId,
Description,
[DateFrom] = DateTime,
[DateTo] = (
SELECT MIN(DateTime) -- earliest in subset
FROM ProductStatus ps_inner
WHERE p.ProductId = ps_inner.ProductId -- our Product
AND ps_inner.DateTime > ps.DateTime -- defines subset, cutoff
)
FROM Product p,
ProductStatus ps
WHERE p.ProductId = ps.ProductId
AND StatusCode = 2 -- Request
-
Myślenie w kategoriach uzyskania następnego wiersza jest zorientowany na wiersz, nie przetwarzanie zorientowane na zbiór. Paraliż podczas pracy z bazą danych zorientowaną na zbiór. Niech Optymalizator zrobi to wszystko za Ciebie. Sprawdź swój SHOWPLAN, to świetnie optymalizuje.
-
Niezdolność do myślenia zestawami , ograniczając się tym samym do pisania tylko jednopoziomowych zapytań, nie jest rozsądnym uzasadnieniem dla:implementacji masowej duplikacji i aktualizacji anomalii w bazie danych; marnowanie zasobów online i miejsca na dysku; gwarantując połowę wydajności. O wiele taniej jest nauczyć się pisać proste podzapytania SQL, aby uzyskać łatwo wyprowadzone dane.