2010-08-20 40 views
15

Lời nói đầu: Tôi đã suy nghĩ về một cấu trúc cơ sở dữ liệu mới cho một ứng dụng mới và nhận ra rằng chúng tôi cần một cách để lưu trữ dữ liệu lịch sử một cách hiệu quả. Tôi muốn một người khác xem xét và xem liệu có bất kỳ vấn đề nào với cấu trúc này không. Tôi nhận ra rằng phương pháp lưu trữ dữ liệu này rất có thể đã được phát minh trước đây (tôi gần như chắc chắn nó có) nhưng tôi không biết nó có tên và một số tìm kiếm trên google mà tôi đã thử không mang lại gì hay không.Cấu trúc cơ sở dữ liệu để lưu trữ dữ liệu lịch sử

Sự cố: Giả sử bạn có bảng cho đơn đặt hàng và đơn đặt hàng có liên quan đến bảng khách hàng cho khách hàng đã đặt hàng. Trong một cấu trúc cơ sở dữ liệu thông thường bạn có thể mong đợi một cái gì đó như thế này:

orders 
------ 
orderID 
customerID 


customers 
--------- 
customerID 
address 
address2 
city 
state 
zip 

Khá đơn giản, OrderID có một chìa khóa nước ngoài của ID khách hàng đó là khóa chính của bảng khách hàng. Nhưng nếu chúng tôi đi và chạy báo cáo trên bảng thứ tự, chúng tôi sẽ tham gia bảng khách hàng vào bảng đơn đặt hàng, sẽ mang lại bản ghi hiện tại cho ID khách hàng đó. Điều gì sẽ xảy ra nếu khi đơn đặt hàng được đặt, địa chỉ của khách hàng là khác nhau và nó đã được thay đổi sau đó. Bây giờ đơn đặt hàng của chúng tôi không còn phản ánh lịch sử của địa chỉ khách hàng đó, vào thời điểm đặt hàng. Về cơ bản, bằng cách thay đổi hồ sơ khách hàng, chúng tôi chỉ thay đổi tất cả lịch sử cho khách hàng đó.

Bây giờ có một số cách để giải quyết vấn đề này, một trong số đó sẽ là sao chép bản ghi khi đơn đặt hàng được tạo. Những gì tôi đã đưa ra mặc dù là những gì tôi nghĩ rằng sẽ là một cách dễ dàng hơn để làm điều này có lẽ là một chút thanh lịch hơn, và có thêm tiền thưởng của đăng nhập bất cứ lúc nào một sự thay đổi được thực hiện.

gì nếu tôi đã làm một cấu trúc như thế này thay vì:

orders 
------ 
orderID 
customerID 
customerHistoryID 


customers 
--------- 
customerID 
customerHistoryID 


customerHistory 
-------- 
customerHistoryID 
customerID 
address 
address2 
city 
state 
zip 
updatedBy 
updatedOn 

xin vui lòng tha thứ cho định dạng, nhưng tôi nghĩ rằng bạn sẽ nhìn thấy ý tưởng. Về cơ bản, ý tưởng là bất cứ khi nào một khách hàng được thay đổi, chèn hoặc cập nhật, customerHistoryID được tăng lên và bảng khách hàng được cập nhật với customerHistoryID mới nhất. Bảng thứ tự bây giờ không chỉ trỏ đến customerID (cho phép bạn xem tất cả các bản sửa đổi của bản ghi khách hàng), mà còn cho customerHistoryID, nó trỏ đến một bản sửa đổi cụ thể của bản ghi. Bây giờ thứ tự phản ánh trạng thái của dữ liệu tại thời điểm thứ tự được tạo ra.

Bằng cách thêm cột cập nhật và cập nhật vào bảng customerHistory, bạn cũng có thể thấy "nhật ký kiểm tra" của dữ liệu, để bạn có thể xem ai đã thực hiện thay đổi và thời điểm.

Một nhược điểm tiềm năng có thể bị xóa, nhưng tôi không thực sự lo lắng về điều đó vì nhu cầu này vì không có gì bị xóa. Nhưng thậm chí vẫn còn, hiệu ứng tương tự có thể đạt được bằng cách sử dụng một activeFlag hoặc một cái gì đó như nó phụ thuộc vào tên miền của dữ liệu.

