Thiết kế cơ sở dữ liệu cho sửa đổi?


125

Chúng tôi có một yêu cầu trong dự án để lưu trữ tất cả các sửa đổi (Lịch sử thay đổi) cho các thực thể trong cơ sở dữ liệu. Hiện tại chúng tôi có 2 đề xuất được thiết kế cho việc này:

ví dụ: Thực thể "Nhân viên"

Thiết kế 1:

-- Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

-- Holds the Employee Revisions in Xml. The RevisionXML will contain
-- all data of that particular EmployeeId
"EmployeeHistories (EmployeeId, DateModified, RevisionXML)"

Thiết kế 2:

-- Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

-- In this approach we have basically duplicated all the fields on Employees 
-- in the EmployeeHistories and storing the revision data.
"EmployeeHistories (EmployeeId, RevisionId, DateModified, FirstName, 
      LastName, DepartmentId, .., ..)"

Có cách nào khác để làm điều này?

Vấn đề với "Thiết kế 1" là chúng tôi phải phân tích cú pháp XML mỗi khi bạn cần truy cập dữ liệu. Điều này sẽ làm chậm quá trình và cũng thêm một số hạn chế như chúng ta không thể thêm các phép nối trên các trường dữ liệu sửa đổi.

Và vấn đề với "Thiết kế 2" là chúng tôi phải sao chép từng trường trên tất cả các thực thể (Chúng tôi có khoảng 70-80 thực thể mà chúng tôi muốn duy trì sửa đổi).



1
FYI: Chỉ trong trường hợp nó có thể giúp. Máy chủ SQL 2008 trở lên có công nghệ hiển thị lịch sử của các thay đổi trên bảng..visit Simple-talk.com/sql/learn-sql-server/ trộm để biết thêm và tôi chắc chắn DB như Oracle cũng sẽ có một cái gì đó như thế này.
Durai Amuthan.H

Lưu ý rằng một số cột có thể lưu trữ XML hoặc JSON. Nếu bây giờ không phải như vậy thì nó có thể xảy ra trong tương lai. Tốt hơn là đảm bảo bạn không cần lồng dữ liệu này vào dữ liệu khác.
jakubiszon

Câu trả lời:


38
  1. Đừng không đặt nó tất cả trong một bảng với một thuộc tính phân biệt IsCurrent. Điều này chỉ gây ra vấn đề xuống dòng, yêu cầu khóa thay thế và tất cả các loại vấn đề khác.
  2. Thiết kế 2 không có vấn đề với thay đổi lược đồ. Nếu bạn thay đổi bảng Nhân viên, bạn phải thay đổi bảng EmployeeHistories và tất cả các sprocs liên quan đi kèm với nó. Có khả năng nhân đôi nỗ lực thay đổi lược đồ của bạn.
  3. Thiết kế 1 hoạt động tốt và nếu được thực hiện đúng cách sẽ không tốn nhiều chi phí về hiệu suất. Bạn có thể sử dụng lược đồ xml và thậm chí các chỉ mục để khắc phục các sự cố hiệu suất có thể xảy ra. Nhận xét của bạn về phân tích xml là hợp lệ nhưng bạn có thể dễ dàng tạo chế độ xem bằng xquery - mà bạn có thể đưa vào các truy vấn và tham gia. Một cái gì đó như thế này ...
CREATE VIEW EmployeeHistory
AS
, FirstName, , DepartmentId

SELECT EmployeeId, RevisionXML.value('(/employee/FirstName)[1]', 'varchar(50)') AS FirstName,

  RevisionXML.value('(/employee/LastName)[1]', 'varchar(100)') AS LastName,

  RevisionXML.value('(/employee/DepartmentId)[1]', 'integer') AS DepartmentId,

FROM EmployeeHistories 

25
Tại sao bạn nói không lưu trữ tất cả trong một bảng với trình kích hoạt IsCản. Bạn có thể chỉ cho tôi một số ví dụ mà điều này sẽ trở thành vấn đề.
Nathan W

