Hier ist ein Modell, um Ihre angegebene Anforderung zu erfüllen.
Link zum Zeitreihen-Datenmodell
Link zur IDEF1X-Notation für diejenigen, die mit dem Relational Modeling Standard nicht vertraut sind.
-
Normalisiert auf 5NF; keine doppelten Spalten; keine Update-Anomalien, keine Nullen.
-
Wenn sich der Status eines Produkts ändert, fügen Sie einfach eine Zeile mit der aktuellen DateTime in ProductStatus ein. Keine Notwendigkeit, vorherige Zeilen zu berühren (die wahr waren und wahr bleiben). Keine Dummy-Werte, die Berichtstools (außer Ihrer App) interpretieren müssen.
-
Die DateTime ist die tatsächliche DateTime, zu der das Produkt in diesen Status versetzt wurde; das "Von", wenn Sie so wollen. Das „Bis“ lässt sich leicht ableiten:Es ist die DateTime der nächsten Zeile (DateTime> „From“) für das Produkt; wo es nicht existiert, ist der Wert die aktuelle DateTime (verwenden Sie ISNULL).
Das erste Modell ist fertig; (ProductId, DateTime) reicht aus, um dem Primärschlüssel Eindeutigkeit zu verleihen. Da Sie jedoch für bestimmte Abfragebedingungen Geschwindigkeit anfordern, können wir das Modell auf der physischen Ebene verbessern und Folgendes bereitstellen:
-
Ein Index (wir haben bereits den PK-Index, also werden wir diesen zuerst verbessern, bevor wir einen zweiten Index hinzufügen), um abgedeckte Abfragen zu unterstützen (solche, die auf einer beliebigen Anordnung von { ProductId | DateTime | Status } basieren, können vom Index bereitgestellt werden, ohne dass dies erforderlich ist um zu den Datenzeilen zu gehen). Dadurch ändert sich die Beziehung Status::ProductStatus von Non-Identifying (gestrichelte Linie) zu Identifizierender Typ (durchgezogene Linie).
-
Die PK-Anordnung wird auf der Grundlage ausgewählt, dass die meisten Abfragen Zeitreihen sind, basierend auf Product⇢DateTime⇢Status.
-
Der zweite Index wird bereitgestellt, um die Geschwindigkeit von Abfragen basierend auf Status zu erhöhen.
-
In der alternativen Anordnung ist das umgekehrt; dh wir wollen meistens den aktuellen Status aller Produkte.
-
In allen Darstellungen von ProductStatus ist die DateTime-Spalte im sekundären Index (nicht der PK) DESCending; das neueste kommt zuerst.
Ich habe die gewünschte Diskussion bereitgestellt. Natürlich müssen Sie mit einem Datensatz angemessener Größe experimentieren und Ihre eigenen Entscheidungen treffen. Wenn Sie hier etwas nicht verstehen, fragen Sie bitte, und ich werde es erweitern.
Antworten auf Kommentare
Melden Sie alle Produkte mit dem aktuellen Status 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
ist indexiert, führende Spalte, beide Seiten -
DateTime
in Indexiert, 2. Spalte in Covered Query Option -
StatusCode
ist indiziert, 3. Spalte in Covered Query Option -
Seit
StatusCode
im Index absteigend ist, ist nur ein Abruf erforderlich, um die innere Abfrage zu erfüllen -
die Zeilen werden gleichzeitig für die eine Abfrage benötigt; sie liegen nahe beieinander (aufgrund des Clstered Index); aufgrund der kurzen Zeilengröße fast immer auf der gleichen Seite.
Dies ist gewöhnliches SQL, eine Unterabfrage, die die Leistungsfähigkeit der SQL-Engine, der Verarbeitung relationaler Mengen, nutzt. Es ist die einzig richtige Methode , es gibt nichts schnelleres und jede andere Methode wäre langsamer. Jedes Berichtstool erstellt diesen Code mit wenigen Klicks, ganz ohne Eintippen.
Zwei Daten im Produktstatus
Spalten wie DateTimeFrom und DateTimeTo sind grobe Fehler. Sortieren wir es nach Wichtigkeit.
-
Es ist ein grober Normalisierungsfehler. "DateTimeTo" lässt sich leicht von der einzelnen DateTime der nächsten Zeile ableiten; es ist daher redundant, eine doppelte Spalte.
- Die Genauigkeit spielt dabei keine Rolle:Das lässt sich leicht durch den Datentyp (DATE, DATETIME, SMALLDATETIME) auflösen. Ob Sie eine Sekunde, Mikrosekunde oder Nanosekunde weniger anzeigen, ist eine Geschäftsentscheidung; es hat nichts mit den gespeicherten Daten zu tun.
-
Das Implementieren einer DateTo-Spalte ist ein 100-prozentiges Duplikat (von DateTime der nächsten Zeile). Dies nimmt das Doppelte des Speicherplatzes in Anspruch . Bei einer großen Tabelle wäre das eine erhebliche unnötige Verschwendung.
-
Da es sich um eine kurze Reihe handelt, benötigen Sie doppelt so viele logische und physische I/Os um die Tabelle bei jedem Zugriff zu lesen.
-
Und doppelt so viel Cache-Speicherplatz (oder anders ausgedrückt, nur halb so viele Zeilen würden in einen gegebenen Cache-Speicherplatz passen).
-
Durch die Einführung einer doppelten Spalte haben Sie die Möglichkeit eines Fehlers eingeführt (der Wert kann jetzt auf zwei Arten abgeleitet werden:aus der doppelten DateTimeTo-Spalte oder der DateTimeFrom der nächsten Zeile).
-
Dies ist auch eine Update-Anomalie . Wenn Sie ein beliebiges DateTimeFrom aktualisieren, muss DateTimeTo der vorherigen Zeile abgerufen (keine große Sache, da es nah ist) und aktualisiert werden (große Sache, da es ein zusätzliches Verb ist, das vermieden werden kann).
-
"Kürzer" und "Codierungskürzel" sind irrelevant, SQL ist eine umständliche Datenmanipulationssprache, aber SQL ist alles, was wir haben (Komm damit klar). Wer eine Unterabfrage nicht codieren kann, sollte wirklich nicht codieren. Jeder, der eine Spalte dupliziert, um kleinere "Schwierigkeiten" beim Codieren zu verringern, sollte wirklich keine Datenbanken modellieren.
Beachten Sie gut, dass, wenn die Regel höchster Ordnung (Normalisierung) beibehalten wurde, der gesamte Satz von Problemen niedrigerer Ordnung eliminiert wird.
In Sätzen denken
-
Jeder, der beim Schreiben von einfachem SQL „Schwierigkeiten“ oder „Schmerzen“ hat, ist in der Erfüllung seiner beruflichen Funktion gelähmt. Normalerweise ist der Entwickler nicht Denken in Mengen und die relationale Datenbank ist ein mengenorientiertes Modell .
-
Für die obige Abfrage benötigen wir die aktuelle DateTime; da ProductStatus ein set ist von Produktzuständen in chronologischer Reihenfolge benötigen wir einfach das neueste oder MAX(DateTime) des Satzes die zum Produkt gehören.
-
Schauen wir uns nun etwas an, das angeblich "schwierig" ist, in Bezug auf Sets . Für einen Bericht über die Dauer, in der sich jedes Produkt in einem bestimmten Zustand befunden hat:DateTimeFrom ist eine verfügbare Spalte und definiert den horizontalen Grenzwert, einen untergeordneten Satz (wir können frühere Zeilen ausschließen); die DateTimeTo ist die früheste des Unter-Satzes der Produktzustände.
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
-
Denken Sie in Begriffen, die nächste Reihe zu bekommen zeilenorientiert ist, nicht mengenorientierte Verarbeitung. Lähmend, wenn man mit einer mengenorientierten Datenbank arbeitet. Lassen Sie den Optimiser all das Denken für Sie erledigen. Überprüfen Sie Ihren SHOWPLAN, dieser optimiert wunderbar.
-
Unfähigkeit, in Mengen zu denken , die somit darauf beschränkt ist, nur einstufige Abfragen zu schreiben, ist keine vernünftige Rechtfertigung für:die Implementierung massiver Duplizierungen und Aktualisierungsanomalien in der Datenbank; Verschwendung von Online-Ressourcen und Speicherplatz; garantiert die halbe Leistung. Viel billiger zu lernen, wie man einfache SQL-Unterabfragen schreibt, um leicht abgeleitete Daten zu erhalten.