Ngôn ngữ truy vấn đối tƣợng (Object Query Language) là ngôn ngữ truy vấn đƣợc đề xuất cho mô hình hƣớng đối tƣợng ODMG. Nó đƣợc thiết kế làm việc chặt chẽ với các ngôn ngữ lập trình nhƣ C++, SmallTalk, và Java. Do đó, truy vấn OQL nhúng trong ngôn ngữ lập trình có thể trả về đối tƣợng phù hợp với hệ thống kiểu của ngôn ngữ lập trình đó. Ngoài ra, thực hiện phép toán lớp trong lƣợc đồ ODMG có mã đƣợc viết trong ngôn ngữ lập trình. Cú pháp truy vấn của OQL tƣơng tự cú pháp của SQL, bổ sung các khái niệm của ODMG nhƣ định danh, đối tƣợng phức tạp, phép toán, kế thừa, đa hình và liên kết [10].
Mục tiêu:
Cho phép truy cập dễ dàng tới cơ sở dữ liệu hƣớng đối tƣợng, có thể tích hợp trong C++, Java, Smalltalk,…
Cung cấp một truy cập phi thủ tục
Cố gắng giữ lại một phần của ngôn ngữ SQL
Phù hợp với chuẩn ODMG: cho phép truy vấn tới các tập và tạo ra kết quả là các litteral, đối tƣợng, tập.
Đảm bảo tính bao gói
Các ví dụ minh họa cho truy vấn OQL dựa trên lƣợc đồ cơ sở dữ liệu UNIVERSITY trong hình 1.6 và ví dụ 1.17.
1.3.3.1. Cấu trúc cơ bản, điểm vào cơ sở dữ liệu, và biến Iterator
Cú pháp cơ bản của OQL là select … from … where giống nhƣ SQL. Ví dụ, đƣa ra các tên khoa trong college là “Engineering”
Q0: SELECT d.dname
WHERE d.college = „Engineering‟; Trong đó departments là tên bền
Các câu lệnh trên tƣơng ứng với câu lệnh SQL: SELECT name
FROM departments as d
WHERE college =„Engineering‟;
Điểm vào cơ sở dữ liệu là cần cho mỗi truy vấn. Điểm vào cơ sở dữ liệu là tên đối tƣợng bền. Với nhiều truy vấn, điểm vào là tên extent của lớp. Quan sát tên extent trong hình 1.6, tên đối tƣợng departments là kiểu set<Department>; persons là kiểu set<Person>; faculty là kiểu set<Faculty>; ...
Trong truy vấn Q0 ta sử dụng tên extent là departments làm điểm vào cơ sở dữ liệu tham chiếu đến tập đối tƣợng bền. Mỗi khi tham chiếu tới tập trong truy vấn OQL, trong Q0 ta định nghĩa một biến bộ lặp (iterator) d để duyệt qua từng đối tƣợng từ tập hợp. Trong nhiều trƣờng hợp, nhƣ trong Q0, truy vấn sẽ lựa chọn các đối tƣợng từ tập dựa trên điều kiện chỉ ra trong mệnh đề WHERE. Trong Q0, chỉ các đối tƣợng bền d trong tập của departments thỏa mãn điều kiện d.college = „Engineering‟ đƣợc lựa chọn cho kết quả truy vấn. Đối với mỗi đối tƣợng đƣợc chọn d, giá trị d.dname đƣợc lấy về trong kết quả truy vấn. Do đó, kiểu kết quả lấy về về của Q0 là bag<string>, bởi vì với mỗi giá trị dname là một xâu (mặc dù trong thực tế kết quả là một tập vì dname là thuộc tính khóa). Nói chung, kết quả của truy vấn có kiểu bag cho lệnh select … from
và có kiểu set cho lệnh select distinct… from nhƣ trong SQL (thêm từ khóa distinct để loại trừ sự lặp lại).
1.3.3.2. Kết quả của một truy vấn và biểu thức đường dẫn
Kết quả của truy vấn có thể có kiểu bất kỳ trong mô hình đối tƣợng ODMG. Một truy vấn không nhất thiết phải có cấu trúc select… from …where…, trong trƣờng hợp đơn giản nhất, chính tên bền là một truy vấn, kết quả là một tham chiếu tới đối tƣợng bền đó. Ví dụ, yêu cầu tìm kiếm
Q1: departments;
Trả về một tham chiếu tới tập tất cả các đối tƣợng bền department, có kiểu là set<Department>.
Giả sử có một tên bền csdepartment cho một đối tƣợng đơn department, sau đó truy vấn là:
Q1a: csdepartment;
Kết quả là một tham chiếu đến đối tƣợng cụ thể kiểu Department.
Với một điểm vào cơ sở dữ liệu, biểu thức đƣờng dẫn đƣợc dùng để mô tả đƣờng dẫn đến thuộc tính và các đối tƣợng liên quan . Biểu thức đƣờng dẫn bắt đầu bằng tên đối tƣợng bền hoặc biến iterator dùng để duyệt qua đối tƣợng trong tập. Sau tên bền có thể không có hoặc có nhiều tên liên kết hoặc tên thuộc tính đƣợc ngăn cách bởi dấu chấm (.).
Nếu a là thuộc tính thì “o.a” là biểu thức đƣờng dẫn để truy cập đến giá trị thuộc tính a của đối tƣợng o.
Nếu r là thuộc tính quan hệ thì biểu thức đƣờng dẫn “o.r” thể hiện kết nối của mối quan hệ khi truy vấn. Biểu thức o.r có thể trả về một đối tƣợng hoặc một tập các đối tƣợng tùy thuộc vào kiểu của quan hệ r.
Với f là một hàm thì biểu thức “o.f” trả về giá trị hàm f của đối tƣợng o.
Ví dụ, trong cơ sở dữ liệu UNIVERSITY, các truy vấn sau là hợp lệ trong OQL: Q2: csdepartment.chair;
Q2a: csdepartment.chair.rank; Q2b: csdepartment.has_faculty;
Đầu tiên Q2 trả về đối tƣợng có kiểu Faculty, bởi vì đó là kiểu của thuộc tính chair trong lớp Department. Nó sẽ tham chiếu tới đối tƣợng Faculty liên quan tới đối tƣợng department có tên bền là csdepartment thông qua thuộc tính chair; có nghĩa là tham chiếu tới đối tƣợng Faculty là trƣởng khoa Khoa học máy tính.
Q2a cũng tƣơng tự, trả về hạng của đối tƣợng Faculty (trƣởng khoa Khoa học máy tính) mà không phải là tham chiếu tới đối tƣợng; vì vậy, kiểu trả về bởi Q2a là string, là kiểu dữ liệu của thuộc tính rank trong lớp Faculty.
Biểu thức đƣờng dẫn Q2 và Q2a trả về các giá trị đơn, bởi vì thuộc tính chair (của Department) và rank (của Faculty) đều là các giá trị đơn và áp dụng cho đối tƣợng đơn. Biểu thức Q2b thì khác, nó trả về đối tƣợng có kiểu set<Fculty> khi áp dụng cho đối tƣợng đơn, bởi vì đó là kiểu quan hệ has_faculty của lớp Department. Một tập trả về gồm các tham chiếu tới tất cả các đối tƣợng Faculty có quan hệ với đối tƣợng department có tên bền là csdepartment thông qua quan hệ has_faculty; có nghĩa là tham chiếu tới tất cả đối tƣợng Faculty làm việc trong khoa Khoa học máy tính.
Trở lại với việc xếp hạng các giảng viên khoa học máy tính. Ta không thể viết: Q3‟: csdepartment.has_faculty.rank;
Nguyên nhân là không rõ ràng đối tƣợng trả về có thể có kiểu set<string> hoặc bag<string> (kiểu thứ 2 có nhiều khả năng hơn, nhiều giảng viên có thể cùng hạng). Đây là vấn đề nhập nhằng, OQL không cho phép biểu thức nhƣ Q3‟. Thay vào đó, phải sử dụng biến iterator duyệt qua tập, nhƣ trong Q3a hoặc Q3b dƣới đây:
Q3a: select f.rank
from f in csdepartment.has_faculty; Q3b: select distinct f.rank
from f in csdepartment.has_faculty;
Tại đây, Q3a trả về bag<string> (có thể trùng lặp giá trị rank trong kết quả), trong khi Q3b set<string> (sự trùng lặp đƣợc loại bỏ thông qua từ khóa distinct). Cả hai Q3a và Q3b minh họa cách định nghĩa biến iterator trong mệnh đề from vƣợt qua phạm vi hạn chế tập mô tả trong truy vấn. Biến f trong Q3a và Q3b duyệt qua các phần tử của tập csdepartment.has_faculty có kiểu set<Faculty> chỉ gồm các giảng viên làm việc trong khoa Khoa học máy tính.
1.3.3.3.Truy vấn trả về các cấu trúc phức tạp
Nói chung, một truy vấn OQL có thể trả về kết quả có cấu trúc phức tạp đƣợc xác định trong yêu cầu tìm kiếm sử dụng từ khóa struct. Xét hai ví dụ sau:
Q4: csdepartment.chair.advises;
Đƣa ra tập các sinh viên (set<GradStudent>) đƣợc trƣởng khoa khoa Khoa học máy tính hƣớng dẫn
Q4a: select struct (name : struct(last_name: s.name.lname, first_name: s.name.fname), degrees:(select struct (deg: d.degree,
yr: d.year, college: d.college)
from d in s.degrees)
from s in csdepartment.chair.advises;
Truy vấn này đƣa ra (lname, fname, degrees) của các sinh viên do trƣởng khoa Khoa học máy tính hƣớng dẫn.
Biến s duyệt qua tập các sinh viên đƣợc hƣớng dẫn trƣởng khoa, và biến d duyệt qua degree của các sinh viên. Kiểu của kết quả trả về của Q4 là một tập (mức 1) cấu trúc, trong đó mỗi cấu trúc có hai thành phần name và degrees. Thành phần name có cấu trúc gồm last_name và first_name, mỗi thành phần có kiểu xâu ký tự. Thành phần degrees đƣợc định nghĩa bởi một truy vấn nhúng và bản thân nó là một tập (mức 2) cấu trúc, gồm 3 thành phần là xâu ký tự: deg, yr, và college.
1.3.3.4. Sắp xếp dữ liệu đưa ra
Chú ý rằng OQL là trực giao (orthogonal) đối với biểu thức đƣờng dẫn. Có nghĩa là các thuộc tính, quan hệ, và tên phép toán (phƣơng thức) có thể hoán đổi cho nhau trong biểu thức đƣờng dẫn, miễn là kiểu hệ thống của OQL không bị ảnh hƣởng. Ví dụ, có thể sử dụng truy vấn dƣới đây lấy điểm trung bình của tất cả các sinh viên học ở khoa Khoa học máy tính, kết quả gồm first_name, last_name, gpa và đƣợc sắp xếp theo gpa
Q5a: select struct (last_name: s.name.lname, first_name: s.name.fname, gpa: s.gpa)
from s in csdepartment.has_majors
where s.class = „senior‟
order by gpa desc, last_name asc, first_name asc;
Đƣa ra first_name, last_name, gpa của các sinh viên học ở khoa Khoa học máy tính, lớp senior. gpa sắp xếp giảm dần, first_name, last_name sắp xếp tăng dần.
Q5b: select struct (last_name: s.name.lname, first_name: s.name.fname, gpa: s.gpa)
from s in students
where s.majors_in.dname = „Computer Science‟ and s.class = „senior‟
Q5a dùng tên csdepartment làm điểm vào cơ sở dữ liệu để tham chiếu đến khoa Khoa học máy tính, sau đó xác định các sinh viên thông qua quan hệ has_majors, trong khi đó Q5b tìm kiếm extent students để định vị tất cả sinh viên chuyên ngành trong khoa. Các tên thuộc tính, tên quan hệ, và tên phép toán có thể hoán đổi vị trí cho nhau trong biểu thức đƣờng dẫn: gpa là phép toán; majors_in và has_majors là các quan hệ; và lớp, name, dname, lname, và fname là các thuộc tính. Phép toán gpa tính điểm trung bình và giá trị trả về có kiểu float cho mỗi sinh viên đƣợc chọn. Mệnh đề order by giống nhƣ của câu lệnh SQL. Tập trả về bởi truy vấn sử dụng mệnh đề order by có kiểu list.
1.3.3.5. Định nghĩa khung nhìn
Cơ chế khung nhìn trong OQL đƣợc sử dụng để định nghĩa tên truy vấn. Từ khóa
define đƣợc dùng để đặt tên cho truy vấn. Tên phải là duy nhất. Nếu tên trùng với tên
một truy vấn đã tồn tại thì định nghĩa mới sẽ thay thế định nghĩa trƣớc đó. Sau khi định nghĩa, truy vấn đƣợc định nghĩa đó là bền cho tới khi định nghĩa lại hoặc xóa. Một khung nhìn cũng có thể có các tham số. Ví dụ, định nghĩa khung nhìn V1 là tên một truy vấn has_minors lấy ra tập đối tƣợng sinh viên học chứng chỉ phụ (minoring) trong khoa:
V1: define has_minors(deptname) as select s
from s in students
where s.minors_in.dname = deptname;
Vì lƣợc đồ ODL trong ví dụ 1.17 chỉ cung cấp thuộc tính liên kết một hƣớng minors_in cho Student, ta có thể dùng khung nhìn ở trên mô tả liên kết ngƣợc mà không cần phải định nghĩa mỗi quan hệ. Khung nhìn kiểu này có thể dùng để mô tả mối quan hệ ngƣợc mà không muốn sử dụng nó thƣờng xuyên. Định nghĩa nhƣ sau:
has_minors(„Computer Science‟);
trả về bag chứa các sinh viên học chứng chỉ phụ trong khoa Khoa học máy tính.
Lƣu ý rằng, trong hình 12.6 không định nghĩa has_minors nhƣ mối quan hệ vì có thể nó đƣợc sử dụng thƣờng xuyên.
1.3.3.6. Lấy ra các phần tử đơn từ các tập đơn
Nói chung, một truy vấn trả về tập kết quả nhƣ bag, set (nếu distinct đƣợc dùng), hoặc list (nếu order by đƣợc dùng). Nếu ngƣời dùng yêu cầu một truy vấn chỉ trả về một phần tử đơn, phép toán element trong OQL trả về phần tử đơn e từ tập đơn (singleton collection) c chỉ chứa một phần tử. Nếu c chứa nhiều hơn một phần tử hoặc
c rỗng thì phép toán element gọi một biệt lệ.
Ví dụ, Q6 trả về đối tƣợng đơn tham chiếu tới khoa Khoa học máy tính: Q6: element (select d
where d.dname = „Computer Science‟);
Khi tên department là duy nhất trong các departments, kết quả là một department. Kiểu của kết quả là d:Department.
1.3.3.7. Phép toán nhóm (min, max, count, sum, avg)
Nhiều truy vấn coi các tập nhƣ kết quả, một số toán tử đƣợc định nghĩa để áp dụng cho các tập nhƣ vậy.
Các phép toán nhóm min, max, count, sum, và avg hoạt động trên các tập. Toán tử count trả về kiểu số nguyên. Các toán tử min, max, avg trả về kiểu giống kiểu của toán hạng của tập hợp.
Q7: count (s in has_minors(„Computer Science‟));
Q7 đƣa ra số lƣợng sinh viên học chứng chỉ phụ trong khoa Khoa học máy tính Q8: avg (select s.gpa
from s in students
where s.majors_in.dname = „Computer Science‟
and s.class = „senior‟);
Q8 đƣa trung bình các gva của các sinh viên chính quy trong khoa Computer Science. Phép toán nhóm áp dụng cho tập có kiểu thích hợp và dùng đƣợc trong bất kỳ phần nào của truy vấn.
Q9: select d.dname
from d in departments
wherecount (d.has_majors) > 100; Q9 đƣa ra tên khoa có số sinh viên > 100
1.3.3.8. Các truy vấn có kết quả True, False
Các phép toán thành viên và định lƣợng trả về kiểu boolean, là true hoặc false. Giả sử v là biến, c là biểu thức tập hợp, b là biểu thức có kiểu logic (biểu thức điều kiện), e là phần tử cùng kiểu với phần tử trong tập c. Khi đó:
(e in c) trả về true nếu e là thành viên của c.
(for all v in c : b) trả về true nếu mọi v trong c thỏa mãn b. (exist v in c:b) trả về true nếu tồn tại v trong c thỏa mãn b.
Để minh họa cho điều kiện thành viên (membership), ta lấy về tên của tất cả các sinh viên hoàn thành khóa học „Database Systems I‟. Nhƣ trong Q10, truy vấn lồng trả về tập tên khóa học mà mỗi sinh viên đã hoàn thành, và điều kiện thành viên (membership) trả về true nếu „Database Systems I‟ có trong tập sinh viên s.
Q10:select s.name.lname, s.name.fname
from s in students
where „Database Systems I‟ in
(select c.cname
from c in s.completed_sections.section.of_course);
Q10 minh họa cách đơn giản trong mệnh đề where của truy vấn để đƣa ra tập cấu trúc, kiểu của Q10 là bag<struct(string, string)>.
Một cách viết truy vấn có kết quả trả về là true/false. Ví dụ, giả sử rằng một đối tƣợng có tên Jeremy có kiểu Student. Khi đó, truy vấn Q11 đặt ra câu hỏi "Có phải Jeremy là sinh viên ngành khoa học máy tính không?". Tƣơng tự nhƣ vậy, Q12 hỏi “Có phải tất cả sinh viên cao học ngành khoa học máy tính đƣợc hƣớng dẫn bởi khoa Khoa học máy tính không?”. Cả hai Q11 và Q12 trả về true hoặc false.
Q11: Jeremy in has_minors(„Computer Science‟); Q12: for all g in
(select s
from s in grad_students
where s.majors_in.dname = „Computer Science‟) : g.advisor in csdepartment.has_faculty;
Truy vấn Q12 minh họa cách thuộc tính, quan hệ, phép toán kế thừa áp dụng trong truy vấn. Mặc dù s là bộ lặp (iterator) để duyệt qua extent grad_students, ta có thể viết s.majors_in vì quan hệ majors_in đƣợc kế thừa bởi GradStudent từ Student thông qua EXTENDS (xem ví dụ 1.17).
Cuối cùng, minh họa cho phép toán định lƣợng, truy vấn Q13 trả lời cho câu hỏi: "Có phải các sinh viên tốt nghiệp ngành khoa học máy tính có điểm trung bình 4.0?". Ở đây, lại một lần nữa phép toán gpa đƣợc kế thừa bởi GradStudent từ Student thông qua EXTENDS.
Q13: exists g in (select s
from s in grad_students
where s.majors_in.dname = „Computer Science‟) : g.gpa = 4;
1.3.3.9. Sắp xếp biểu thức tập hợp
Nhƣ đã trình bày, tập là danh sách và mảng có bổ sung các phép toán nhƣ lấy về phần tử đầu tiên, phần tử cuối cùng hay phần tử thứ i. Ngoài ra, có phép toán dùng để trích ra tập con và nối hai danh sách. Do đó, biểu thức truy vấn gồm có danh sách hoặc mảng có thể gọi các phép toán này.
Truy vấn Q14 lấy về last name của ngƣời trong khoa có lƣơng cao nhất.: Q14: first (select struct(faculty: f.name.lname, salary: f.salary)
from f in faculty
order by f.salary desc);
Q14 minh họa việc sử dụng toán tử first trên danh sách tập chứa lƣơng của những ngƣời trong khoa đã đƣợc sắp xếp giảm dần. Do đó phần tử đầu tiên trong danh sách đã đƣợc sắp xếp tƣơng ứng với ngƣời có lƣơng cao nhất.
Q15, lấy về 3 đối tƣợng trên cùng dựa vào gpa.
Q15:(select struct(last_name: s.name.lname, first_name: s.name.fname, gpa: s.gpa)
from s in csdepartment.has_majors
Truy vấn select-from-order-by trả về danh sách sinh viên ngành khoa học máy tính sắp xếp giảm dần theo gpa. Phần tử đầu tiên trong tập có chỉ số là 0, do đó biểu thức [0:2] trả về danh sách chứa phần tử thứ nhất, thứ hai và thứ ba trong kết quả truy vấn.
1.3.3.10. Toán tử Group by
Mệnh đề group by trong OQL giống nhƣ trong SQL, cung cấp tham chiếu tới tập đối tƣợng trong mỗi nhóm (group) hoặc phân vùng (partition).
Q16 lấy về số ngành (majors) của các mỗi khoa. Trong truy vấn này, sinh viên đƣợc