@Simon Munro Còn khóa chính hay khóa cụm thì sao? Chìa khóa nào chúng ta có thể thêm vào bảng lịch sử Thiết kế 1 để giúp tìm kiếm nhanh hơn?
gotqn

Tôi giả sử một SELECT * FROM EmployeeHistory WHERE LastName = 'Doe'kết quả đơn giản trong một lần quét toàn bộ bảng . Không phải là ý tưởng tốt nhất để mở rộng một ứng dụng.
Kaii

54

Tôi nghĩ câu hỏi quan trọng cần đặt ra ở đây là 'Ai / Điều gì sẽ sử dụng lịch sử'?

Nếu nó chủ yếu dành cho báo cáo / lịch sử có thể đọc được của con người, thì chúng tôi đã thực hiện kế hoạch này trong quá khứ ...

Tạo một bảng có tên 'AuditTrail' hoặc thứ gì đó có các trường sau ...

[ID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [int] NULL,
[EventDate] [datetime] NOT NULL,
[TableName] [varchar](50) NOT NULL,
[RecordID] [varchar](20) NOT NULL,
[FieldName] [varchar](50) NULL,
[OldValue] [varchar](5000) NULL,
[NewValue] [varchar](5000) NULL

Sau đó, bạn có thể thêm cột 'LastUpdatedByUserID' vào tất cả các bảng sẽ được đặt mỗi khi bạn thực hiện cập nhật / chèn trên bảng.

Sau đó, bạn có thể thêm trình kích hoạt vào mỗi bảng để bắt bất kỳ thao tác chèn / cập nhật nào xảy ra và tạo một mục trong bảng này cho từng trường đã thay đổi. Vì bảng cũng được cung cấp 'LastUpdateByUserID' cho mỗi lần cập nhật / chèn, bạn có thể truy cập giá trị này trong trình kích hoạt và sử dụng nó khi thêm vào bảng kiểm toán.

Chúng tôi sử dụng trường RecordID để lưu trữ giá trị của trường khóa của bảng đang được cập nhật. Nếu đó là một khóa kết hợp, chúng ta chỉ cần thực hiện nối chuỗi với '~' giữa các trường.

Tôi chắc rằng hệ thống này có thể có nhược điểm - đối với cơ sở dữ liệu được cập nhật mạnh, hiệu suất có thể bị ảnh hưởng, nhưng đối với ứng dụng web của tôi, chúng tôi nhận được nhiều lượt đọc hơn ghi và dường như nó hoạt động khá tốt. Chúng tôi thậm chí đã viết một tiện ích VB.NET nhỏ để tự động viết các kích hoạt dựa trên các định nghĩa bảng.

Chỉ là một ý nghĩ!


5
Không cần lưu trữ NewValue, vì nó được lưu trữ trong bảng đã được kiểm toán.
Petrus Theron

17
Nói đúng ra, đó là sự thật. Nhưng - khi có một số thay đổi cho cùng một trường trong một khoảng thời gian, việc lưu trữ giá trị mới làm cho các truy vấn như 'hiển thị cho tôi tất cả các thay đổi được thực hiện bởi Brian' dễ dàng hơn rất nhiều vì tất cả thông tin về một bản cập nhật được lưu giữ một bản ghi. Chỉ là một ý nghĩ!
Chris Roberts

1
Tôi nghĩ sysnamecó thể là một kiểu dữ liệu phù hợp hơn cho tên bảng và cột.
Sam

2
@Sam sử dụng sysname không thêm bất kỳ giá trị nào; nó thậm chí có thể gây nhầm lẫn ... stackoverflow.com/questions/5720212/ từ
Jowen

19

Các bảng History bài viết trong Programmer Cơ sở dữ liệu trên blog có thể có ích - bao gồm một số các điểm nêu ra ở đây và thảo luận về việc lưu trữ các đồng bằng châu thổ.

Biên tập

Trong bài tiểu luận về Bảng lịch sử , tác giả ( Kenneth Downs ), khuyên bạn nên duy trì một bảng lịch sử có ít nhất bảy cột:

  1. Dấu thời gian của sự thay đổi,
  2. Người dùng đã thực hiện thay đổi,
  3. Mã thông báo để xác định bản ghi đã được thay đổi (trong đó lịch sử được duy trì tách biệt với trạng thái hiện tại),
  4. Cho dù thay đổi là chèn, cập nhật hoặc xóa,
  5. Giá trị cũ,
  6. Giá trị mới,
  7. Đồng bằng (để thay đổi giá trị số).

Các cột không bao giờ thay đổi hoặc không cần lịch sử không được theo dõi trong bảng lịch sử để tránh phình to. Lưu trữ delta cho các giá trị số có thể làm cho các truy vấn tiếp theo dễ dàng hơn, mặc dù nó có thể được lấy từ các giá trị cũ và mới.

Bảng lịch sử phải được bảo mật, với những người dùng không phải là hệ thống không thể chèn, cập nhật hoặc xóa các hàng. Chỉ hỗ trợ thanh lọc định kỳ để giảm kích thước tổng thể (và nếu được phép trong trường hợp sử dụng).


14

Chúng tôi đã thực hiện một giải pháp rất giống với giải pháp mà Chris Roberts gợi ý, và nó hoạt động khá tốt đối với chúng tôi.

Chỉ khác là chúng tôi chỉ lưu trữ giá trị mới. Giá trị cũ là sau khi tất cả được lưu trữ trong hàng lịch sử trước đó

[ID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [int] NULL,
[EventDate] [datetime] NOT NULL,
[TableName] [varchar](50) NOT NULL,
[RecordID] [varchar](20) NOT NULL,
[FieldName] [varchar](50) NULL,
[NewValue] [varchar](5000) NULL

Hãy nói rằng bạn có một bảng với 20 cột. Bằng cách này, bạn chỉ phải lưu trữ cột chính xác đã thay đổi thay vì phải lưu trữ toàn bộ hàng.


14

Tránh thiết kế 1; nó không tiện dụng một khi bạn cần quay lại các phiên bản cũ của hồ sơ - tự động hoặc "thủ công" bằng bảng điều khiển quản trị viên.

Tôi thực sự không thấy nhược điểm của Thiết kế 2. Tôi nghĩ rằng bảng thứ hai, Lịch sử nên chứa tất cả các cột có trong bảng đầu tiên, Bảng ghi. Ví dụ, trong mysql bạn có thể dễ dàng tạo bảng có cùng cấu trúc với bảng khác ( create table X like Y). Và, khi bạn sắp thay đổi cấu trúc của bảng Bản ghi trong cơ sở dữ liệu trực tiếp của mình, bạn vẫn phải sử dụng alter tablecác lệnh - và không có nỗ lực lớn nào trong việc chạy các lệnh này cho bảng Lịch sử của bạn.

Ghi chú

  • Bảng ghi chỉ chứa bản sửa đổi mới nhất;
  • Bảng lịch sử chứa tất cả các bản sửa đổi trước đó của bản ghi trong bảng Bản ghi;
  • Khóa chính của bảng lịch sử là khóa chính của bảng Bản ghi có thêm RevisionIdcột;
  • Hãy suy nghĩ về các trường phụ trợ bổ sung như ModifiedBy- người dùng đã tạo bản sửa đổi cụ thể. Bạn cũng có thể muốn có một trường DeletedByđể theo dõi ai đã xóa bản sửa đổi cụ thể.
  • Hãy suy nghĩ về DateModifiedý nghĩa của nó - có nghĩa là nơi sửa đổi cụ thể này được tạo ra, hoặc nó sẽ có nghĩa là khi bản sửa đổi cụ thể này được thay thế bằng một bản sửa đổi khác. Cái trước đòi hỏi trường phải nằm trong bảng Records, và dường như trực quan hơn ngay từ cái nhìn đầu tiên; tuy nhiên, giải pháp thứ hai có vẻ thực tế hơn đối với các hồ sơ đã bị xóa (ngày mà bản sửa đổi cụ thể này đã bị xóa). Nếu bạn đi cho giải pháp đầu tiên, có lẽ bạn sẽ cần một trường thứ hai DateDeleted(chỉ khi bạn cần nó tất nhiên). Phụ thuộc vào bạn và những gì bạn thực sự muốn ghi lại.

Các thao tác trong Thiết kế 2 rất tầm thường:

Sửa đổi
  • sao chép bản ghi từ bảng Bản ghi sang bảng Lịch sử, cung cấp cho RevisionId mới (nếu nó chưa có trong bảng Bản ghi), xử lý DateModified (tùy thuộc vào cách bạn diễn giải nó, xem ghi chú ở trên)
  • tiếp tục với bản cập nhật bình thường của bản ghi trong bảng Records
Xóa bỏ
  • thực hiện chính xác như trong bước đầu tiên của hoạt động Sửa đổi. Xử lý DateModified / DateDelatted tương ứng, tùy thuộc vào cách hiểu bạn đã chọn.
Phục hồi (hoặc khôi phục)
  • lấy bản sửa đổi cao nhất (hoặc một số cụ thể?) từ bảng Lịch sử và sao chép nó vào bảng Bản ghi
Liệt kê lịch sử sửa đổi cho hồ sơ cụ thể
  • chọn từ bảng Lịch sử và bảng Bản ghi
  • nghĩ chính xác những gì bạn mong đợi từ hoạt động này; nó có thể sẽ xác định thông tin bạn yêu cầu từ các trường DateModified / DateDelatted (xem ghi chú ở trên)

Nếu bạn chọn Thiết kế 2, tất cả các lệnh SQL cần thiết để làm điều đó sẽ rất rất dễ dàng, cũng như bảo trì! Có thể, sẽ dễ dàng hơn nhiều nếu bạn sử dụng các cột phụ ( RevisionId, DateModified) cũng trong bảng Bản ghi - để giữ cả hai bảng ở cùng một cấu trúc (ngoại trừ các phím duy nhất)! Điều này sẽ cho phép các lệnh SQL đơn giản, có thể chịu được mọi thay đổi cấu trúc dữ liệu:

insert into EmployeeHistory select * from Employe where ID = XX

Đừng quên sử dụng các giao dịch!

Về tỷ lệ , giải pháp này rất hiệu quả, vì bạn không chuyển đổi bất kỳ dữ liệu nào từ XML qua lại, chỉ sao chép toàn bộ các hàng của bảng - các truy vấn rất đơn giản, sử dụng các chỉ mục - rất hiệu quả!


12

Nếu bạn phải lưu trữ lịch sử, hãy tạo một bảng bóng có cùng lược đồ với bảng bạn đang theo dõi và cột 'Ngày sửa đổi' và 'Loại sửa đổi' (ví dụ: 'xóa', 'cập nhật'). Viết (hoặc tạo - xem bên dưới) một tập hợp các kích hoạt để điền vào bảng kiểm toán.

Thật đơn giản để tạo ra một công cụ sẽ đọc từ điển dữ liệu hệ thống cho một bảng và tạo một tập lệnh tạo bảng bóng và một tập hợp các trình kích hoạt để điền vào nó.

Đừng cố sử dụng XML cho việc này, lưu trữ XML kém hiệu quả hơn nhiều so với lưu trữ bảng cơ sở dữ liệu gốc mà loại trình kích hoạt này sử dụng.


3
+1 cho đơn giản! Một số người sẽ kỹ sư quá mức vì sợ những thay đổi sau này, trong khi hầu hết thời gian không có thay đổi thực sự xảy ra! Ngoài ra, việc quản lý lịch sử trong một bảng và các bản ghi thực tế trong một bảng khác dễ dàng hơn nhiều so với việc có tất cả chúng trong một bảng (cơn ác mộng) với một số cờ hoặc trạng thái. Nó được gọi là 'KISS' và thông thường sẽ thưởng cho bạn về lâu dài.
Jeach

+1 hoàn toàn đồng ý, chính xác những gì tôi nói trong câu trả lời của mình ! Đơn giản và mạnh mẽ!
TMS

8

Ramesh, tôi đã tham gia phát triển hệ thống dựa trên phương pháp đầu tiên.
Hóa ra việc lưu trữ các bản sửa đổi khi XML đang dẫn đến sự tăng trưởng cơ sở dữ liệu khổng lồ và làm chậm đáng kể mọi thứ.
Cách tiếp cận của tôi sẽ có một bảng cho mỗi thực thể:

Employee (Id, Name, ... , IsActive)  

Trong đó IsActive là một dấu hiệu của phiên bản mới nhất

Nếu bạn muốn liên kết một số thông tin bổ sung với các sửa đổi, bạn có thể tạo bảng riêng biệt chứa thông tin đó và liên kết nó với các bảng thực thể bằng quan hệ PK \ FK.

Bằng cách này bạn có thể lưu trữ tất cả các phiên bản của nhân viên trong một bảng. Ưu điểm của phương pháp này:

  • Cấu trúc cơ sở dữ liệu đơn giản
  • Không có xung đột kể từ khi bảng trở thành chỉ phụ thuộc
  • Bạn có thể quay lại phiên bản trước bằng cách thay đổi cờ IsActive
  • Không cần tham gia để có được lịch sử đối tượng

Lưu ý rằng bạn nên cho phép khóa chính không phải là duy nhất.


6
Tôi sẽ sử dụng cột "RevisionNumber" hoặc "RevisionDate" thay vì hoặc ngoài IsActive, để bạn có thể xem tất cả các sửa đổi theo thứ tự.
Sklivvz

Tôi sẽ sử dụng "ParentRowId" vì điều đó cho phép bạn dễ dàng truy cập vào các phiên bản trước cũng như khả năng tìm thấy cả cơ sở và kết thúc một cách nhanh chóng.
chacham15

6

Cách mà tôi đã thấy điều này được thực hiện trong quá khứ là có

Employees (EmployeeId, DateModified, < Employee Fields > , boolean isCurrent );

Bạn không bao giờ "cập nhật" trên bảng này (ngoại trừ thay đổi hợp lệ của isC Hiện tại), chỉ cần chèn các hàng mới. Đối với bất kỳ EmployeeId nào, chỉ có 1 hàng có thể có isCản == 1.

Sự phức tạp của việc duy trì điều này có thể bị ẩn bởi các khung nhìn và các thay đổi "thay vì" (trong orory, tôi cho rằng những thứ tương tự RDBMS khác), bạn thậm chí có thể chuyển đến các khung nhìn cụ thể nếu các bảng quá lớn và không thể được xử lý bởi các chỉ mục) .

Phương pháp này là ok, nhưng bạn có thể kết thúc với một số truy vấn phức tạp.

Cá nhân tôi rất thích cách thiết kế 2 của bạn, đó là cách tôi đã từng làm trong quá khứ. Nó đơn giản để hiểu, đơn giản để thực hiện và đơn giản để duy trì.

Nó cũng tạo ra rất ít chi phí cho cơ sở dữ liệu và ứng dụng, đặc biệt là khi thực hiện các truy vấn đọc, có khả năng là những gì bạn sẽ làm 99% thời gian.

Nó cũng sẽ khá dễ dàng để tự động tạo các bảng lịch sử và kích hoạt để duy trì (giả sử nó sẽ được thực hiện thông qua các kích hoạt).


4

Sửa đổi dữ liệu là một khía cạnh của khái niệm ' thời gian hợp lệ ' của Cơ sở dữ liệu tạm thời. Nhiều nghiên cứu đã đi sâu vào vấn đề này, và nhiều mô hình và hướng dẫn đã xuất hiện. Tôi đã viết một bài trả lời dài với một loạt các tài liệu tham khảo cho câu hỏi này cho những người quan tâm.


4

Tôi sẽ chia sẻ với bạn thiết kế của tôi và nó khác với cả hai thiết kế của bạn ở chỗ nó yêu cầu một bảng cho mỗi loại thực thể. Tôi tìm thấy cách tốt nhất để mô tả bất kỳ thiết kế cơ sở dữ liệu nào là thông qua ERD, đây là của tôi:

nhập mô tả hình ảnh ở đây

Trong ví dụ này, chúng tôi có một thực thể có tên là nhân viên . sử dụng bảng giữ hồ sơ và người dùng của bạn thực thểentity_revision hai bảng mà giữ lịch sử sửa đổi cho tất cả các loại thực thể mà bạn sẽ có trong hệ thống của bạn. Đây là cách thiết kế này hoạt động:

Hai trường của entity_idrevision_id

Mỗi thực thể trong hệ thống của bạn sẽ có một id thực thể duy nhất của riêng nó. Thực thể của bạn có thể trải qua các phiên bản nhưng thực thể của nó sẽ vẫn như cũ. Bạn cần giữ id thực thể này trong bảng nhân viên của bạn (dưới dạng khóa ngoại). Bạn cũng nên lưu trữ loại thực thể của mình trong bảng thực thể (ví dụ: 'staff'). Bây giờ, đối với revision_id, như tên của nó hiển thị, nó theo dõi các phiên bản thực thể của bạn. Cách tốt nhất tôi tìm thấy cho việc này là sử dụng worker_id làm revision_id của bạn. Điều này có nghĩa là bạn sẽ có id sửa đổi trùng lặp cho các loại thực thể khác nhau nhưng điều này không đối xử với tôi (tôi không chắc về trường hợp của bạn). Lưu ý quan trọng duy nhất cần thực hiện là sự kết hợp giữa entity_id và revision_id phải là duy nhất.

Ngoài ra còn có một trường trạng thái trong bảng entity numvision chỉ ra trạng thái sửa đổi. Nó có thể có một trong ba trạng thái: latest, obsoletehoặc deleted(không dựa vào ngày sửa đổi sẽ giúp bạn rất nhiều để thúc đẩy các truy vấn của bạn).

Một lưu ý cuối cùng về revision_id, tôi đã không tạo khóa ngoại kết nối worker_id với revision_id vì chúng tôi không muốn thay đổi bảng entity numvision cho từng loại thực thể mà chúng tôi có thể thêm trong tương lai.

CHỨNG MINH

Đối với mỗi nhân viên mà bạn muốn chèn vào cơ sở dữ liệu, bạn cũng sẽ bổ sung thêm một kỷ lục để tổ chứcentity_revision . Hai bản ghi cuối cùng này sẽ giúp bạn theo dõi ai và khi một bản ghi đã được chèn vào cơ sở dữ liệu.

CẬP NHẬT

Mỗi bản cập nhật cho một hồ sơ nhân viên hiện tại sẽ được triển khai dưới dạng hai lần chèn, một trong bảng nhân viên và một trong thực thể. Điều thứ hai sẽ giúp bạn biết ai và khi nào hồ sơ đã được cập nhật.

XÓA

Để xóa một nhân viên, một bản ghi được chèn vào entity numvision cho biết việc xóa và được thực hiện.

Như bạn có thể thấy trong thiết kế này, không có dữ liệu nào bị thay đổi hoặc xóa khỏi cơ sở dữ liệu và quan trọng hơn là mỗi loại thực thể chỉ cần một bảng. Cá nhân tôi thấy thiết kế này thực sự linh hoạt và dễ làm việc. Nhưng tôi không chắc về bạn vì nhu cầu của bạn có thể khác.

[CẬP NHẬT]

Có các phân vùng được hỗ trợ trong các phiên bản MySQL mới, tôi tin rằng thiết kế của tôi cũng đi kèm với một trong những hiệu suất tốt nhất. Người ta có thể phân vùng entitybảng bằng cách sử dụng typetrường trong khi phân vùng entity_revisionsử dụng statetrường của nó . Điều này sẽ tăng các SELECTtruy vấn cho đến nay trong khi giữ cho thiết kế đơn giản và sạch sẽ.


3

Nếu thực sự một dấu vết kiểm toán là tất cả những gì bạn cần, tôi sẽ nghiêng về giải pháp bảng kiểm toán (hoàn thành với các bản sao không chuẩn hóa của cột quan trọng trên các bảng khác, ví dụ UserName:). Tuy nhiên, hãy nhớ rằng kinh nghiệm cay đắng đó chỉ ra rằng một bảng kiểm toán duy nhất sẽ là một nút cổ chai lớn; có lẽ đáng để nỗ lực tạo các bảng kiểm toán riêng cho tất cả các bảng được kiểm toán của bạn.

Nếu bạn cần theo dõi các phiên bản lịch sử thực tế (và / hoặc tương lai), thì giải pháp tiêu chuẩn là theo dõi cùng một thực thể với nhiều hàng bằng cách sử dụng một số kết hợp của các giá trị bắt đầu, kết thúc và thời lượng. Bạn có thể sử dụng chế độ xem để giúp truy cập các giá trị hiện tại thuận tiện. Nếu đây là cách tiếp cận bạn thực hiện, bạn có thể gặp vấn đề nếu tham chiếu dữ liệu được phiên bản của bạn biến đổi nhưng dữ liệu không được đảo ngược.


3

Nếu bạn muốn làm cái đầu tiên, bạn cũng có thể muốn sử dụng XML cho bảng Nhân viên. Hầu hết các cơ sở dữ liệu mới hơn cho phép bạn truy vấn vào các trường XML, vì vậy đây không phải lúc nào cũng là một vấn đề. Và có thể đơn giản hơn khi có một cách để truy cập dữ liệu nhân viên bất kể đó là phiên bản mới nhất hay phiên bản cũ hơn.

Tôi sẽ thử cách tiếp cận thứ hai mặc dù. Bạn có thể đơn giản hóa việc này bằng cách chỉ có một bảng Nhân viên với trường DateModified. EmployeeId + DateModified sẽ là khóa chính và bạn có thể lưu trữ một bản sửa đổi mới chỉ bằng cách thêm một hàng. Cách này lưu trữ các phiên bản cũ hơn và khôi phục các phiên bản từ kho lưu trữ cũng dễ dàng hơn.

Một cách khác để làm điều này có thể là mô hình datavault của Dan Linstedt. Tôi đã làm một dự án cho văn phòng thống kê Hà Lan sử dụng mô hình này và nó hoạt động khá tốt. Nhưng tôi không nghĩ rằng nó trực tiếp hữu ích cho việc sử dụng cơ sở dữ liệu hàng ngày. Bạn có thể nhận được một số ý tưởng từ việc đọc các bài báo của anh ấy mặc dù.


2

Làm thế nào về:

  • Mã hiệu công nhân
  • DateModified
    • và / hoặc số sửa đổi, tùy thuộc vào cách bạn muốn theo dõi nó
  • Đã sửa đổiByUSerId
    • cộng với bất kỳ thông tin nào khác bạn muốn theo dõi
  • Lĩnh vực nhân viên

Bạn tạo khóa chính (EmployeeId, DateModified) và để có (các) bản ghi "hiện tại", bạn chỉ cần chọn MAX (DateModified) cho mỗi nhân viên. Lưu trữ một IsC hiện tại là một ý tưởng rất tồi, bởi vì trước hết, nó có thể được tính toán và thứ hai, quá dễ dàng để dữ liệu không đồng bộ hóa.

Bạn cũng có thể tạo chế độ xem chỉ liệt kê các bản ghi mới nhất và chủ yếu sử dụng chế độ đó trong khi làm việc trong ứng dụng của bạn. Điều thú vị về cách tiếp cận này là bạn không có các bản sao dữ liệu và bạn không phải thu thập dữ liệu từ hai nơi khác nhau (hiện tại trong Nhân viên và được lưu trữ trong Emp EmployHistory) để lấy tất cả lịch sử hoặc khôi phục, v.v.) .