Suy nghĩ của tôi là tất cả các bảng sẽ sử dụng cấu trúc này. Dữ liệu lịch sử bất cứ lúc nào đang được truy lục, nó sẽ được nối với bảng lịch sử bằng cách sử dụng customerHistoryID để hiển thị trạng thái dữ liệu cho thứ tự cụ thể đó.

Lấy danh sách khách hàng thật dễ dàng, chỉ cần tham gia vào bảng khách hàng trên customerHistoryID.

Mọi người có thể thấy bất kỳ vấn đề nào với phương pháp này, hoặc từ quan điểm thiết kế hoặc lý do hiệu suất tại sao điều này là xấu. Hãy nhớ rằng, không có vấn đề gì tôi cần phải đảm bảo rằng các dữ liệu lịch sử được bảo quản để cập nhật tiếp theo cho hồ sơ không thay đổi lịch sử. Có cách nào tốt hơn? Đây có phải là ý tưởng đã biết có tên hoặc bất kỳ tài liệu nào về nó không?

Cảm ơn bạn đã được trợ giúp.

Cập nhật: Đây là một ví dụ rất đơn giản về những gì tôi thực sự sẽ có. Ứng dụng thực sự của tôi sẽ có "đơn đặt hàng" với một số phím nước ngoài để bàn khác. Thông tin vị trí gốc/đích, thông tin khách hàng, thông tin cơ sở, thông tin người dùng, v.v. Đã được đề xuất một vài lần tôi có thể sao chép thông tin vào hồ sơ đặt hàng tại thời điểm đó và tôi đã thấy nó được thực hiện theo cách này nhiều lần, nhưng điều này sẽ dẫn đến một kỷ lục với hàng trăm cột, mà thực sự là không khả thi trong trường hợp này.

+0

Vì vậy, về cơ bản những gì bạn đang nói là: "Tôi có quá nhiều cột trong bảng thứ tự Vì vậy, tôi muốn. để đặt địa chỉ thứ tự trong bảng khách hàng. Để hỗ trợ điều này, tôi muốn thỏa hiệp dữ liệu khách hàng với lược đồ theo dõi lịch sử phức tạp. " Âm thanh như một ý tưởng tồi với tôi. –

+1

Không ... không hề. Điều tôi đang nói là tôi cần có khả năng theo dõi địa chỉ, khi họ thay đổi, và có thể liên kết một đơn đặt hàng với một tiểu bang cụ thể (bản sửa đổi) của một địa chỉ. Đơn đặt hàng có thể không phải là bảng duy nhất có liên quan đến địa chỉ, chưa kể chúng tôi muốn biết khi nào và ai đã thay đổi địa chỉ. –

+0

BTW không bao giờ giả định sẽ không bao giờ bị xóa. Lập kế hoạch xóa sẽ vô tình xảy ra hoặc tạo ra một kích hoạt mà sẽ không cho phép xóa. – HLGEM

Trả lời

10

Khi tôi gặp phải sự cố như vậy, một giải pháp thay thế là đặt thứ tự bảng lịch sử. Các chức năng của nó giống nhau nhưng dễ dàng hơn một chút để theo dõi

orders 
------ 
orderID 
customerID 
address 
City 
state 
zip 



customers 
--------- 
customerID 
address 
City 
state 
zip 

CHỈNH SỬA: nếu số lượng cột cao theo ý thích của bạn, bạn có thể tách nó ra theo ý muốn.

Nếu bạn thực hiện tùy chọn khác và sử dụng bảng lịch sử, bạn nên cân nhắc sử dụng dữ liệu bitemporal vì bạn có thể phải xử lý khả năng dữ liệu lịch sử cần được sửa. Ví dụ: Khách hàng đã thay đổi địa chỉ hiện tại của mình Từ A thành B nhưng bạn cũng phải sửa địa chỉ trên đơn đặt hàng hiện tại hiện đang được thực hiện.

