Có các bảng tham chiếu trong cơ sở dữ liệu của bạn không phải là vấn đề lớn, phải không? Bạn chỉ cần gắn mã hoặc ID với mô tả cho từng loại tham chiếu. Nhưng điều gì sẽ xảy ra nếu bạn thực sự có hàng chục và hàng chục bảng tham chiếu? Có một giải pháp thay thế cho phương pháp tiếp cận mỗi loại một bảng không? Đọc tiếp để khám phá chung và có thể mở rộng thiết kế cơ sở dữ liệu để xử lý tất cả dữ liệu tham chiếu của bạn.
Sơ đồ trông khác thường này là một cái nhìn tổng quan về mô hình dữ liệu lôgic (LDM) chứa tất cả các loại tham chiếu cho hệ thống doanh nghiệp. Đó là từ một tổ chức giáo dục, nhưng nó có thể áp dụng cho mô hình dữ liệu của bất kỳ loại tổ chức nào. Mô hình càng lớn, bạn càng có nhiều khả năng khám phá ra nhiều loại tham chiếu.
Theo loại tham chiếu, ý tôi là dữ liệu tham chiếu hoặc giá trị tra cứu hoặc - nếu bạn muốn nhanh - phân loại . Thông thường, các giá trị được xác định ở đây được sử dụng trong danh sách thả xuống trong giao diện người dùng của ứng dụng của bạn. Chúng cũng có thể xuất hiện dưới dạng tiêu đề trên một báo cáo.
Mô hình dữ liệu cụ thể này có khoảng 100 kiểu tham chiếu. Hãy phóng to và xem chỉ hai trong số chúng.
Từ sơ đồ lớp này, chúng ta thấy rằng tất cả các kiểu tham chiếu đều mở rộng Root_Reference_Type
. Trên thực tế, điều này chỉ có nghĩa là tất cả các loại tham chiếu của chúng tôi có cùng thuộc tính từ Alt_Sequence
qua Type_Key
bao gồm, như được hiển thị bên dưới.
Thuộc tính | Mô tả |
---|---|
Alt_Sequence | Được sử dụng để xác định một trình tự thay thế khi yêu cầu một thứ tự không phải chữ cái. |
Description | Mô tả loại. |
Effective_Period | Xác định hiệu quả xem mục nhập tham chiếu có được bật hay không. Khi một tham chiếu đã được sử dụng, nó không thể bị xóa do các ràng buộc tham chiếu; nó chỉ có thể bị vô hiệu hóa. |
| Cái tên đẹp cho loại này. Đây là những gì người dùng nhìn thấy trên màn hình. |
Type_Key | KEY nội bộ duy nhất cho loại. Điều này bị ẩn với người dùng nhưng các nhà phát triển ứng dụng có thể sử dụng rộng rãi tính năng này trong SQL của họ. |
Loại hình tiệc ở đây là tổ chức hoặc cá nhân. Các loại giới tính là nam và nữ. Vì vậy, đây là những trường hợp thực sự đơn giản.
Giải pháp Bảng Tham chiếu Truyền thống
Vậy chúng ta sẽ triển khai mô hình logic trong thế giới vật lý của một cơ sở dữ liệu thực tế như thế nào?
Chúng ta có thể coi rằng mỗi loại tham chiếu sẽ ánh xạ tới bảng của riêng nó. Bạn có thể gọi đây là một bàn cho mỗi lớp truyền thống hơn sự hòa tan. Nó đủ đơn giản và trông giống như sau:
Mặt trái của điều này là có thể có hàng chục và hàng chục bảng này, tất cả đều có các cột giống nhau, tất cả đều hoạt động giống nhau.
Hơn nữa, chúng tôi có thể đang tạo ra nhiều công việc phát triển hơn nữa . Nếu một giao diện người dùng cho mỗi loại được yêu cầu để quản trị viên duy trì các giá trị, thì khối lượng công việc sẽ nhanh chóng nhân lên. Không có quy tắc cứng và nhanh cho việc này - nó thực sự phụ thuộc vào môi trường phát triển của bạn - vì vậy, bạn sẽ cần nói chuyện với các nhà phát triển của mình để hiểu điều này có tác động gì.
Nhưng do tất cả các kiểu tham chiếu của chúng ta có cùng thuộc tính hoặc cột, có cách nào chung hơn để triển khai mô hình dữ liệu logic của chúng ta không? Có, có! Và nó chỉ yêu cầu hai bảng .
Giải pháp hai bảng
Cuộc thảo luận đầu tiên mà tôi từng có về chủ đề này là vào giữa những năm 90, khi tôi đang làm việc cho một công ty bảo hiểm ở Thị trường Luân Đôn. Hồi đó, chúng tôi đi thẳng vào thiết kế vật lý và chủ yếu sử dụng khóa tự nhiên / nghiệp vụ, không phải ID. Khi dữ liệu tham chiếu tồn tại, chúng tôi quyết định giữ một bảng cho mỗi loại bao gồm một mã duy nhất (VARCHAR PK) và một mô tả. Trên thực tế, lúc đó có ít bảng tham chiếu hơn rất nhiều. Thường xuyên hơn không, một bộ mã nghiệp vụ hạn chế sẽ được sử dụng trong một cột, có thể với một ràng buộc kiểm tra cơ sở dữ liệu được xác định; sẽ không có bảng tham chiếu nào cả.
Nhưng trò chơi đã tiếp diễn kể từ đó. Đây là giải pháp hai bàn có thể trông giống như:
Như bạn có thể thấy, mô hình dữ liệu vật lý này rất đơn giản. Nhưng nó hoàn toàn khác với mô hình logic, và không phải vì một cái gì đó đã trở thành hình quả lê. Đó là vì một số thứ đã được thực hiện như một phần của thiết kế vật lý .
reference_type
bảng đại diện cho từng lớp tham chiếu riêng lẻ từ LDM. Vì vậy, nếu bạn có 20 loại tham chiếu trong LDM của mình, bạn sẽ có 20 hàng siêu dữ liệu trong bảng. reference_value
bảng chứa các giá trị được phép cho tất cả các loại tham chiếu.
Vào thời điểm của dự án này, đã có một số cuộc thảo luận khá sôi nổi giữa các nhà phát triển. Một số ưa thích giải pháp hai bảng và những người khác ưa thích một bàn cho mỗi loại phương pháp.
Có những ưu và khuyết điểm cho mỗi giải pháp. Như bạn có thể đoán, các nhà phát triển chủ yếu quan tâm đến khối lượng công việc mà giao diện người dùng sẽ thực hiện. Một số người nghĩ rằng việc kết hợp giao diện người dùng quản trị với nhau cho mỗi bảng sẽ khá nhanh chóng. Những người khác nghĩ rằng việc xây dựng một giao diện người dùng quản trị duy nhất sẽ phức tạp hơn nhưng cuối cùng đã thành công.
Trong dự án cụ thể này, giải pháp hai bàn đã được ưa chuộng. Hãy xem xét nó chi tiết hơn.
Mẫu dữ liệu tham chiếu có thể mở rộng và linh hoạt
Khi mô hình dữ liệu của bạn phát triển theo thời gian và các loại tham chiếu mới là bắt buộc, bạn không cần phải tiếp tục thực hiện các thay đổi đối với cơ sở dữ liệu của mình cho từng loại tham chiếu mới. Bạn chỉ cần xác định dữ liệu cấu hình mới. Để thực hiện việc này, bạn thêm một hàng mới vào reference_type
và thêm danh sách các giá trị cho phép được kiểm soát của nó vào reference_value
bảng.
Một khái niệm quan trọng có trong giải pháp này là xác định khoảng thời gian hiệu quả cho các giá trị nhất định. Ví dụ:tổ chức của bạn có thể cần nắm bắt một reference_value
'Bằng chứng về ID' sẽ được chấp nhận vào một ngày nào đó trong tương lai. Vấn đề đơn giản là thêm reference_value
với effective_period_from
ngày được đặt chính xác. Điều này có thể được thực hiện trước. Cho đến khi đến ngày đó, mục nhập mới sẽ không xuất hiện trong danh sách thả xuống các giá trị mà người dùng ứng dụng của bạn nhìn thấy. Điều này là do ứng dụng của bạn chỉ hiển thị các giá trị hiện tại hoặc được bật.
Mặt khác, bạn có thể cần ngăn người dùng sử dụng một reference_value
. Trong trường hợp đó, chỉ cần cập nhật nó với effective_period_to
ngày được đặt chính xác. Khi ngày đó trôi qua, giá trị sẽ không còn xuất hiện trong danh sách thả xuống. Nó sẽ bị vô hiệu hóa kể từ thời điểm đó. Nhưng vì nó vẫn tồn tại về mặt vật lý dưới dạng một hàng trong bảng, nên tính toàn vẹn của tham chiếu được duy trì cho những bảng mà nó đã được tham chiếu.
Bây giờ chúng tôi đang làm việc trên giải pháp hai bảng, rõ ràng là một số cột bổ sung sẽ hữu ích trên reference_type
bàn. Những điều này chủ yếu tập trung vào các mối quan tâm về giao diện người dùng.
Ví dụ:pretty_name
trên reference_type
bảng đã được thêm vào để sử dụng trong giao diện người dùng. Sẽ rất hữu ích cho các đơn vị phân loại lớn sử dụng cửa sổ có chức năng tìm kiếm. Sau đó, pretty_name
có thể được sử dụng cho tiêu đề của cửa sổ.
Mặt khác, nếu danh sách giá trị thả xuống là đủ, pretty_name
có thể được sử dụng cho lời nhắc LOV. Theo cách tương tự, mô tả có thể được sử dụng trong giao diện người dùng để điền trợ giúp cuộn qua.
Xem qua loại cấu hình hoặc siêu dữ liệu đi vào các bảng này sẽ giúp làm rõ mọi thứ một chút.
Cách quản lý tất cả điều đó
Trong khi ví dụ được sử dụng ở đây rất đơn giản, các giá trị tham chiếu cho một dự án lớn có thể nhanh chóng trở nên khá phức tạp. Vì vậy, có thể nên duy trì tất cả những điều này trong một bảng tính. Nếu vậy, bạn có thể sử dụng chính bảng tính để tạo SQL bằng cách nối chuỗi. Điều này được dán vào các tập lệnh, được thực thi dựa trên cơ sở dữ liệu đích hỗ trợ vòng đời phát triển và cơ sở dữ liệu sản xuất (trực tiếp). Điều này cung cấp cho cơ sở dữ liệu tất cả các dữ liệu tham khảo cần thiết.
Đây là dữ liệu cấu hình cho hai loại LDM, Gender_Type
và Party_Type
:
PROMPT Gender_Type INSERT INTO reference_type (id, pretty_name, ref_type_key, description, id_range_from, id_range_to) VALUES (rety_seq.nextval, 'Gender Type', 'GENDER_TYPE', ' Identifies the gender of a person.', 13000000, 13999999); INSERT INTO reference_value (id, pretty_name, description, effective_period_from, alt_sequence, reference_type_id) VALUES (13000010,'Female', 'Female', TRUNC(SYSDATE), 10, rety_seq.currval); INSERT INTO reference_value (id, pretty_name, description, effective_period_from, alt_sequence, reference_type_id) VALUES (13000020,'Male', 'Male', TRUNC(SYSDATE), 20, rety_seq.currval); PROMPT Party_Type INSERT INTO reference_type (id, pretty_name, ref_type_key, description, id_range_from, id_range_to) VALUES (rety_seq.nextval, 'Party Type', 'PARTY_TYPE', A controlled list of reference values that identifies the type of party.', 23000000, 23999999); INSERT INTO reference_value (id, pretty_name, description, effective_period_from, alt_sequence, reference_type_id) VALUES (23000010,'Organisation', 'Organisation', TRUNC(SYSDATE), 10, rety_seq.currval); INSERT INTO reference_value (id, pretty_name, description, effective_period_from, alt_sequence, reference_type_id) VALUES (23000020,'Person', 'Person', TRUNC(SYSDATE), 20, rety_seq.currval);
Có một hàng trong reference_type
cho mỗi loại phụ LDM của Root_Reference_Type
. Mô tả trong reference_type
được lấy từ mô tả lớp LDM. Đối với Gender_Type
, điều này sẽ là "Xác định giới tính của một người". Các đoạn mã DML cho thấy sự khác biệt trong mô tả giữa loại và giá trị, có thể được sử dụng trong giao diện người dùng hoặc trong báo cáo.
Bạn sẽ thấy rằng reference_type
được gọi là Gender_Type
đã được phân bổ phạm vi từ 13000000 đến 13999999 cho reference_value.ids
được liên kết của nó . Trong mô hình này, mỗi reference_type
được cấp phát một dải ID duy nhất, không trùng lặp. Điều này không hoàn toàn cần thiết, nhưng nó cho phép chúng tôi nhóm các ID giá trị liên quan lại với nhau. Nó giống như những gì bạn sẽ nhận được nếu bạn có các bảng riêng biệt. Thật tuyệt khi có, nhưng nếu bạn không nghĩ rằng có lợi ích nào trong việc này thì bạn có thể phân bổ nó.
Một cột khác đã được thêm vào PDM là admin_role
. Đây là lý do tại sao.
Quản trị viên là ai
Một số đơn vị phân loại có thể có các giá trị được thêm vào hoặc bị loại bỏ với ít hoặc không có tác động. Điều này sẽ xảy ra khi không có chương trình nào sử dụng các giá trị trong logic của chúng hoặc khi kiểu không được giao tiếp với các hệ thống khác. Trong những trường hợp như vậy, quản trị viên người dùng sẽ an toàn khi cập nhật những thông tin này.
Nhưng trong những trường hợp khác, cần phải chăm sóc nhiều hơn. Giá trị tham chiếu mới có thể gây ra những hậu quả không mong muốn đối với logic của chương trình hoặc đối với hệ thống hạ lưu.
Ví dụ:giả sử chúng ta thêm phần sau vào phân loại Loại giới tính:
INSERT INTO reference_value (id, pretty_name, description, effective_period_from, alt_sequence, reference_type_id) VALUES (13000040,'Not Known', 'Gender has not been recorded. Covers gender of unborn child, when someone has refused to answer the question or when the question has not been asked.', TRUNC(SYSDATE), 30, (SELECT id FROM reference_type WHERE ref_type_key = 'GENDER_TYPE'));
Điều này nhanh chóng trở thành vấn đề nếu chúng ta có sẵn logic sau:
IF ref_key = 'MALE' THEN RETURN 'M'; ELSE RETURN 'F'; END IF;
Rõ ràng, logic “nếu bạn không phải là nam thì bạn phải là nữ” không còn được áp dụng trong phép phân loại mở rộng.
Đây là nơi chứa admin_role
cột đi vào hoạt động. Nó được sinh ra từ các cuộc thảo luận với các nhà phát triển về thiết kế vật lý và nó hoạt động cùng với giải pháp giao diện người dùng của họ. Nhưng nếu giải pháp một bảng cho mỗi lớp đã được chọn, thì reference_type
sẽ không tồn tại. Siêu dữ liệu mà nó chứa sẽ được mã hóa cứng vào ứng dụng Gender_Type
table -, không linh hoạt và cũng không thể mở rộng.
Chỉ những người dùng có đặc quyền chính xác mới có thể quản lý phân loại. Điều này có thể dựa trên kiến thức chuyên môn về chủ đề ( SME ). Mặt khác, một số đơn vị phân loại có thể cần được quản lý bởi CNTT để cho phép phân tích tác động, kiểm tra kỹ lưỡng và để mọi thay đổi mã được phát hành một cách hài hòa kịp thời cho cấu hình mới. (Việc này được thực hiện theo yêu cầu thay đổi hay theo cách khác là tùy thuộc vào tổ chức của bạn.)
Bạn có thể đã lưu ý rằng các cột kiểm tra created_by
, created_date
, updated_by
và updated_date
hoàn toàn không được tham chiếu trong tập lệnh trên. Một lần nữa, nếu bạn không quan tâm đến những thứ này, bạn không cần phải sử dụng chúng. Tổ chức cụ thể này có một tiêu chuẩn bắt buộc phải có các cột kiểm toán trên mọi bảng.
Kích hoạt:Giữ mọi thứ nhất quán
Trình kích hoạt đảm bảo rằng các cột kiểm tra này được cập nhật nhất quán, bất kể nguồn của SQL là gì (tập lệnh, ứng dụng của bạn, cập nhật hàng loạt theo lịch trình, cập nhật đặc biệt, v.v.).
-------------------------------------------------------------------------------- PROMPT >>> create REFERENCE_TYPE triggers -------------------------------------------------------------------------------- CREATE OR REPLACE TRIGGER rety_bri BEFORE INSERT ON reference_type FOR EACH ROW DECLARE BEGIN IF (:new.id IS NULL) THEN :new.id := rety_seq.nextval; END IF; :new.created_by := function_to_get_user(); :new.created_date := SYSDATE; :new.updated_by := :new.created_by; :new.updated_date := :new.created_date; END rety_bri; / CREATE OR REPLACE TRIGGER rety_bru BEFORE UPDATE ON reference_type FOR EACH ROW DECLARE BEGIN :new.updated_by := function_to_get_user(); :new.updated_date := SYSDATE; END rety_bru; / -------------------------------------------------------------------------------- PROMPT >>> create REFERENCE_VALUE triggers -------------------------------------------------------------------------------- CREATE OR REPLACE TRIGGER reva_bri BEFORE INSERT ON reference_value FOR EACH ROW DECLARE BEGIN IF (:new.type_key IS NULL) THEN -- create the type_key from pretty_name: :new.type_key := function_to_create_key(new.pretty_name); END IF; :new.created_by := function_to_get_user(); :new.created_date := SYSDATE; :new.updated_by := :new.created_by; :new.updated_date := :new.created_date; END reva_bri; / CREATE OR REPLACE TRIGGER reva_bru BEFORE UPDATE ON reference_value FOR EACH ROW DECLARE BEGIN -- once the type_key is set it cannot be overwritten: :new.type_key := :old.type_key; :new.updated_by := function_to_get_user(); :new.updated_date := SYSDATE; END reva_bru; /
Nền tảng của tôi chủ yếu là Oracle và thật không may, Oracle giới hạn số nhận dạng ở mức 30 byte. Để tránh vượt quá điều này, mỗi bảng được cung cấp một bí danh ngắn gồm ba đến năm ký tự và các hiện vật liên quan đến bảng khác sử dụng bí danh đó trong tên của chúng. Vì vậy, reference_value
Bí danh của là reva
- hai ký tự đầu tiên của mỗi từ. Trước khi chèn hàng và trước khi cập nhật hàng được viết tắt thành bri
và bru
tương ứng. Tên trình tự reva_seq
, v.v..
Các trình kích hoạt mã hóa thủ công như thế này, hết bảng này đến bảng khác, đòi hỏi các nhà phát triển phải thực hiện rất nhiều công việc phân loại nồi hơi. May mắn thay, những trình kích hoạt này có thể được tạo thông qua tạo mã , nhưng đó là chủ đề của một bài viết khác!
Tầm quan trọng của các phím
ref_type_key
và type_key
cả hai cột đều được giới hạn ở 30 byte. Điều này cho phép chúng được sử dụng trong các truy vấn SQL kiểu PIVOT (trong Oracle. Các cơ sở dữ liệu khác có thể không có cùng giới hạn về độ dài số nhận dạng).
Vì tính duy nhất của khóa được đảm bảo bởi cơ sở dữ liệu và trình kích hoạt đảm bảo rằng giá trị của nó luôn không đổi, nên những khóa này có thể - và nên - được sử dụng trong các truy vấn và mã để làm cho chúng dễ đọc hơn . Ý tôi là gì? Tốt, thay vì:
SELECT … FROM … INNER JOIN … WHERE reference_value.id = 13000020
Bạn viết:
SELECT … FROM … INNER JOIN … WHERE reference_value.type_key = 'MALE'
Về cơ bản, khóa giải thích rõ ràng những gì truy vấn đang thực hiện .
Từ LDM sang PDM, với khả năng phát triển
Hành trình từ LDM đến PDM không nhất thiết phải là một con đường thẳng. Nó cũng không phải là một sự chuyển đổi trực tiếp từ cái này sang cái khác. Đó là một quy trình riêng biệt đưa ra những cân nhắc và mối quan tâm của chính nó.
Làm cách nào để bạn lập mô hình dữ liệu tham chiếu trong cơ sở dữ liệu của mình?