Если я правильно понимаю сценарий, вы должны определить таблицу, в которой сохранен временной ряд Price ; поэтому, я согласен, это во многом связано с временным аспектом базы данных, с которой вы работаете.
Бизнес правила
Давайте начнем анализировать ситуацию с концептуального уровня. Так что, если в вашей сфере бизнеса,
- продукт приобретается на один-ко-многим Цены ,
- каждая цена покупки становится текущей при точной дате начала , и
- Цена EndDate (что указывает на даты , когда цена перестает быть ток ) равен StartDate из непосредственно следующей Цена ,
тогда это означает, что
- нет никаких разрывов между отдельными периодами, в течение которых цены являются текущими (временной ряд является непрерывным или соединенным ), и
- EndDate из Цены является выводимым ИГДОМ.
IDEF1X - схема показана на рисунке 1 , хотя и очень упрощена, изображает такой сценарий:
Описательная логическая схема
И следующая схема логического уровня SQL-DDL, основанная на упомянутой диаграмме IDEF1X, иллюстрирует выполнимый подход, который вы можете адаптировать к вашим собственным точным потребностям:
-- 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)
);
Price
Таблица имеет составной первичный ключ, состоящие из двух колонок, т.е. ProductNumber
(сдержанные, в свою очередь, в качестве внешнего ключа , который делает ссылку на Product.ProductNumber
) и StartDate
(указывая конкретную Дату , в которой определенный продукт был куплен в определенной Цене ) ,
В случае, если товары приобретаются на разных Цены в тот же день , вместо StartDate
колонки, вы можете включать в себя один помеченный , как StartDateTime
что держит Instant , когда данный продукт был куплен в точном цена . ПЕРВИЧНЫЙ КЛЮЧ должен быть объявлен как (ProductNumber, StartDateTime)
.
Как показано, вышеупомянутая таблица является обычной, потому что вы можете объявить операции SELECT, INSERT, UPDATE и DELETE, чтобы напрямую манипулировать ее данными, следовательно, она (a) позволяет избежать установки дополнительных компонентов и (b) может использоваться во всех основные платформы SQL с некоторыми изменениями, если это необходимо.
Образцы манипулирования данными
В качестве примера некоторые манипуляции операции , которые кажутся полезными, давайте скажем , что вы вставили следующие данные в Product
и Price
таблицы, соответственно:
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);
Так как Price.EndDate
точка извлекаемых данных, то вы должны получить ее через точно производную таблицу, которая может быть создана как представление , чтобы создать «полный» временной ряд, как показано ниже:
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;
Затем следующая операция, которая выбирает непосредственно из этого представления
SELECT ProductNumber,
ProductEtcetera,
PriceAmount,
StartDate,
EndDate
FROM PriceWithEndDate
ORDER BY StartDate DESC;
поставляет следующий набор результатов:
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.
Теперь, давайте предположим , что вы заинтересованы в получении целые Price
данные для Product
прежде всего идентифицируются ProductNumber
1750 по Date
2 июня 2017 года . Видя, что Price
утверждение (или строка) является текущим или эффективным в течение всего интервала, который проходит от (i) его StartDate
до (ii) его EndDate
, тогда эта операция DML
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.
дает следующий набор результатов
ProductNumber ProductEtcetera PriceAmount StartDate EndDate
------------- ------------------ ----------- ---------- ----------
1750 Price time series… 1000 2017-06-01 2017-06-03
который обращается к указанному требованию.
Как показано, PriceWithEndDate
представление играет первостепенную роль в получении большей части извлекаемых данных и может быть ВЫБРАНО ИЗ довольно обычным способом.
Учитывая, что вашей платформой предпочтений является PostgreSQL, этот контент с официального сайта документации содержит информацию о «материализованных» представлениях , которые могут помочь оптимизировать скорость выполнения с помощью механизмов физического уровня, если указанный аспект становится проблематичным. Другие системы управления базами данных SQL (СУБД) предлагают физические инструменты, которые очень похожи, хотя может применяться другая терминология, например, «индексированные» представления в Microsoft SQL Server.
Вы можете увидеть обсуждаемые примеры кода DDL и DML в действии в этой скрипте db <> и в этой скрипте SQL .
Связанные ресурсы
В этом разделе « Вопросы и ответы» мы обсуждаем бизнес-контекст, который включает изменения цен на продукцию, но имеет более широкий охват, поэтому вы можете найти его интересным.
Эти сообщения о переполнении стека охватывают очень важные вопросы, касающиеся типа столбца, который содержит данные о валюте в PostgreSQL.
Ответы на комментарии
Это похоже на работу, которую я проделал, но я обнаружил, что гораздо удобнее / эффективнее работать с таблицей, в которой цена (в данном случае) имеет столбец startdate и столбец enddate - так что вы просто ищете строки с targettdate > = startdate и targettdate <= enddate. Конечно, если данные не хранятся с этими полями (включая конечную дату 31 декабря 9999 года, а не пустую, где нет фактической конечной даты), вам придется поработать над ее созданием. Я фактически заставлял это работать каждый день, с датой окончания = сегодняшняя дата по умолчанию. Кроме того, мое описание требует enddate 1 = startdate 2 минус 1 день. - @Robert Carnegie , 2017-06-22 20: 56: 01Z
Метод, который я предлагаю выше, обращается к бизнес-сфере с ранее описанными характеристиками , следовательно, применение вашего предложения об объявлении EndDate
столбца (который отличается от «поля») названной базовой таблицы Price
будет означать, что логическая структура базы данных будет не отражать концептуальную схему правильно, и концептуальная схема должна быть определена и отражена с точностью, включая дифференцирование (1) базовой информации от (2) выводимой информации.
Кроме того, такой порядок действий привел бы к дублированию, поскольку EndDate
затем его можно было бы получить с помощью (a) производной таблицы, а также с помощью (b) названной базовой таблицы Price
с таким образом дублированным EndDate
столбцом. Хотя это и возможно, если практикующий врач решит следовать указанному подходу, он или она должны решительно предупредить пользователей базы данных о неудобствах и недостатках, которые с этим связаны. Одним из таких неудобств и неэффективностей является, например, настоятельная необходимость разработки механизма, который всегда обеспечивает , чтобы каждое Price.EndDate
значение равнялось значению Price.StartDate
столбца непосредственно следующего ряда для имеющегося Price.ProductNumber
значения.
Напротив, работа по созданию рассматриваемых производных данных, как я выдвинул, честно говоря, вовсе не особенная, и она должна (i) гарантировать правильное соответствие между логическим и концептуальным уровнями абстракции базы данных и (ii) ) обеспечить целостность данных, оба аспекта, которые, как отмечалось ранее, имеют решающее значение.
Если аспект эффективности, о котором вы говорите, связан со скоростью выполнения некоторых операций с данными, то этим нужно управлять в соответствующем месте, то есть на физическом уровне, например, с помощью выгодной стратегии индексации, основанной на (1 ) конкретные тенденции запросов и (2) конкретные физические механизмы, предоставляемые СУБД использования. В противном случае, отказ от соответствующего концептуально-логического отображения и компрометация целостности данных легко превращает надежную систему (т. Е. Ценный организационный актив) в ненадежный ресурс.
Разрывные или несвязанные временные ряды
С другой стороны, существуют обстоятельства, когда сохранение EndDate
каждой строки в таблице временных рядов не только более удобно и эффективно, но и востребовано , хотя это, конечно, полностью зависит от требований конкретной бизнес-среды. Один пример такого рода обстоятельств возникает, когда
- как StartDate и EndDate часть информации проведены до (и удерживаются с помощью) каждый вставления, и
- могут быть разрывы в середине различных периодов, в течение которых цены являются текущими (т. е. временной ряд прерывистый или несвязанный ).
Я представил указанный сценарий на диаграмме IDEF1X, показанной на рисунке 2 .
В этом случае, да, гипотетическая Price
таблица должна быть объявлена способом, подобным этому:
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)
);
И да, эта логическая структура DDL упрощает администрирование на физическом уровне, поскольку вы можете создать стратегию индексирования, которая охватывает EndDate
столбец (который, как показано, объявлен в базовой таблице) в относительно более простых конфигурациях.
Затем операция SELECT, как показано ниже
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';
могут быть использованы для получения целых Price
данных для Product
главным образом идентифицированы ProductNumber
1750 на Date
2 июня 2017 года .
prices
создайте таблицуprices_history
с похожими столбцами. Hibernate Envers может автоматизировать это для вас