Ngoài ra nếu bạn đang sử dụng MS SQL Server, bạn có thể muốn xem xét sử dụng chế độ xem được lập chỉ mục. Điều đó sẽ cho phép bạn giao dịch một sự gia tăng nhỏ/cập nhật hoàn hảo giảm cho một sự lựa chọn lớn tăng perf. Nếu bạn không sử dụng máy chủ MS SQL, bạn có thể tái tạo điều này bằng cách sử dụng trình kích hoạt và bảng.

+1

vâng, tôi đã nhìn thấy nó được thực hiện theo cách này trước khi là tốt. Nhưng đây là một ví dụ rất nhỏ, trong ứng dụng thực tế tôi đang nghĩ về điều này, một "thứ tự" sẽ có một loạt các khóa ngoại với rất nhiều dữ liệu trong các bảng khác. Cuối cùng tôi sẽ có một bản ghi "thứ tự" với hàng trăm cột. –

+6

Đây là cách tiếp cận chính xác, bởi vì địa chỉ là một chức năng của trật tự, không phải là khách hàng, tại thời điểm đặt hàng. Nếu bạn muốn đơn giản hóa bảng thứ tự, tôi đề xuất một khóa ngoại cho một bảng địa chỉ.Trên thực tế, khách hàng và đơn hàng có thể lưu trữ địa chỉ của họ trong cùng một bảng địa chỉ mà không gặp khó khăn gì. Điều này cũng sẽ giúp bạn dễ dàng bao gồm các địa chỉ giao hàng và thanh toán riêng lẻ, v.v. –

+1

@Jeffrey L Whitledge đang tạo một điểm cực kỳ quan trọng ở đây, địa chỉ, tên khách hàng, giá vv hiện là chức năng của đơn hàng không phải là khách hàng hoặc bảng giá, đó là lý do tại sao không có giải pháp tốt khác ngoại trừ việc đưa chúng vào các bảng liên quan đến thứ tự. – HLGEM

4

Thông thường đơn đặt hàng chỉ lưu trữ thông tin vì nó là tại thời điểm đặt hàng. Điều này đặc biệt đúng với những thứ như số bộ phận, tên bộ phận và giá cả cũng như địa chỉ và tên khách hàng. Sau đó, bạn không phải tham gia vào 5 hoặc sáu bảng để có được thông tin có thể được lưu trữ trong một. Đây không phải là không chuẩn hóa vì bạn thực sự cần phải có sự bất biến như nó tồn tại tại thời điểm của lệnh. Tôi nghĩ là ít có khả năng có thông tin này theo thứ tự và chi tiết đơn đặt hàng (lưu trữ các mục riêng lẻ được đặt hàng) ít rủi ro hơn về thay đổi ngẫu nhiên đối với dữ liệu.

Bảng đặt hàng của bạn sẽ không có hàng trăm cột. Bạn sẽ có một bảng thứ tự và một bảng chi tiết đơn đặt hàng do một đến nhiều mối quan hệ. Bảng đặt hàng sẽ bao gồm thứ tự không. id khách hàng 9so bạn có thể tìm kiếm mọi thứ mà khách hàng này đã từng đặt hàng ngay cả khi tên đã thay đổi), tên khách hàng, địa chỉ khách hàng (lưu ý bạn không cần zip thành phố, đặt địa chỉ trong một trường), ngày đặt hàng và có thể là vài trường khác liên quan trực tiếp đến thứ tự ở cấp cao nhất. Sau đó, bạn có bảng chi tiết đơn hàng có số thứ tự, detail_id, số phần, mô tả một phần (điều này có thể là tổng hợp một loạt các trường như kích thước, màu sắc vv hoặc bạn có thể tách ra phổ biến nhất), Không có mục nào, loại đơn vị, giá mỗi đơn vị, thuế, tổng giá, ngày giao hàng, trạng thái. Bạn đặt một mục nhập cho mỗi mục được đặt hàng.

+0

Tôi thấy những gì bạn đang nói, nhưng như tôi đã nói về câu trả lời của Conrad Frix, cuối cùng, bảng "thứ tự" của tôi sẽ có hàng trăm cột, điều này thực sự không khả thi. Tôi có lẽ nên thêm điều này vào câu hỏi. –

