Đừng chỉ "Cập nhật" dữ liệu. Hãy "Xếp chồng" chúng lên. (2 người xem)

Người dùng đang xem chủ đề này

jack nt

Thành viên thường trực
Tham gia
23/12/07
Bài viết
305
Được thích
209
Cái "khổ" của việc quản lý dữ liệu lịch sử

Là người làm lập trình, chắc hẳn ai cũng từng gặp tình huống này: Bạn xây dựng một hệ thống quản lý nhân sự hay tài sản rất mượt mà. Mọi thứ đều ổn cho đến khi sếp hoặc khách hàng nói: "Tôi không chỉ muốn biết hiện tại họ đang ở đâu; tôi muốn xem lại toàn bộ quá trình họ đã làm gì trong 10 năm qua."

Ngay lập tức, logic "Update" (Cập nhật) đơn giản của bạn biến thành một cơn ác mộng:

  1. Xáo trộn bảng biểu (Table Shuffle): Bạn phải tạo thêm một bảng "Hiện tại" và một bảng "Lịch sử".
  2. Gánh nặng mã nguồn: Mỗi khi dữ liệu thay đổi, bạn phải viết những đoạn code phức tạp để:
    • Tìm bản ghi cũ.
    • Sao chép nó.
    • Dán vào bảng Lịch sử.
    • Sau đó mới được cập nhật bản ghi mới vào bảng Hiện tại.
  3. Nỗi đau truy xuất: Khi cần một báo cáo đầy đủ, bạn bị kẹt trong những câu lệnh JOIN hoặc UNION nặng nề, khiến hệ thống chạy chậm như rùa khi dữ liệu phình to.
Trong ngành vận tải biển, nơi mà lịch sử 20 năm đi tàu của một thuyền viên (Tên tàu, trọng tải, chức danh, đánh giá của thuyền trưởng) chính là "linh hồn" của họ, thì việc xáo trộn bảng biểu này là công thức dẫn đến thảm họa.

Mẹo "Hash-Stack": Sự tinh tế trong đơn giản

Nhiều năm trước, tôi đã ngừng việc xây dựng các bảng lịch sử riêng biệt. Thay vào đó, tôi sử dụng một quy ước đặt tên đơn giản, biến một bảng phẳng (flat table) thành một "ngăn xếp" (stack) lịch sử có tốc độ truy xuất cực nhanh.

Cách làm như sau:

Khi một trạng thái thay đổi (ví dụ: thuyền viên rời tàu), đừng di chuyển dữ liệu đi đâu cả. Chỉ cần đổi tên Mã định danh (Primary Key) của họ:

Mã_Gốc $\rightarrow$ Mã_Gốc + "#" + Ngày_Cất_Kho (YYYYMMDD)

Tại sao cách này lại "ăn đứt" mọi giải pháp khác?


  • Lịch sử trong nháy mắt: Bạn muốn xem toàn bộ lịch sử của nhân viên TV_101? Không cần những câu lệnh truy vấn phức tạp. Chỉ cần lọc (filter) cột Mã với điều kiện:
TV_101#*

Dấu sao (*) sẽ ngay lập tức gọi ra mọi "mẩu giấy" lịch sử về người đó theo đúng thứ tự thời gian.

  • Không cần di chuyển dữ liệu: Không còn việc sao chép dòng từ bảng này sang bảng kia. Không còn nỗi lo về hàm "Push/Pop" (đẩy vào/lấy ra). Mọi thứ nằm yên một chỗ, bất biến và vẹn nguyên.
  • Giao diện luôn sạch sẽ: Các màn hình làm việc hiện hành của bạn chỉ cần tìm các mã "sạch" (không có dấu #). Toàn bộ "bóng ma" của quá khứ (các bản ghi có dấu #) sẽ ẩn mình ở phía sau cho đến khi bạn cần gọi chúng lên.
Bài học rút ra

Chúng ta thường quá phức tạp hóa vấn đề bằng những cấu trúc quan hệ rắc rối. Nhưng đôi khi, giải pháp "đẳng cấp" nhất lại chỉ là một mẹo xử lý chuỗi ký tự thông minh và một dấu đại diện (wildcard).

Đừng ghi đè lên hiện tại. Hãy xếp chồng quá khứ lên nhau. Bản thân bạn (và những người kiểm toán sau này) sẽ phải thầm cảm ơn vì quyết định đó.
 
... Chỉ cần đổi tên Mã định danh (Primary Key) của họ:

Mã_Gốc $\rightarrow$ Mã_Gốc + "#" + Ngày_Cất_Kho (YYYYMMDD)
...
Bài này đọc thấy sao sao ấy nhỉ...
Primary Key mà đổi được thì còn gì là toàn vẹn dữ liệu???
Thông thường khi thiết kế thì phải dự kiến trước các trường hợp để tạo sẳn các trường (field) dữ liệu. Ví dụ: bảng Danh sách nhân viên thì ngoài cột [ngày vào cty] còn có thêm cột [ngày nghỉ việc]; bảng danh mục hàng hoá thì ngoài cột [giá bán], còn có cột [ngày áp dụng] để có thể truy vấn lịch sử giá bán theo thời điểm...

Truy vấn nhân viên đã nghỉ:
Mã:
SELECT TenNV FROM tblDSNhanVien WHERE [NgayNghiViec] Is Not Null;
 
Primary key mà sửa đổi. (?)
Tôi làm thế này:
nhân viên có Key cố định và có tên.
Tiếp nhận, thử việc, ký hợp đồng, nghỉ việc, ... là phải có chứng từ và là loại 1.
Luân chuyển phòng ban, thăng chức, thăng lương, ... cũng phải có chứng từ và là loại 2
Nhưng không phải mỗi chứng từ là 1 bảng (table dữ liệu). Tôi thiết kế chỉ 1 bảng dữ liệu với các trường:
- Loại chứng từ, đánh ký hiệu hoặc số
- Ngày hiệu lực.
- Nếu loại 2, thì có 1 cột thông tin mới (phòng ban mới, chức vụ mới, mức lương mới).
Khi kết hợp ngày hiệu lực và thông tin mới là có 1 bản liệt kê lịch sử chi tiết cho từng yêu cầu: Lịch sử làm việc, lịch sử lương, lịch sử thăng tiến, ...
Cái gọi là "bóng ma lịch sử" không hề tồn tại trong bảng danh sách nhân viên nên không thể nào có mặt trên giao diện, không cần loại bỏ bằng filter # với * (đọc gần như lọc trăng với sao).
Nếu áp dụng đổi primary key kiểu này, thì hàng chục giao diện đều phải lọc trước khi hiển thị. Thế thì thời gian load chứng từ lên giao diện chậm có làm hài lòng người dùng không?

Còn cái mục bị gán cho người ta là "phải tìm và di chuyển dữ liệu cũ sang bảng lịch sử rồi mới ghi dữ liệu mới vào bảng hiện tại" là sự gán ghép vô căn cứ. Tôi chưa thấy ai làm tệ đến mức đó.
 

Bài viết mới nhất

Back
Top Bottom