2008-11-27 32 views
122

Giả sử tôi có bản ghi trong cơ sở dữ liệu và cả quản trị viên và người dùng thông thường đều có thể cập nhật.Cách kiểm soát phiên bản bản ghi trong cơ sở dữ liệu

Bất kỳ ai cũng có thể đề xuất cách tiếp cận/kiến ​​trúc tốt về cách kiểm soát phiên bản mọi thay đổi trong bảng này để có thể khôi phục bản ghi về bản sửa đổi trước đó.

Trả lời

123

Giả sử bạn có bảng FOO mà quản trị viên và người dùng có thể cập nhật. Hầu hết thời gian bạn có thể viết các truy vấn đối với bảng FOO. Những ngày hạnh phúc.

Sau đó, tôi sẽ tạo bảng FOO_HISTORY. Điều này có tất cả các cột của bảng FOO. Khóa chính giống như FOO cộng với cột RevisionNumber. Có khóa ngoại từ FOO_HISTORY đến FOO. Bạn cũng có thể thêm các cột liên quan đến bản sửa đổi như UserId và RevisionDate. Điền các RevisionNumbers theo kiểu ngày càng tăng trên tất cả các bảng *_HISTORY (ví dụ: từ một chuỗi Oracle hoặc tương đương). Đừng dựa vào đó chỉ là một sự thay đổi trong một giây. I E. không đặt RevisionDate vào khóa chính.

Bây giờ, mỗi lần bạn cập nhật FOO, ngay trước khi bạn cập nhật, bạn chèn các giá trị cũ vào FOO_HISTORY. Bạn làm điều này ở một mức độ cơ bản trong thiết kế của bạn để các lập trình viên không thể vô tình bỏ lỡ bước này.

Nếu bạn muốn xóa một hàng từ FOO bạn có một số lựa chọn. Hoặc là xếp chồng và xóa tất cả lịch sử hoặc thực hiện xóa hợp lý bằng cách gắn cờ FOO là đã xóa.

Giải pháp này là tốt khi bạn phần lớn quan tâm đến các giá trị hiện tại và chỉ thỉnh thoảng trong lịch sử. Nếu bạn luôn cần lịch sử thì bạn có thể đặt ngày bắt đầu và kết thúc hiệu quả và giữ tất cả các bản ghi trong chính FOO. Mỗi truy vấn sau đó cần phải kiểm tra những ngày đó.

+0

Bạn có thể thực hiện cập nhật bảng kiểm tra bằng trình kích hoạt cơ sở dữ liệu nếu lớp truy cập dữ liệu của bạn không trực tiếp hỗ trợ nó. Ngoài ra, không khó để xây dựng trình tạo mã để làm cho trình kích hoạt sử dụng nội suy từ từ điển dữ liệu hệ thống. – ConcernedOfTunbridgeWells

+25

Tôi woyuld khuyên bạn nên thực sự chèn dữ liệu _new_, không phải trước đó, vì vậy bảng lịch sử có tất cả dữ liệu. Mặc dù nó lưu trữ dữ liệu redyundent, nó loại bỏ các trường hợp đặc biệt cần thiết để đối phó với tìm kiếm trong cả hai bảng khi dữ liệu lịch sử được yêu cầu. – Nerdfest

+0

@Nerdfest - Tôi có nghĩa là chèn tất cả các giá trị hiện tại vào bảng lịch sử, và sau đó cập nhật bảng chính với các giá trị mới. –

35

Tôi nghĩ bạn đang tìm kiếm phiên bản nội dung của bản ghi cơ sở dữ liệu (như StackOverflow thực hiện khi ai đó chỉnh sửa câu hỏi/câu trả lời). Điểm khởi đầu tốt có thể là xem xét một số mô hình cơ sở dữ liệu sử dụng theo dõi sửa đổi.

Ví dụ tốt nhất mà bạn nghĩ đến là MediaWiki, công cụ Wikipedia. So sánh sơ đồ cơ sở dữ liệu here, đặc biệt là revision table.

Tùy thuộc vào công nghệ bạn đang sử dụng, bạn sẽ phải tìm một số thuật toán khác biệt/hợp nhất tốt.

Kiểm tra this question nếu nó dành cho .NET.

21

Trong thế giới BI, bạn có thể thực hiện việc này bằng cách thêm startDate và endDate vào bảng bạn muốn phiên bản. Khi bạn chèn bản ghi đầu tiên vào bảng, startDate được điền, nhưng endDate là null. Khi bạn chèn bản ghi thứ hai, bạn cũng cập nhật endDate của bản ghi đầu tiên với startDate của bản ghi thứ hai.