0

Bản thân tôi muốn giữ nó đơn giản. Tôi sẽ sử dụng hai bảng, một bảng khách hàng và một bảng lịch sử khách hàng. Nếu bạn có khóa (ví dụ: customerId) trong bảng lịch sử không có lý do gì để tạo một bảng nối, một lựa chọn trên khóa đó sẽ cung cấp cho bạn tất cả các bản ghi.

Bạn cũng không có thông tin kiểm tra (ví dụ: ngày sửa đổi, người sửa đổi vv) trong bảng lịch sử khi bạn hiển thị nó, tôi hy vọng bạn muốn điều này.

Vì vậy, tôi sẽ giống như thế này:

CustomerTable (this contains current customer information) 
CustID (distinct non null) 
...all customer information fields 

CustomerHistoryTable 
CustId (not distinct non null) 
...all customer information fields 
DateOfChange 
WhoChanged 

Trường DataOfChagne là ngày bảng customer đã thay đổi (từ các giá trị trong hồ sơ này) để các giá trị trong một kỷ lục gần đây hơn của các giá trị trong CustomerTable

Bạn yêu cầu bảng chỉ cần một CustomerID nếu bạn cần tìm thông tin khách hàng tại thời điểm đặt hàng đơn giản.

+0

Tôi không biết rằng điều này có bất kỳ lợi thế nào so với lược đồ được đề xuất của OP - và theo một số cách, điều này hơi khó xử hơn (nếu bạn muốn tìm thông tin khách hàng lịch sử.) phức tạp hơn một chút) - nói rằng, tôi cũng đã sử dụng cách tiếp cận này trước đây, và đã thấy nó được sử dụng bởi một số lập trình viên khác. –

+0

Bạn nói đúng, tôi muốn ngày sửa đổi, người đã sửa đổi. (Tôi đã đề cập đến nó bằng văn bản, nhưng không phải trong bảng, tôi sẽ chỉnh sửa để làm rõ hơn). Tôi thực sự đã thiết kế chính xác theo cách này ngay từ đầu, nhưng nhận ra nó thực sự là một sự sao chép không đáng kể của dữ liệu. Với một phép nối đơn giản (mà nên rất nhanh với các chỉ mục thích hợp), tôi có thể làm điều tương tự và lưu lại phải viết tất cả thông tin đó hai lần mỗi lần. Nhưng lúc đầu tôi cũng có suy nghĩ tương tự. –

+0

Tham chiếu đơn đặt hàng là gì? ID khách hàng? Nếu vậy việc thay đổi địa chỉ khách hàng sẽ tự động ảnh hưởng đến thông tin đơn hàng? –

0

Điều bạn muốn được gọi là datawarehouse. Kể từ khi datawarehouses là OLAP và không OLTP, nó được khuyến khích để có càng nhiều cột như bạn cần để đạt được mục tiêu của bạn. Trong trường hợp của bạn, bảng orders trong datawarehouse sẽ có 11 trường như có 'ảnh chụp nhanh' các đơn hàng khi chúng đến, bất kể người dùng cập nhật tài khoản.

Wiley -The Data Warehouse Toolkit, Second Edition 

Đó là một khởi đầu tốt.

+0

Datawarehouses/Datamarts không nhất thiết phải là OLAP. Datawarehouses và datamarts có thể là nguồn của OLAP Cubes nhưng bạn có thể có một datawarehouse và không có OLAP. – jasonco

+0

@jasonco Datawarehouses nguồn cấp dữ liệu nói chung là OLTP, đó là đúng nhưng đó là nó. Tuy nhiên, Datawarehouses không tính toán và để làm điều đó họ cần phải quên đi bình thường hóa do đó có bảng khổng lồ cũng như thời gian đáp ứng, đó là OK vì chúng là OLAP chứ không phải OLTP. @OP cần một Datawarehouse, theo mô tả của ông về vấn đề này, không chỉ là một DB khác. – Ben

5