Một nhược điểm của phương pháp này là bảng sẽ phát triển nhanh hơn so với nếu bạn sử dụng hai bảng.
cdmckay

2

Nếu bạn muốn dựa vào dữ liệu lịch sử (vì lý do báo cáo), bạn nên sử dụng cấu trúc tương tự như sau:

// Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

// Holds the Employee revisions in rows.
"EmployeeHistories (HistoryId, EmployeeId, DateModified, OldValue, NewValue, FieldName)"

Hoặc giải pháp toàn cầu cho ứng dụng:

// Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

// Holds all entities revisions in rows.
"EntityChanges (EntityName, EntityId, DateModified, OldValue, NewValue, FieldName)"

Bạn cũng có thể lưu các bản sửa đổi của mình bằng XML, sau đó bạn chỉ có một bản ghi cho một bản sửa đổi. Điều này sẽ giống như:

// Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

// Holds all entities revisions in rows.
"EntityChanges (EntityName, EntityId, DateModified, XMLChanges)"

1
Tốt hơn: sử dụng nguồn sự kiện :)
dariol

1

Chúng tôi đã có những yêu cầu tương tự, và những gì chúng tôi tìm thấy là thường thì người dùng chỉ muốn xem những gì đã được thay đổi, không nhất thiết phải quay lại bất kỳ thay đổi nào.