Khi bạn muốn xem bản ghi hiện tại, bạn chọn một trong đó endDate là rỗng.

Điều này đôi khi được gọi là loại 2 Slowly Changing Dimension. Xem thêm TupleVersioning

+0

Bàn của tôi có phát triển khá lớn bằng cách sử dụng phương pháp này không? –

+1

Có, nhưng bạn có thể giải quyết vấn đề đó bằng cách lập chỉ mục và/hoặc phân vùng bảng. Ngoài ra, sẽ chỉ có một số ít các bảng lớn. Hầu hết sẽ nhỏ hơn nhiều. – ConcernedOfTunbridgeWells

3

Bạn không nói cơ sở dữ liệu nào và tôi không thấy nó trong các thẻ bài đăng. Nếu nó dành cho Oracle, tôi có thể đề xuất phương pháp được xây dựng trong Nhà thiết kế: sử dụng journal tables.Nếu nó cho bất kỳ cơ sở dữ liệu khác, tốt, tôi về cơ bản đề nghị cùng một cách, quá ...

Cách hoạt động, trong trường hợp bạn muốn sao chép nó trong một DB khác, hoặc có thể nếu bạn chỉ muốn hiểu nó, là đối với một bảng có một bảng bóng đổ được tạo ra, chỉ là một bảng cơ sở dữ liệu bình thường, với các thông số kỹ thuật tương tự, cộng thêm một số trường: như hành động cuối cùng (chuỗi, giá trị điển hình "INS" để chèn, "UPD" để cập nhật và "DEL" để xóa), ngày giờ cho khi hành động diễn ra và id người dùng cho người đã thực hiện.

Thông qua các trình kích hoạt, mọi hành động cho bất kỳ hàng nào trong bảng chèn một hàng mới trong bảng tạp chí với các giá trị mới, hành động được thực hiện, khi nào và bởi người dùng nào. Bạn không bao giờ xóa bất kỳ hàng nào (ít nhất là không phải trong vài tháng qua). Có, nó sẽ phát triển lớn, dễ dàng hàng triệu hàng, nhưng bạn có thể dễ dàng theo dõi giá trị cho bất kỳ bản ghi nào tại bất kỳ lúc nào kể từ khi nhật ký bắt đầu hoặc hàng tạp chí cũ bị xóa lần cuối và ai thực hiện thay đổi cuối cùng .

Trong Oracle mọi thứ bạn cần được tạo tự động dưới dạng mã SQL, tất cả những gì bạn phải làm là biên dịch/chạy nó; và nó đi kèm với một ứng dụng CRUD cơ bản (chỉ thực sự là "R") để kiểm tra nó.

3

Hai lựa chọn:

  1. Có một bảng lịch sử - chèn dữ liệu cũ vào bảng lịch sử này bất cứ khi nào ban đầu được cập nhật.
  2. Bảng kiểm tra - lưu trữ giá trị trước và sau - chỉ cho các cột đã sửa đổi trong bảng kiểm tra cùng với các thông tin khác như cập nhật và thời điểm.
2

Bạn có thể thực hiện kiểm tra trên bảng SQL thông qua trình kích hoạt SQL. Từ trình kích hoạt, bạn có thể truy cập 2 bảng đặc biệt (inserted and deleted). Các bảng này chứa các hàng chính xác được chèn hoặc xóa mỗi khi bảng được cập nhật. Trong SQL kích hoạt, bạn có thể lấy các hàng đã sửa đổi này và chèn chúng vào bảng kiểm toán. Cách tiếp cận này có nghĩa là kiểm toán của bạn là minh bạch đối với lập trình viên; không đòi hỏi nỗ lực từ họ hoặc bất kỳ kiến ​​thức triển khai nào.

Tiền thưởng bổ sung của phương pháp này là việc kiểm tra sẽ xảy ra bất kể hoạt động sql diễn ra thông qua DLL truy cập dữ liệu của bạn hay thông qua truy vấn SQL thủ công; (khi kiểm toán được thực hiện trên chính máy chủ).

7

Nâng cấp lên SQL 2008.

Hãy thử sử dụng SQL Change Tracking, trong SQL 2008. Thay vì timestamping và cột bia mộ hacks, bạn có thể sử dụng tính năng mới này cho những thay đổi theo dõi trên dữ liệu trong cơ sở dữ liệu của bạn.

MSDN SQL 2008 Change Tracking

2

Tôi cũng đang làm tương tự. Tôi đang tạo một cơ sở dữ liệu cho các kế hoạch bài học. Các kế hoạch này cần sự linh hoạt về thay đổi phiên bản nguyên tử. Nói cách khác, mỗi thay đổi, cho dù nhỏ đến đâu, các kế hoạch bài học cần được cho phép nhưng phiên bản cũ cũng cần được giữ nguyên vẹn. Bằng cách đó, người tạo bài học có thể chỉnh sửa kế hoạch bài học trong khi sinh viên đang sử dụng chúng.

