Wenn ich das Szenario angemessen verstehe, sollten Sie eine Tabelle definieren, die eine Preiszeitreihe enthält . Daher stimme ich zu, dass dies viel mit dem zeitlichen Aspekt der Datenbank zu tun hat, mit der Sie arbeiten.
Geschäftsregeln
Beginnen wir mit der Analyse der Situation auf konzeptioneller Ebene. Also, wenn in Ihrer Geschäftsdomäne,
- Ein Produkt wird zu einem zu vielen Preisen gekauft .
- jeder Preis des Kaufs wird Strom an einem exakten Startdatum und
- der Preis EndDate (die die angibt , Datum , wenn ein Preis zu sein aufhört Strom ) ist gleich dem Startdatum des unmittelbar nachfolgenden Preis ,
dann bedeutet das das
- es gibt keine Lücken zwischen den verschiedenen Perioden , in denen die Preise sind aktuelle (die Zeitreihe ist kontinuierlich oder in Konjunktion ) und
- Das Enddatum eines Preises ist ein ableitbares Datum.
Das in Abbildung 1 gezeigte IDEF1X- Diagramm zeigt ein solches Szenario, obwohl es stark vereinfacht ist:
Logisches Layout des Expositorys
Das folgende Design auf logischer SQL-DDL-Ebene, das auf dem IDEF1X-Diagramm basiert, zeigt einen praktikablen Ansatz, den Sie genau an Ihre eigenen Anforderungen anpassen können:
-- At the physical level, you should define a convenient
-- indexing strategy based on the data manipulation tendencies
-- so that you can supply an optimal execution speed of the
-- queries declared at the logical level; thus, some testing
-- sessions with considerable data load should be carried out.
CREATE TABLE Product (
ProductNumber INT NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Product_PK PRIMARY KEY (ProductNumber)
);
CREATE TABLE Price (
ProductNumber INT NOT NULL,
StartDate DATE NOT NULL,
Amount INT NOT NULL, -- Retains the amount in cents, but there are other options regarding the type of use.
--
CONSTRAINT Price_PK PRIMARY KEY (ProductNumber, StartDate),
CONSTRAINT Price_to_Product_FK FOREIGN KEY (ProductNumber)
REFERENCES Product (ProductNumber),
CONSTRAINT AmountIsValid_CK CHECK (Amount >= 0)
);
Die Price
Tabelle enthält einen zusammengesetzten PRIMARY KEY, der aus zwei Spalten besteht, dh ProductNumber
(wiederum eingeschränkt als FOREIGN KEY, auf den verwiesen wird Product.ProductNumber
) und StartDate
(unter Angabe des bestimmten Datums, an dem ein bestimmtes Produkt zu einem bestimmten Preis gekauft wurde ). .
Für den Fall , dass Produkte an unterschiedlichen gekauft Preisen im gleichen Tag statt der StartDate
Spalte können Sie einen als gekennzeichnet sind , StartDateTime
dass hält die Sofort , wenn ein bestimmte Artikel in einem exakten erworben wurde Preis . Der PRIMARY KEY müsste dann als deklariert werden (ProductNumber, StartDateTime)
.
Wie gezeigt, handelt es sich bei der oben genannten Tabelle um eine gewöhnliche Tabelle, da Sie die Operationen SELECT, INSERT, UPDATE und DELETE deklarieren können, um ihre Daten direkt zu bearbeiten. Daher (a) kann die Installation zusätzlicher Komponenten vermieden werden, und (b) kann in allen verwendet werden die wichtigsten SQL-Plattformen mit einigen Anpassungen, falls erforderlich.
Datenmanipulationsbeispiele
Um einige Manipulationen exemplifizieren , die nützlich erscheinen, lassen Sie uns sagen , dass Sie die folgenden Daten in den eingefügt haben Product
und Price
Tabellen dargestellt:
INSERT INTO Product
(ProductNumber, Etcetera)
VALUES
(1750, 'Price time series sample');
INSERT INTO Price
(ProductNumber, StartDate, Amount)
VALUES
(1750, '20170601', 1000),
(1750, '20170603', 3000),
(1750, '20170605', 4000),
(1750, '20170607', 3000);
Da es sich bei dem Price.EndDate
um einen ableitbaren Datenpunkt handelt, müssen Sie ihn genau über eine abgeleitete Tabelle abrufen, die als Ansicht erstellt werden kann, um die „vollständige“ Zeitreihe zu erstellen, wie im Folgenden veranschaulicht:
CREATE VIEW PriceWithEndDate AS
SELECT P.ProductNumber,
P.Etcetera AS ProductEtcetera,
PR.Amount AS PriceAmount,
PR.StartDate,
(
SELECT MIN(StartDate)
FROM Price InnerPR
WHERE P.ProductNumber = InnerPR.ProductNumber
AND InnerPR.StartDate > PR.StartDate
) AS EndDate
FROM Product P
JOIN Price PR
ON P.ProductNumber = PR.ProductNumber;
Dann die folgende Operation, die direkt aus dieser Ansicht auswählt
SELECT ProductNumber,
ProductEtcetera,
PriceAmount,
StartDate,
EndDate
FROM PriceWithEndDate
ORDER BY StartDate DESC;
liefert die nächste Ergebnismenge:
ProductNumber ProductEtcetera PriceAmount StartDate EndDate
------------- ------------------ ----------- ---------- ----------
1750 Price time series… 4000 2017-06-07 NULL -- (*)
1750 Price time series… 3000 2017-06-05 2017-06-07
1750 Price time series… 2000 2017-06-03 2017-06-05
1750 Price time series… 1000 2017-06-01 2017-06-03
-- (*) A ‘sentinel’ value would be useful to avoid the NULL marks.
Nehmen wir nun an, dass Sie daran interessiert sind, die gesamten Price
Daten für die Product
primär identifizierten bis ProductNumber
1750 am Date
2. Juni 2017 zu erhalten . Wenn Sie sehen, dass eine Price
Zusicherung (oder Zeile) während des gesamten Intervalls , das von (i) StartDate
bis (ii) bis zu (ii) läuft, aktuell oder wirksam ist EndDate
, dann diese DML-Operation
SELECT ProductNumber,
ProductEtcetera,
PriceAmount,
StartDate,
EndDate
FROM PriceWithEndDate
WHERE ProductNumber = 1750 -- (1)
AND StartDate <= '20170602' -- (2)
AND EndDate >= '20170602'; -- (3)
-- (1), (2) and (3): You can supply parameters in place of fixed values to make the query more versatile.
ergibt die folgende Ergebnismenge
ProductNumber ProductEtcetera PriceAmount StartDate EndDate
------------- ------------------ ----------- ---------- ----------
1750 Price time series… 1000 2017-06-01 2017-06-03
die diese Anforderung adressiert.
Wie gezeigt, PriceWithEndDate
spielt die Ansicht eine entscheidende Rolle beim Abrufen der meisten ableitbaren Daten und kann auf ziemlich gewöhnliche Weise AUSGEWÄHLT werden.
Unter Berücksichtigung der Tatsache, dass Ihre bevorzugte Plattform PostgreSQL ist, enthält dieser Inhalt der offiziellen Dokumentationsseite Informationen zu „materialisierten“ Ansichten , die dazu beitragen können, die Ausführungsgeschwindigkeit mithilfe von Mechanismen auf physikalischer Ebene zu optimieren, falls dieser Aspekt problematisch wird. Andere SQL-Datenbankverwaltungssysteme (DBMS) bieten physische Instrumente, die sich sehr ähnlich sind, obwohl möglicherweise andere Begriffe verwendet werden, z. B. "indizierte" Ansichten in Microsoft SQL Server.
Sie können die besprochenen DDL- und DML-Codebeispiele in Aktion in dieser Datenbank-Geige und in dieser SQL-Geige sehen .
Ähnliche Resourcen
In diesen Fragen und Antworten wird ein Geschäftskontext erörtert , der die Änderungen der Produktpreise umfasst, jedoch einen größeren Umfang aufweist, sodass Sie ihn möglicherweise von Interesse finden.
Diese Stapelüberlauf-Posts decken sehr relevante Punkte in Bezug auf den Typ einer Spalte ab, die ein Währungsdatum in PostgreSQL enthält.
Antworten auf Kommentare
Dies sieht ähnlich aus wie meine Arbeit, aber ich fand es viel bequemer / effizienter, mit einer Tabelle zu arbeiten, in der ein Preis (in diesem Fall) eine Startdatums- und eine Enddatumspalte enthält. Sie suchen also nur nach Zeilen mit Zieldatum > = Startdatum und Zieldatum <= Enddatum. Wenn die Daten nicht in diesen Feldern gespeichert sind (einschließlich Enddatum 31. Dezember 9999, nicht Null, wo kein tatsächliches Enddatum vorhanden ist), müssen Sie natürlich arbeiten, um sie zu erstellen. Ich habe es tatsächlich jeden Tag laufen lassen, standardmäßig mit Enddatum = heutigem Datum. Außerdem erfordert meine Beschreibung Enddatum 1 = Startdatum 2 minus 1 Tag. - @ Robert Carnegie , am 2017-06-22 20: 56: 01Z
Die oben vorgeschlagene Methode adressiert eine Geschäftsdomäne mit den zuvor beschriebenen Merkmalen und wendet folglich Ihren Vorschlag an, die EndDate
Spalte als Tabelle zu deklarieren, die sich von einem „Feld“ unterscheidet. Dies Price
würde bedeuten, dass die logische Struktur der Datenbank dies tun würde nicht korrekt sein reflektieren des konzeptionelle Schemas und ein konzeptionelles Schema muss definiert und mit Präzision reflektiert werden, einschließlich der Differenzierung von (1) Basisinformationen aus (2) ableitbaren Informationen.
Abgesehen davon würde eine solche Vorgehensweise zu einer Vervielfältigung führen, da die EndDate
dann aufgrund (a) einer ableitbaren Tabelle und auch aufgrund (b) der genannten Basistabelle Price
mit der daher duplizierten EndDate
Spalte erhalten werden könnte. Während dies eine Möglichkeit ist, sollte ein Praktiker, wenn er sich für diesen Ansatz entscheidet, die Datenbankbenutzer entschieden vor den damit verbundenen Unannehmlichkeiten und Ineffizienzen warnen. Eine dieser Unannehmlichkeiten und Ineffizienzen ist beispielsweise die dringende Notwendigkeit, einen Mechanismus zu entwickeln, der jederzeit sicherstellt, dass jeder Price.EndDate
Wert gleich dem der Price.StartDate
Spalte der unmittelbar aufeinanderfolgenden Zeile für den Price.ProductNumber
vorliegenden Wert ist.
Im Gegensatz dazu ist die Arbeit zur Erstellung der fraglichen abgeleiteten Daten, wie ich sie dargelegt habe, ehrlich gesagt überhaupt nicht speziell und muss (i) die korrekte Entsprechung zwischen den logischen und konzeptuellen Abstraktionsebenen der Datenbank gewährleisten und (ii) ) Gewährleistung der Datenintegrität, wobei beide Aspekte, wie bereits erwähnt, ausgesprochen wichtig sind.
Wenn der Effizienzaspekt, von dem Sie sprechen, mit der Ausführungsgeschwindigkeit einiger Datenmanipulationsvorgänge zusammenhängt, muss er an der geeigneten Stelle verwaltet werden, dh auf physischer Ebene, z. B. über eine vorteilhafte Indizierungsstrategie, die auf (1) basiert ) die besonderen Abfragetendenzen und (2) die besonderen physikalischen Mechanismen, die vom DBMS verwendet werden. Andernfalls wird ein robustes System (dh ein wertvolles organisatorisches Gut) leicht zu einer nicht zuverlässigen Ressource, wenn die entsprechende konzeptionell-logische Zuordnung geopfert und die Integrität der beteiligten Daten beeinträchtigt wird.
Diskontinuierliche oder disjunkte Zeitreihen
Andererseits gibt es Umstände, unter denen das Beibehalten der EndDate
einzelnen Zeilen in einer Zeitreihentabelle nicht nur bequemer und effizienter ist, sondern auch gefordert wird , obwohl dies natürlich ausschließlich von den geschäftsumgebungsspezifischen Anforderungen abhängt. Ein Beispiel für diese Art von Umständen ergibt sich, wenn
- Sowohl die StartDate- als auch die EndDate- Informationen werden vor jeder INSERTion gespeichert (und über diese gespeichert)
- es kann Lücken in der Mitte der verschiedenen Perioden , in denen die Preise sind Strom (dh die Zeitreihe diskontinuierlich oder disjunct ).
Ich habe dieses Szenario in dem in Abbildung 2 gezeigten IDEF1X-Diagramm dargestellt .
In diesem Fall Price
muss die hypothetische Tabelle auf ähnliche Weise deklariert werden:
CREATE TABLE Price (
ProductNumber INT NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL,
Amount INT NOT NULL,
--
CONSTRAINT Price_PK PRIMARY KEY (ProductNumber, StartDate, EndDate),
CONSTRAINT Price_to_Product_FK FOREIGN KEY (ProductNumber)
REFERENCES Product (ProductNumber),
CONSTRAINT DatesOrder_CK CHECK (EndDate >= StartDate)
);
Und ja, dieses logische DDL-Design vereinfacht die Verwaltung auf physischer Ebene, da Sie eine Indexierungsstrategie erstellen können, die die EndDate
Spalte (die, wie gezeigt, in einer Basistabelle deklariert ist) in relativ einfacheren Konfigurationen umfasst.
Dann eine SELECT-Operation wie die folgende
SELECT P.ProductNumber,
P.Etcetera,
PR.Amount,
PR.StartDate,
PR.EndDate
FROM Price PR
JOIN Product P
WHERE P.ProductNumber = 1750
AND StartDate <= '20170602'
AND EndDate >= '20170602';
kann verwendet werden, um die gesamten Price
Daten für die Product
hauptsächlich bis ProductNumber
1750 am Date
2. Juni 2017 identifizierten abzuleiten .
prices
erstellen Sie eine Tabelleprices_history
mit ähnlichen Spalten. Hibernate Envers kann dies für Sie automatisieren