Chế độ xem (InterBase và Firebird)
Alexey Kovyazin, cập nhật lần cuối 13-tháng 4-2012
Những ai quen thuộc với ngôn ngữ SQL không cần giải thích chi tiết về chủ đề này, nhưng để giữ trình tự trình bày, chúng tôi sẽ giới thiệu một định nghĩa ngắn gọn về view.
VIEW là một bảng ảo được tạo ra dựa trên truy vấn đến các bảng thông thường. Một view được triển khai như một truy vấn, được lưu trữ trên máy chủ và thực thi mỗi khi chúng ta tham chiếu đến view đó.
Hãy xem xét các biến thể khác nhau của việc sử dụng view. View cho phép tạo ra các cấp độ của cấu trúc dữ liệu, cho phép tách biệt việc triển khai lưu trữ dữ liệu khỏi loại dữ liệu của chúng. Ví dụ, chúng ta có thể tạo một view chọn dữ liệu từ nhiều bảng. Nếu khách hàng sử dụng view này thay vì tham chiếu trực tiếp đến các bảng cơ sở, nhà phát triển cơ sở dữ liệu sẽ có thể thay đổi truy vấn nền tảng của view, sửa đổi nó (ví dụ, để tối ưu hóa), và khách hàng sẽ không nhận thấy gì - đối với họ nó vẫn là view đó. Ngoài việc cô lập việc triển khai lưu trữ dữ liệu khỏi người dùng, view cho phép tổ chức dữ liệu theo cách thuận tiện và đơn giản hơn. Vấn đề “đơn giản hóa” cấu trúc dữ liệu nảy sinh khi số lượng bảng trong cơ sở dữ liệu trở nên đủ lớn và mối quan hệ giữa chúng trở nên phức tạp. View cho phép loại bỏ (hoặc ngược lại, thêm vào) một phần dữ liệu không cần thiết cho một khách hàng cơ sở dữ liệu cụ thể (hoặc - cần thiết).
Ngoài ra, view cho phép tổ chức bảo mật trong cơ sở dữ liệu InterBase đơn giản hơn. Một số người dùng có thể chỉ có quyền đọc / cập nhật dữ liệu trong view, nhưng không có quyền (và thậm chí không biết) về các bảng nền tảng của view! Để biết thêm chi tiết về bảo mật trong InterBase, xem chương “Bảo mật trong InterBase: người dùng, chức năng và quyền của họ” (phần 4).
Cú pháp DDL để làm việc với view
Bây giờ chúng ta sẽ xem xét các lệnh tạo và xóa view được định nghĩa bởi DDL (Data Definition Language - tập con của SQL, xem thuật ngữ). Để tạo một view trong InterBase, chúng ta nên sử dụng câu lệnh có cú pháp sau:
CREATE VIEW viewname [(view_column[, view_column…])] AS [WITH CHECK OPTION]; Ở đây viewname là tên của view phải là duy nhất trong cơ sở dữ liệu, và sau đó là một nhóm các tên trường không phải lúc nào cũng bắt buộc có trong view: [(view_column [, view_column …])]. Điều cần thiết là xác định câu lệnh chọn dữ liệu có trong view. Chúng ta sẽ thảo luận về tham số tùy chọn WITH CHECK OPTION một chút sau trong phần “View có thể sửa đổi”.
Để thay đổi view, chúng ta sẽ phải tạo lại nó, tức là xóa và tạo mới. Khi xóa view, cần phải xóa tất cả các đối tượng phụ thuộc - các trigger, stored procedure và các view khác. Đây là một trong những bất tiện chính khi làm việc với view: sự cần thiết phải tạo lại cây đối tượng sử dụng view (có các tiện ích cho phép chúng ta làm điều này dễ dàng hơn, ví dụ IBAlterView, xem ứng dụng “Công cụ quản trị và thiết kế InterBase”). Chúng ta nên sử dụng lệnh DDL sau nếu muốn xóa view:
DROP VIEW viewname;
Ví dụ về view
Đây là một ví dụ về view đơn giản:
CREATE VIEW MyView AS SELECT NAME, PRICE_1 FROM Table_example;
Trong ví dụ này, chúng ta tạo một view dựa trên truy vấn đến bảng Table_example mà chúng ta đã xem xét trong chương “Bảng. Khóa chính và bộ sinh”. Trong trường hợp này, view sẽ bao gồm hai trường - NAME và PRICE_1, được chọn từ bảng Table_example mà không có điều kiện nào, tức là số lượng bản ghi trong view MyView sẽ bằng số lượng bản ghi trong Table_example. Tuy nhiên, view không phải lúc nào cũng đơn giản như vậy. Chúng có thể dựa trên dữ liệu từ nhiều bảng và thậm chí dựa trên các view khác. Ngoài ra, view có thể chứa dữ liệu nhận được dựa trên các biểu thức khác nhau - bao gồm cả dựa trên các hàm tổng hợp. Để xem xét việc sử dụng view này chi tiết hơn, hãy tạo hai bảng được liên kết bằng quan hệ một-nhiều (thường được gọi là master-detail). Đây là tập lệnh DDL để tạo các bảng này:
/\* Table: WISEMEN */
CREATE TABLE WISEMEN ( ID_WISEMAN INTEGER NOT NULL, WISEMAN_NAME VARCHAR(80));
/\* Primary keys definition */
ALTER TABLE WISEMEN ADD CONSTRAINT PK_WISEMEN PRIMARY KEY (ID_WISEMAN);
/\* Table: WISEBOOK */
CREATE TABLE WISEBOOK ( ID_BOOK INTEGER NOT NULL, ID_WISEMAN INTEGER, BOOK VARCHAR(80));
/\* Primary keys definition */
ALTER TABLE WISEBOOK ADD CONSTRAINT PK_WISEBOOK PRIMARY KEY (ID_BOOK);
/\* Foreign keys definition */
ALTER TABLE WISEBOOK ADD CONSTRAINT FK_WISEBOOK FOREIGN KEY (ID_WISEMAN) REFERENCES WISEMEN (ID_WISEMAN);
Vậy là chúng ta đã tạo hai bảng - WISEMEN và WISEBOOK được liên kết bằng quan hệ master-detail sử dụng ràng buộc khóa ngoại - FOREIGN KEY. Giả sử các bảng này sẽ lưu trữ thông tin về các nhà hiền triết vĩ đại của Trung Quốc và các tác phẩm của họ. Bây giờ chúng ta có thể tạo một vài view dựa trên các bảng này. Ví dụ, hãy tạo view hiển thị mỗi nhà hiền triết có bao nhiêu tác phẩm:
CREATE VIEW WiseBookCount (WISEMAN, HOW_WISEBOOKS) AS SELECT M.WISEMAN_NAME, COUNT(B.BOOK) FROM WISEMEN M, WISEBOOK B WHERE (M.ID_WISEMAN = B.ID_WISEMAN) GROUP BY M.WISEMAN_NAME
Chú ý rằng khi sử dụng bất kỳ biểu thức tính toán nào như các hàm tổng hợp COUNT (), SUM (), MAX (), v.v., điều cần thiết là sử dụng tên xác định của các trường view, tức là đặt tên cho tất cả các trường được trả về bởi truy vấn. Như chúng ta có thể thấy từ ví dụ này, các tên này không nhất thiết phải trùng với tên các trường của truy vấn, nhưng số lượng của chúng phải trùng với số lượng trường được trả về bởi truy vấn. Việc xác định trường nào được trả về bởi truy vấn tương ứng với trường nào của view được thực hiện theo số thứ tự - trường truy vấn đầu tiên sẽ được phản ánh vào trường đầu tiên của view, trường thứ hai - vào trường thứ hai, v.v.
Và nếu chúng ta muốn biết nhà hiền triết nào đã viết nhiều sách nhất? Chúng ta sẽ thử thêm biểu thức sắp xếp - ORDER BY vào truy vấn nền tảng của view. Tuy nhiên, nỗ lực này sẽ không thành công: việc sử dụng sắp xếp ORDER BY trong view không được phép và khi cố gắng tạo view với truy vấn chứa ORDER BY, lỗi sẽ xảy ra. Nếu chúng ta muốn sắp xếp kết quả được trả về bởi view, chúng ta sẽ phải thực hiện điều đó thay mặt cho khách hàng:
SELECT * FROM WiseBookCount ORDER BY HOW_WISEBOOKS
Việc thực thi truy vấn SQL này sẽ dẫn đến kết quả mong muốn. Ngoài hạn chế sử dụng biểu thức ORDER BY trong view, chúng ta cũng không thể sử dụng tập dữ liệu nhận được từ việc thực thi stored procedure làm nguồn dữ liệu (xem chương “Stored procedures” bên dưới).
Có lẽ, đáng để đưa ra một ví dụ nữa minh họa việc áp dụng view. Giả sử chúng ta phải xuất ra danh sách các nhà hiền triết có tên bắt đầu bằng chữ “K”. Trong trường hợp này, chúng ta sẽ sử dụng view có điều kiện:
CREATE VIEW WiseMen2 (WISEMAN) AS SELECT M.WISEMAN_NAME FROM WISEMEN M WHERE M.WISEMAN_NAME LIKE ‘K%’
Vì vậy, thật dễ dàng để tạo các view đóng vai trò là nhà cung cấp dữ liệu có thể cập nhật liên tục, chọn dữ liệu từ cơ sở dữ liệu theo các điều kiện xác định.
View có thể sửa đổi
Chúng ta đã đề cập ở trên rằng có khả năng tạo các view dữ liệu có thể sửa đổi. Điều đó thực sự đúng - có khả năng không chỉ đọc dữ liệu từ view mà còn sửa đổi chúng!
Có hai cách để làm cho view có thể sửa đổi. Cách đầu tiên được áp dụng khi view được tạo dựa trên một bảng duy nhất (hoặc một view có thể sửa đổi khác) và tất cả các cột của bảng đã cho phải cho phép sự hiện diện của NULL. Do đó, truy vấn mà view dựa trên không thể chứa subquery, hàm tổng hợp, UDF, stored procedure, câu lệnh DISTINCT và HAVING. Nếu tất cả các điều kiện này được đáp ứng, view tự động trở thành có thể sửa đổi, tức là chúng ta có thể thực thi các truy vấn DELETE, INSERT và UPDATE cho nó, điều này sẽ thay đổi dữ liệu trong bảng nguồn.
Danh sách các điều kiện khá ấn tượng và hạn chế rất nhiều việc áp dụng các view có thể sửa đổi như vậy, do đó chúng được sử dụng khá hiếm.
Để tạo một view có thể sửa đổi vi phạm bất kỳ điều kiện nào được liệt kê ở trên, cơ chế trigger được áp dụng. Để biết thêm chi tiết về trigger, xem chương “Triggers” (phần 1). Bây giờ chúng ta sẽ chỉ xem xét các nguyên tắc chung của việc tổ chức thay đổi dữ liệu trong VIEW.
Để triển khai view cập nhật bằng trigger, cần phải thực hiện những điều sau. Tạo 3 trigger cho view đã cho cho các sự kiện: BEFORE DELETE, BEFORE UPDATE và BEFORE INSERT. Mô tả trong các trigger này những gì nên được thực hiện với dữ liệu khi xóa, cập nhật và chèn.
Sau đó, chúng ta nên sử dụng view đã cho trong các truy vấn sửa đổi - DELETE, INSERT hoặc UPDATE. Khi InterBase nhận được truy vấn này, nó sẽ kiểm tra xem có các trigger thích hợp cho view đã cho hay không, tức là BEFORE DELETE/INSERT/UPDATE. Nếu trigger cho hành động có thể thực thi tồn tại, InterBase sẽ gọi nó để sửa đổi dữ liệu thực trong các bảng nền tảng của view (mặc dù nó có thể là dữ liệu khác - không có ràng buộc văn bản nào cho các trigger này), và sau đó sẽ đọc lại chuỗi (hoặc các chuỗi) mà việc sửa đổi đã được thực hiện trên đó.
Vì vậy, có khả năng thực hiện các chuỗi cập nhật dữ liệu phức tạp trong view.
Tùy chọn WITH CHECK OPTION đã được đề cập trong mô tả cú pháp tạo view. Nếu tùy chọn này được đặt khi tạo một view có thể sửa đổi, mọi chuỗi dữ liệu được chèn hoặc thay đổi trong view này sẽ được kiểm tra theo điều kiện đưa vào view. Điều này có thể được giải thích như sau: nếu một bản ghi mới được người dùng chèn hoặc nhận được từ việc cập nhật bản ghi hiện có không thỏa mãn các điều kiện của truy vấn, là nhà cung cấp dữ liệu cho VIEW, việc chèn bản ghi này sẽ bị hủy bỏ và lỗi sẽ xảy ra.
Kết luận
Mặc dù việc tạo và sử dụng view có vẻ đơn giản, chúng cung cấp khả năng tuyệt vời để cải thiện việc tổ chức dữ liệu trong cơ sở dữ liệu và cho phép tạo ra một hệ thống phân cấp tổ chức dữ liệu.
Một số nhà thiết kế ứng dụng cơ sở dữ liệu sử dụng view rất thường xuyên trong công việc của họ, những người khác tránh áp dụng chúng, biện minh bằng sự phức tạp của việc sửa đổi view và xu hướng giữ cho lược đồ cơ sở dữ liệu đơn giản và hiệu quả nhất có thể. Việc bạn sẽ áp dụng view như thế nào trong công việc của mình là tùy thuộc vào bạn. Điều quan trọng nhất là nhớ về sự tồn tại của một công cụ mạnh mẽ như view và biết cách sử dụng nó.