Tôi không chắc trường hợp sử dụng của bạn là gì, nhưng những gì chúng tôi đã làm là tạo và bảng Kiểm toán được cập nhật tự động với các thay đổi đối với một thực thể kinh doanh, bao gồm tên thân thiện của bất kỳ tham chiếu và liệt kê khóa ngoại nào.

Bất cứ khi nào người dùng lưu các thay đổi của họ, chúng tôi tải lại đối tượng cũ, chạy so sánh, ghi lại các thay đổi và lưu thực thể (tất cả được thực hiện trong một giao dịch cơ sở dữ liệu trong trường hợp có bất kỳ vấn đề nào).

Điều này dường như hoạt động rất tốt cho người dùng của chúng tôi và giúp chúng tôi không phải đau đầu khi có một bảng kiểm toán hoàn toàn riêng biệt với cùng các lĩnh vực như thực thể kinh doanh của chúng tôi.


0

Có vẻ như bạn muốn theo dõi các thay đổi đối với các thực thể cụ thể theo thời gian, ví dụ: ID 3, "bob", "123 đường chính", sau đó một ID 3 khác, "bob" "234 elm st", v.v. để tìm ra lịch sử sửa đổi hiển thị mọi địa chỉ "bob" đã có tại.

Cách tốt nhất để làm điều này là có một trường "hiện tại" trên mỗi bản ghi và (có thể) dấu thời gian hoặc FK vào bảng ngày / giờ.

Sau đó, các phần chèn phải đặt "hiện tại" và bỏ đặt "hiện tại" trên bản ghi "hiện tại" trước đó. Các truy vấn phải chỉ định "là hiện tại", trừ khi bạn muốn tất cả lịch sử.

Có nhiều điều chỉnh hơn nữa nếu đây là một bảng rất lớn hoặc dự kiến ​​sẽ có một số lượng lớn các bản sửa đổi, nhưng đây là một cách tiếp cận khá chuẩn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.