Cách thức hoạt động là khi học sinh đã thực hiện một bài học, kết quả của chúng được đính kèm với phiên bản mà chúng đã hoàn thành. Nếu thay đổi được thực hiện, kết quả của họ sẽ luôn trỏ đến phiên bản của họ.

Bằng cách này, nếu tiêu chí bài học bị xóa hoặc di chuyển, kết quả của chúng sẽ không thay đổi.

Cách tôi hiện đang thực hiện việc này là xử lý tất cả dữ liệu trong một bảng. Thông thường tôi sẽ chỉ có một trường id, nhưng với hệ thống này, tôi đang sử dụng một id và một sub_id. Sub_id luôn luôn ở lại với hàng, thông qua các bản cập nhật và xóa. Id được tự động tăng lên. Phần mềm kế hoạch bài học sẽ liên kết với sub_id mới nhất. Kết quả của học sinh sẽ liên kết với id. Tôi cũng đã bao gồm dấu thời gian để theo dõi khi có thay đổi, nhưng không cần thiết để xử lý phiên bản.

Một điều tôi có thể thay đổi, khi tôi đã thử nghiệm nó, là tôi có thể sử dụng ý tưởng null kết thúc được đề cập trước đó. Trong hệ thống của tôi, để tìm phiên bản mới nhất, tôi sẽ phải tìm tối đa (id). Hệ thống khác chỉ tìm endDate = null. Không chắc chắn nếu các lợi ích ngoài có trường ngày khác.

Hai xu của tôi.

2

Trong khi @WW. câu trả lời là một câu trả lời hay là một cách khác là tạo cột phiên bản và giữ tất cả các phiên bản của bạn trong cùng một bảng.

Đối với một cách tiếp cận bảng bạn có:

  • Sử dụng một lá cờ để chỉ ra ala mới nhất Word Press
  • HOẶC làm một khó chịu hơn so với phiên bản outer join.

Một ví dụ SQL của outer join phương pháp sử dụng số sửa đổi là:

SELECT tc.* 
FROM text_content tc 
LEFT OUTER JOIN text_content mc ON tc.path = mc.path 
AND mc.revision > tc.revision 
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- path in this case is our natural id. 

Tin xấu là ở trên đòi hỏi một outer join trong và bên ngoài tham gia có thể chậm. Tin tốt là tạo ra các mục mới về mặt lý thuyết là rẻ hơn bởi vì bạn có thể làm điều đó trong một thao tác ghi với các giao dịch out (giả sử cơ sở dữ liệu của bạn là nguyên tử).

Một ví dụ thực hiện một phiên bản mới cho '/stuff' có thể là:

INSERT INTO text_content (id, path, data, revision, revision_comment, enabled, create_time, update_time) 
(
SELECT 
(md5(random()::text)) -- {id} 
, tc.path 
, 'NEW' -- {data} 
, (tc.revision + 1) 
, 'UPDATE' -- {comment} 
, 't' -- {enabled} 
, tc.create_time 
, now() 
FROM text_content tc 
LEFT OUTER JOIN text_content mc ON tc.path = mc.path 
AND mc.revision > tc.revision 
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- {path} 
) 

Chúng tôi chèn bằng cách sử dụng các dữ liệu cũ. Điều này đặc biệt hữu ích nếu nói rằng bạn chỉ muốn cập nhật một cột và tránh khóa và giao dịch lạc quan.

Cách tiếp cận cờ và phương pháp tiếp cận bảng lịch sử yêu cầu hai hàng để chèn/cập nhật.

Ưu điểm khác với phương pháp tiếp cận số sửa đổi outer join là bạn luôn có thể cấu trúc lại phương pháp tiếp cận nhiều bảng sau với trình kích hoạt vì trình kích hoạt của bạn về cơ bản sẽ làm điều gì đó như trên.

5

Chỉ muốn thêm rằng một giải pháp tốt cho vấn đề này là sử dụng một Temporal database. Nhiều nhà cung cấp cơ sở dữ liệu cung cấp tính năng này ra khỏi hộp hoặc thông qua phần mở rộng. Tôi đã sử dụng thành công phần mở rộng temporal table với PostgreSQL nhưng những người khác cũng đã sử dụng nó. Bất cứ khi nào bạn cập nhật một bản ghi trong cơ sở dữ liệu, cơ sở dữ liệu cũng giữ phiên bản trước đó của bản ghi đó.

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