Khi bạn đang thiết kế cấu trúc dữ liệu của mình, hãy rất cẩn thận để lưu trữ các mối quan hệ chính xác, chứ không phải điều gì đó tương tự với các mối quan hệ chính xác. Nếu địa chỉ cho một đơn đặt hàng cần được duy trì, thì đó là vì địa chỉ là một phần của đơn đặt hàng, chứ không phải là khách hàng. Ngoài ra, đơn giá là một phần của trật tự, không phải là sản phẩm vv

Hãy thử một sự sắp xếp như thế này:

Customer 
-------- 
CustomerId (PK) 
Name 
AddressId (FK) 
PhoneNumber 
Email 

Order 
----- 
OrderId (PK) 
CustomerId (FK) 
ShippingAddressId (FK) 
BillingAddressId (FK) 
TotalAmount 

Address 
------- 
AddressId (PK) 
AddressLine1 
AddressLine2 
City 
Region 
Country 
PostalCode 

OrderLineItem 
------------- 
OrderId (PK) (FK) 
OrderItemSequence (PK) 
ProductId (FK) 
UnitPrice 
Quantity 

Product 
------- 
ProductId (PK) 
Price 

etc. 

Nếu bạn thực sự cần phải lưu trữ lịch sử cho một cái gì đó, giống như thay đổi theo dõi một theo thời gian, sau đó bạn nên làm điều đó với một bảng đăng nhập hoặc kiểm toán, không phải với bảng giao dịch của bạn.

+0

các bảng tôi đã sử dụng chỉ để minh họa. Chúng tôi thực sự sẽ làm chính xác những gì bạn mô tả, tách địa chỉ của khách hàng (chúng tôi gọi đó là địa điểm). Câu hỏi của tôi chỉ là về lược đồ lưu trữ dữ liệu mà thôi. –

+0

Câu trả lời này không giúp được câu hỏi gốc. Anh ấy muốn giúp đỡ với thời điểm, giải pháp này tiếp tục sử dụng một bảng địa chỉ và nếu một địa chỉ được cập nhật, nó sẽ cập nhật bất kỳ thứ tự nào cũng như bạn có mối quan hệ khóa ngoại. – maguy

+0

@maguy - Không rõ câu trả lời của tôi, nhưng ý định của tôi là dữ liệu địa chỉ sẽ không bao giờ được cập nhật. Thay vào đó, nếu địa chỉ của khách hàng thay đổi thì địa chỉ mới sẽ được chèn và khách hàng sẽ nhận được ID địa chỉ mới, để lại địa chỉ đơn đặt hàng hiện tại không thay đổi. Tương tự, nếu địa chỉ trên đơn đặt hàng cần thay đổi. –

0

Hệ thống tính lương của chúng tôi sử dụng ngày hiệu lực trong nhiều bảng. Bảng ADDRESSES được khóa trên EMPLID và EFFDT. Điều này cho phép chúng tôi theo dõi mỗi khi địa chỉ của nhân viên thay đổi. Bạn có thể sử dụng cùng một logic để theo dõi các địa chỉ lịch sử cho khách hàng. Truy vấn của bạn sẽ chỉ cần bao gồm mệnh đề so sánh ngày đặt hàng với ngày địa chỉ của khách hàng có hiệu lực tại thời điểm đặt hàng. Ví dụ:

select o.orderID, c.customerID, c.address, c.city, c.state, c.zip 
from orders o, customers c 
where c.customerID = o.customerID 
and c.effdt = (
    select max(c1.effdt) from customers c1 
    where c1.customerID = c.customerID and c1.effdt <= o.orderdt 
) 

Mục tiêu là chọn hàng gần đây nhất trong khách hàng có ngày hiệu lực vào hoặc trước ngày đặt hàng. Chiến lược tương tự này có thể được sử dụng để giữ thông tin lịch sử về giá sản phẩm.

0

Nếu bạn thực sự quan tâm đến những vấn đề như vậy, tôi chỉ có thể đề nghị bạn xem xét nghiêm túc "Dữ liệu tạm thời và Mô hình quan hệ".

Cảnh báo1: không có SQL trong đó và hầu như bất kỳ điều gì bạn cho rằng bạn biết về mô hình quan hệ sẽ bị tuyên bố là sai. Với lý do chính đáng.

