Mục lục bài họcĐang ở d08-b4
SQL Queries, Joins, Indexes and Database Security
The anatomy of a query. SELECT names the columns wanted; FROM names the table; WHERE filters rows with a condition; ORDER BY sorts the result; GROUP BY collapses rows sharing a value so that aggregate functions — COUNT, SUM, AVG, MAX, MIN — can be applied per group, and HAVING filters those groups. Read a query in execution order, not written order: FROM and JOIN build the working table first, WHERE removes rows, GROUP BY and HAVING collapse and filter, ORDER BY sorts, and SELECT picks columns last. Most exam errors come from confusing WHERE (filters rows before grouping) with HAVING (filters groups after).
Joins. Normalisation splits data across tables; a join reassembles it. A foreign key in one table holds the primary key of another, and JOIN ... ON matches rows where they are equal. An inner join keeps only matching pairs — a student with no enrolments does not appear; a left join keeps every row of the left table, with empty values where there is no match, which is how you list students including those with no enrolments. Joining three tables chains two ON conditions. The join is what makes normalisation workable: data is stored once and reassembled on demand.
Indexes. Without an index, finding rows with a given value means scanning the whole table. An index is a separate sorted structure (typically a tree) on one column that lets the database jump to matching rows, so a search on an indexed column is logarithmic instead of linear. The cost: every insert, update or delete must also update the index, so writes slow down, and each index takes storage. The rule is to index columns used in WHERE and JOIN on tables that are read far more than written, and not to index everything.
Security and integrity. A database concentrates an organisation's most valuable data, so it needs layered protection: access rights per user or role (read-only for reporting, no access to salary tables for most staff); encryption of sensitive columns and of backups; audit logs of who read or changed what; backups stored off-site and tested by restoring them; and validation rules in the schema (types, ranges, foreign keys) so that bad data is rejected at the door. The classic attack is SQL injection: a program builds a query by pasting in text the user typed, so a user who types a fragment of SQL gets it executed with the program's rights. The defence is parameterised queries, which send user input as data that can never be interpreted as code. Finally, a centralised database gives a single version of the truth — but is also a single point of failure and a single target, which is the standard evaluation point.
Cấu trúc một truy vấn. SELECT nêu các cột muốn lấy; FROM nêu bảng; WHERE lọc dòng bằng một điều kiện; ORDER BY sắp kết quả; GROUP BY gộp các dòng có cùng giá trị để các hàm tổng hợp — COUNT, SUM, AVG, MAX, MIN — áp dụng theo nhóm, và HAVING lọc các nhóm đó. Đọc truy vấn theo thứ tự thực thi, không theo thứ tự viết: FROM và JOIN dựng bảng làm việc trước, WHERE bỏ dòng, GROUP BY và HAVING gộp và lọc, ORDER BY sắp, và SELECT chọn cột sau cùng. Phần lớn lỗi thi tới từ nhầm WHERE (lọc dòng trước khi gộp) với HAVING (lọc nhóm sau khi gộp).
Phép nối. Chuẩn hoá tách dữ liệu ra nhiều bảng; phép nối ghép lại. Khoá ngoại ở một bảng giữ khoá chính của bảng khác, và JOIN ... ON khớp các dòng có giá trị bằng nhau. Nối trong chỉ giữ các cặp khớp — học sinh chưa đăng ký môn nào không xuất hiện; nối trái giữ mọi dòng của bảng trái, với giá trị rỗng ở chỗ không khớp, và đó là cách liệt kê học sinh kể cả người chưa đăng ký. Nối ba bảng thì nối chuỗi hai điều kiện ON. Phép nối là thứ làm chuẩn hoá vận hành được: dữ liệu lưu một lần và ghép lại khi cần.
Chỉ mục. Không có chỉ mục, tìm các dòng có một giá trị nghĩa là quét cả bảng. Chỉ mục là một cấu trúc riêng đã sắp (thường là cây) trên một cột, cho phép cơ sở dữ liệu nhảy thẳng tới dòng khớp, nên tìm trên cột có chỉ mục là logarit thay vì tuyến tính. Cái giá: mỗi lần thêm, sửa hay xoá phải cập nhật cả chỉ mục nên ghi chậm đi, và mỗi chỉ mục tốn lưu trữ. Quy tắc là đánh chỉ mục các cột dùng trong WHERE và JOIN trên các bảng đọc nhiều hơn ghi, và không đánh chỉ mục mọi thứ.
Bảo mật và toàn vẹn. Cơ sở dữ liệu tập trung dữ liệu quý nhất của tổ chức, nên cần bảo vệ nhiều lớp: quyền truy cập theo người dùng hoặc vai trò (chỉ đọc cho báo cáo, không thấy bảng lương với đa số nhân viên); mã hoá các cột nhạy cảm và bản sao lưu; nhật ký kiểm tra ai đọc hay đổi gì; sao lưu cất ngoài máy và được kiểm bằng cách khôi phục thử; và quy tắc kiểm tra trong lược đồ (kiểu, khoảng, khoá ngoại) để dữ liệu xấu bị chặn ngay cửa. Tấn công kinh điển là SQL injection: chương trình dựng truy vấn bằng cách dán thẳng chữ người dùng gõ, nên người gõ một mảnh SQL sẽ được thực thi nó với quyền của chương trình. Cách phòng là truy vấn tham số hoá, gửi đầu vào người dùng như dữ liệu không bao giờ bị diễn giải thành mã. Cuối cùng, cơ sở dữ liệu tập trung cho một bản sự thật duy nhất — nhưng cũng là một điểm hỏng và một mục tiêu duy nhất, và đó là điểm đánh giá chuẩn.
Với hai bảng Student(StudentID, Name, Year) và Enrolment(StudentID, Course, Mark): (a) tên và điểm của mọi học sinh khối 12 có điểm từ 70 trong bất kỳ môn nào, sắp giảm dần theo điểm; (b) điểm trung bình mỗi môn, chỉ hiện môn có trung bình trên 75; (c) danh sách mọi học sinh kể cả người chưa đăng ký môn nào.
(a) Nối, lọc, sắp.
SELECT Name, Course, Mark FROM Student JOIN Enrolment ON Student.StudentID = Enrolment.StudentID WHERE Year = 12 AND Mark ≥ 70 ORDER BY Mark DESC.
Thứ tự thực thi: dựng bảng nối, giữ dòng khối 12 và điểm từ 70, sắp giảm dần, chọn ba cột. Với dữ liệu trong hình: Chi Maths 91, An Maths 78.
(b) Gộp và lọc nhóm.
SELECT Course, AVG(Mark) FROM Enrolment GROUP BY Course HAVING AVG(Mark) > 75.
Điểm cốt lõi: điều kiện trung bình trên 75 áp lên nhóm sau khi gộp nên phải dùng HAVING; viết WHERE AVG(Mark) là lỗi vì WHERE chạy trước khi gộp, lúc chưa có trung bình nào. Với dữ liệu trong hình: Maths trung bình 84,5 hiện; Physics 65 bị loại.
(c) Nối trái.
SELECT Name, Course FROM Student LEFT JOIN Enrolment ON Student.StudentID = Enrolment.StudentID.
Nối trong sẽ bỏ Bình vì không có dòng đăng ký khớp; nối trái giữ mọi dòng của bảng trái, và Bình xuất hiện với Course rỗng. Câu hỏi kể cả người chưa là dấu hiệu nhận biết nối trái. Muốn chỉ liệt kê người chưa đăng ký thì thêm WHERE Course IS NULL.
Điểm giám khảo tìm. Điều kiện ON đúng cặp khoá; phân biệt WHERE và HAVING; chọn đúng loại nối theo câu hỏi; và kiểm kết quả trên dữ liệu nhỏ.
(a) Trang đăng nhập dựng truy vấn bằng cách ghép chuỗi: SELECT * FROM Users WHERE name = '[tên gõ]' AND password = '[mật khẩu gõ]'. Một người gõ tên là: admin' -- và bất kỳ mật khẩu nào, rồi vào được tài khoản admin. Giải thích và sửa. (b) Truy vấn tìm đơn hàng theo mã khách trên bảng 20 triệu dòng mất 40 giây; đề xuất và nêu cái giá.
(a) Cơ chế. Sau khi ghép, truy vấn thành: SELECT * FROM Users WHERE name = 'admin' -- ' AND password = '...'. Trong SQL, hai dấu gạch ngang bắt đầu một chú thích tới hết dòng, nên toàn bộ phần kiểm mật khẩu bị bỏ qua; điều kiện còn lại chỉ là name = 'admin', đúng, và người đó vào với quyền admin. Nguyên nhân gốc: chương trình để dữ liệu người dùng gõ trở thành mã vì nó được dán thẳng vào câu lệnh. Biến thể nguy hiểm hơn có thể xoá bảng hoặc đọc toàn bộ dữ liệu.
Sửa. Dùng truy vấn tham số hoá: câu lệnh được gửi với chỗ trống, SELECT * FROM Users WHERE name = ? AND password = ?, và giá trị người dùng gõ được gửi riêng như dữ liệu; cơ sở dữ liệu không bao giờ diễn giải chúng thành SQL, nên chuỗi admin' -- chỉ là một cái tên không tồn tại. Kèm theo: không lưu mật khẩu dạng chữ mà lưu băm, và tài khoản ứng dụng chỉ có quyền tối thiểu để kể cả khi bị tấn công cũng không xoá được bảng.
(b) Chẩn đoán. 40 giây trên 20 triệu dòng là quét toàn bảng: không có chỉ mục trên cột mã khách. Tạo chỉ mục trên CustomerID; tìm kiếm thành logarit, vài mili giây. Cái giá: mỗi đơn hàng mới phải cập nhật chỉ mục nên chèn chậm hơn một chút, và chỉ mục tốn lưu trữ. Với bảng đơn hàng — đọc nhiều hơn ghi rất nhiều — cái giá đáng trả. Cần nói thêm: không đánh chỉ mục mọi cột, chỉ cột dùng trong WHERE và JOIN, vì một bảng có mười chỉ mục ghi rất chậm.
Bẫy 2 — dùng nối trong khi câu hỏi nói kể cả người không có. Nối trong bỏ dòng không khớp; cần nối trái.
Bẫy 3 — nói chỉ mục luôn tốt. Ghi chậm đi và tốn lưu trữ; chỉ đánh cột lọc và nối trên bảng đọc nhiều.
Đọc xong rồi — làm thử ngay
Bài tập của chương Option: Object-Oriented Programming and Databases gồm 14 câu trắc nghiệm và 5 đề tự luận. Đáp án hiện ngay khi chọn, miễn phí.