Nếu tôi hiểu kịch bản phù hợp, bạn nên xác định một bảng giữ lại chuỗi thời gian Giá ; do đó, tôi đồng ý, điều này có liên quan nhiều đến khía cạnh tạm thời của cơ sở dữ liệu bạn đang làm việc.
Quy tắc kinh doanh
Chúng ta hãy bắt đầu phân tích tình hình từ cấp độ khái niệm. Vì vậy, nếu , trong lĩnh vực kinh doanh của bạn,
- một sản phẩm được mua tại một-nhiều giá ,
- mỗi Giá mua trở thành Hiện tại tại một StartDate chính xác và
- các giá EndDate (mà chỉ ra ngày khi giá không còn là hiện tại ) là tương đương với StartDate của ngay lập tức tiếp theo Giá ,
vậy có nghĩa là
- không có khoảng trống giữa các biệt nguyệt trong đó giá là hiện tại (các chuỗi thời gian là liên tục hoặc liên kết ), và
- các EndDate của một giá là một cột mốc derivable.
Các IDEF1X sơ đồ thể hiện trong hình 1 , mặc dù rất đơn giản, mô tả một kịch bản như vậy:
Bố cục logic phơi bày
Và thiết kế mức logic SQL-DDL sau đây, dựa trên sơ đồ IDEF1X đã nói, minh họa một cách tiếp cận khả thi mà bạn có thể thích ứng với nhu cầu chính xác của mình:
-- 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)
);
Các Price
bảng có một PRIMARY KEY tổng hợp tạo thành từ hai cột, tức là ProductNumber
(bị hạn chế, đến lượt nó, như một KEY NƯỚC NGOÀI mà làm cho một tham chiếu đến Product.ProductNumber
) và StartDate
(chỉ ra những đặc biệt ngày , trong đó một số sản phẩm được mua tại một cụ Giá ) .
Trong trường hợp Sản phẩm được mua ở các mức giá khác nhau trong cùng một ngày , thay vì StartDate
cột, bạn có thể bao gồm một nhãn được StartDateTime
giữ nhãn tức thì khi một Sản phẩm nhất định được mua ở một mức giá chính xác . KEY PRIMARY sau đó sẽ phải được khai báo là (ProductNumber, StartDateTime)
.
Như đã trình bày, bảng đã nói ở trên là một bảng thông thường, bởi vì bạn có thể khai báo các hoạt động CHỌN, CHERTN, CẬP NHẬT và XÓA để thao tác trực tiếp dữ liệu của nó, do đó (a) cho phép tránh cài đặt các thành phần bổ sung và (b) có thể được sử dụng trong tất cả các nền tảng SQL chính với một số điều chỉnh, nếu cần thiết.
Mẫu thao tác dữ liệu
Để minh họa một số thao tác thao tác có vẻ hữu ích, hãy giả sử rằng bạn đã CHỨNG MINH dữ liệu sau trong bảng Product
và Price
bảng tương ứng:
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);
Vì đó Price.EndDate
là một điểm dữ liệu có thể tạo được, nên bạn phải lấy nó thông qua, chính xác, một bảng dẫn xuất có thể được tạo như một khung nhìn để tạo ra chuỗi thời gian đầy đủ của Rô-lô, như được minh họa dưới đây:
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;
Sau đó, thao tác sau đây CHỌN trực tiếp từ chế độ xem đó
SELECT ProductNumber,
ProductEtcetera,
PriceAmount,
StartDate,
EndDate
FROM PriceWithEndDate
ORDER BY StartDate DESC;
cung cấp tập kết quả tiếp theo:
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.
Bây giờ, chúng tôi giả định rằng bạn quan tâm đến việc lấy toàn bộ Price
dữ liệu cho lần Product
đầu tiên được xác định vào năm ProductNumber
1750 vào ngày Date
2 tháng 6 năm 2017 . Thấy rằng một Price
xác nhận (hoặc hàng) là hiện tại hoặc có hiệu lực trong toàn bộ Khoảng thời gian chạy từ (i) StartDate
đến (ii) EndDate
, sau đó hoạt động DML này
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.
mang lại tập kết quả theo sau
ProductNumber ProductEtcetera PriceAmount StartDate EndDate
------------- ------------------ ----------- ---------- ----------
1750 Price time series… 1000 2017-06-01 2017-06-03
Địa chỉ nào yêu cầu.
Như được hiển thị, PriceWithEndDate
chế độ xem đóng vai trò tối quan trọng trong việc thu được hầu hết các dữ liệu có thể lấy được và có thể được CHỌN TỪ theo cách khá bình thường.
Có tính đến việc nền tảng ưu tiên của bạn là PostgreSQL, nội dung này từ trang tài liệu chính thức chứa thông tin về các chế độ xem được vật chất hóa , có thể giúp tối ưu hóa tốc độ thực thi bằng các cơ chế mức vật lý, nếu khía cạnh nói trở nên có vấn đề. Các hệ thống quản lý cơ sở dữ liệu SQL (DBMS) khác cung cấp các công cụ vật lý rất giống nhau, mặc dù các thuật ngữ khác nhau có thể được áp dụng, ví dụ, các khung nhìn được lập chỉ mục của Drake trong Microsoft SQL Server.
Bạn có thể thấy các mẫu mã DDL và DML được thảo luận đang hoạt động trong db <> fiddle này và trong Fiddle SQL này .
Tài nguyên liên quan
Trong phần hỏi đáp này, chúng tôi thảo luận về bối cảnh kinh doanh bao gồm những thay đổi của Giá sản phẩm nhưng có phạm vi rộng hơn, vì vậy bạn có thể thấy nó đáng quan tâm.
Các bài đăng Stack Overflow này bao gồm các điểm rất phù hợp liên quan đến loại cột chứa dữ liệu tiền tệ trong PostgreQuery.
Phản hồi ý kiến
Điều này trông tương tự như công việc tôi đã làm, nhưng tôi thấy làm việc thuận tiện / hiệu quả hơn nhiều với bảng trong đó giá (trong trường hợp này) có cột bắt đầu và cột kết thúc - vì vậy bạn chỉ tìm kiếm các hàng có mục tiêu > = startdate và targetdate <= enddate. Tất nhiên, nếu dữ liệu không được lưu trữ với các trường đó (bao gồm cả ngày kết thúc vào ngày 31 tháng 12 năm 9999, không phải là Null, nơi không có ngày kết thúc thực sự tồn tại), thì bạn phải làm việc để sản xuất nó. Tôi thực sự đã làm cho nó chạy mỗi ngày, với ngày kết thúc = ngày hôm nay theo mặc định. Ngoài ra, mô tả của tôi yêu cầu enddate 1 = startdate 2 trừ 1 ngày. - @Robert Carnegie , vào ngày 2017-06-22 20: 56: 01Z
Phương pháp tôi đề xuất ở trên giải quyết một lĩnh vực kinh doanh có các đặc điểm được mô tả trước đây , do đó áp dụng đề xuất của bạn về việc khai EndDate
báo cột như là một cột khác với trường lĩnh vực - - của bảng cơ sở có tên Price
ngụ ý rằng cấu trúc logic của cơ sở dữ liệu sẽ không được phản ánh chính xác lược đồ khái niệm và lược đồ khái niệm phải được xác định và phản ánh với độ chính xác, bao gồm phân biệt (1) thông tin cơ sở từ (2) thông tin có thể dẫn xuất .
Ngoài ra, một quá trình hành động như vậy sẽ giới thiệu sự trùng lặp, vì EndDate
sau đó có thể có được nhờ (a) một bảng có thể dẫn xuất và cũng nhờ (b) bảng cơ sở được đặt tên Price
, với EndDate
cột được sao chép . Mặc dù đó là một khả năng, nếu một học viên quyết định làm theo phương pháp đã nói, anh ta hoặc cô ta nên quyết định cảnh báo người dùng cơ sở dữ liệu về những bất tiện và không hiệu quả mà nó liên quan. Một trong những bất tiện và không hiệu quả đó là, ví dụ, nhu cầu cấp thiết là phải phát triển một cơ chế đảm bảo, mọi lúc , mỗi Price.EndDate
giá trị đều bằng với Price.StartDate
cột của hàng liên tiếp ngay lập tức cho Price.ProductNumber
giá trị trong tay.
Ngược lại, công việc tạo ra dữ liệu dẫn xuất theo yêu cầu mà tôi đưa ra là, trung thực, không đặc biệt, và được yêu cầu (i) đảm bảo sự tương ứng chính xác giữa mức độ trừu tượng logic và khái niệm của cơ sở dữ liệu và (ii ) đảm bảo tính toàn vẹn dữ liệu, cả hai khía cạnh như đã lưu ý trước đây đều có tầm quan trọng lớn.
Nếu khía cạnh hiệu quả mà bạn đang nói đến có liên quan đến tốc độ thực hiện của một số thao tác thao tác dữ liệu, thì nó phải được quản lý ở nơi thích hợp, ví dụ, ở cấp độ vật lý, ví dụ: chiến lược lập chỉ mục có lợi, dựa trên (1 ) các xu hướng truy vấn cụ thể và (2) các cơ chế vật lý cụ thể được cung cấp bởi DBMS sử dụng. Mặt khác, hy sinh ánh xạ logic theo khái niệm phù hợp và làm tổn hại tính toàn vẹn của dữ liệu liên quan dễ dàng biến một hệ thống mạnh mẽ (nghĩa là một tài sản tổ chức có giá trị) thành một tài nguyên không đáng tin cậy.
Chuỗi thời gian không liên tục hoặc không liên tục
Mặt khác, có những trường hợp giữ lại EndDate
mỗi hàng trong bảng chuỗi thời gian không chỉ thuận tiện và hiệu quả hơn mà còn đòi hỏi , mặc dù điều đó hoàn toàn phụ thuộc vào các yêu cầu cụ thể của môi trường kinh doanh. Một ví dụ về loại tình huống đó xảy ra khi
- cả hai phần thông tin StartDate và EndDate đều được giữ trước (và được giữ lại thông qua) mỗi INSERTion và
- có thể có những khoảng trống ở giữa các Thời kỳ riêng biệt trong đó Giá là hiện tại (nghĩa là chuỗi thời gian không liên tục hoặc không liên tục ).
Tôi đã trình bày kịch bản đã nói trong sơ đồ IDEF1X được hiển thị trong Hình 2 .
Trong trường hợp đó, có, Price
bảng giả thuyết phải được khai báo theo cách tương tự như sau:
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)
);
Và, vâng, thiết kế DDL logic đó đơn giản hóa việc quản trị ở cấp độ vật lý, bởi vì bạn có thể đưa ra một chiến lược lập chỉ mục bao gồm EndDate
cột (như được hiển thị trong bảng cơ sở) trong các cấu hình tương đối dễ dàng hơn .
Sau đó, một thao tác CHỌN như hoạt động dưới đây
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';
có thể được sử dụng để lấy toàn bộ Price
dữ liệu cho năm 1750Product
được xác định chủ yếu vào ngày 2 tháng 6 năm 2017 .ProductNumber
Date
prices
tạo một bảngprices_history
với các cột tương tự. Hibernate Envers có thể tự động hóa việc này cho bạn