Cảnh báo2: bạn sẽ nghĩ và suy nghĩ kỹ.

Cảnh báo3: cuốn sách là về giải pháp cho gia đình cụ thể của vấn đề này nên trông như thế nào, nhưng khi phần giới thiệu cho biết, nó không phải về bất kỳ công nghệ nào hiện có.

Điều đó nói rằng, cuốn sách là sự khai sáng chân chính. Ít nhất, nó giúp giải thích rõ ràng rằng giải pháp cho các vấn đề như vậy sẽ không được tìm thấy trong SQl như nó đứng ngày hôm nay, hoặc trong ORM như những người đứng ngày hôm nay, cho vấn đề đó.

2

Như tôi đã trả lời trước khi cho a similar question:

tôi đã tìm thấy rằng những gì dường như là một bước đầu âm thanh "bậc thầy của khách hàng" thiết kế thường sau phá vỡ bởi vì: trước điều trị một doanh nghiệp là khách hàng phát triển thành đối xử với người lao động cá nhân như khách hàng hoặc khách hàng sẽ thay đổi/tách/hợp nhất địa chỉ hoặc doanh nghiệp thay đổi tên nhưng chúng tôi vẫn muốn hợp nhất tổng hiệu suất cũ và mới hoặc địa chỉ giao hàng và địa chỉ thanh toán hiện phải được mở rộng để bao gồm địa chỉ hỗ trợ, hoặc nhà khai thác quên hoặc nhầm lẫn một mục đích địa chỉ cho mục đích địa chỉ khác hoặc khách hàng chỉ muốn sử dụng địa chỉ giao hàng đặc biệt tạm thời hoặc v.v.

Kết quả là, tôi đã từ bỏ ý tưởng của một tập tin khách hàng tổng thể hoàn toàn. Thay vào đó, thông tin tên/công ty/địa chỉ không bao giờ là bản ghi chính (ngoại trừ các ứng dụng như thanh toán tiện ích hoặc thuế bất động sản không thể chỉnh sửa địa chỉ cụ thể vào địa chỉ khác), chúng chỉ là các trường biểu thị địa chỉ liên hệ đang sử dụng AT A PARTICULAR POINT IN TIME, thường bên trong một thứ gì đó giống như hồ sơ bán hàng. Mỗi đơn đặt hàng được liên kết với đơn đặt hàng trước đó và tiếp theo cho khách hàng đó, ngay cả khi khách hàng thay đổi tên hoặc địa chỉ của họ. Ưu điểm là tất cả các đơn đặt hàng có thể được tổng hợp/tổng cộng/phân tích trong toàn bộ lịch sử giao dịch của khách hàng, mặc dù mỗi đơn hàng có thể thay đổi tên hoặc địa chỉ liên hệ. Nó có phần phản trực giác, đặc biệt là khi cố gắng làm hài lòng các nhà thiết kế db, nhưng nó kết thúc rất linh hoạt và thuận tiện.

Ví dụ: khi khách hàng X đặt hàng đầu tiên, không có bản ghi khách hàng nào được tạo. Thay vào đó, một bản ghi đơn đặt hàng được tạo ra có chứa thông tin tên/công ty/địa chỉ cần thiết có hiệu lực tại thời điểm đặt hàng. Khi khách hàng X đặt hàng thứ hai của mình, chúng tôi không tìm kiếm tệp khách hàng, chúng tôi tìm kiếm tệp đơn đặt hàng, sau đó sao chép/chuỗi nó để tạo đơn đặt hàng thứ hai của mình. Nếu anh ta muốn thay đổi tên/thông tin về công ty/địa chỉ của mình, thì chúng tôi chỉ chỉnh sửa các trường đó theo thứ tự bán hàng số 2 và thứ tự bán hàng số 1 không thay đổi. Bây giờ anh ấy có thể định vị theo một trong hai biến thể (thứ tự 1 hoặc 2).

Đối với những cân nhắc khác khi cố gắng để quyết định xem hai hồ sơ khách hàng đang thực sự giống nhau, thấy http://semaphorecorp.com/mpdd/mpdd.html

Các vấn đề liên quan