Về mặt ngữ nghĩa thì View giống như là xem hoặc nhìn, nhưng ở khía cạnh hệ quản trị cơ sở dữ liệu thì View có tác dụng giống như tạo một Table ảo với các Fields và Records mà ta có thể tự định nghĩa và khác hoàn tòa với Table gốc.
View có đầy đủ các tính chất của một Table nên ta có thể truy vấn bằng cách sửu dụng các lệnh T-SQL trên nó, không những thế bạn có thể delete và tạo mới View thông qua một câu lệnh đơn giản.
Khi ta tạo một View từ một table thì nếu table đó đổi dữ liệu thì mặc nhiên View cũng sẽ thay đổi theo. Điều này thật tuyệt vời vì ta không mất nhiều công sức để cập nhật lại dữ liệu.
Để rõ hơn thì mình lấy một ví dụ thế này: Giả sử trang Frontend có một Block gọi là hiển thị 10 tin mới nhất, như vậy ta sẽ truy vấn lấy 10 tin và sắp xếp giảm dần theo ID. Nhưng bạn biết trong SQL nó sẽ duyệt toàn bộ bảng rồi mới trả về kết quả và điều này làm cho truy vấn trở nên chậm chạm.
Để giải quyết nó thì ta sẽ tạo một View gồm 10 tin mới nhất và lúc hiển thị ra chỉ cần lấy trong View nên tốc độ sẽ nhanh hơn rất nhiều lần.
+ Ưu điểm khi sử dụng View trong MySQL
Sau đây chúng ta sẽ thảo luận về những lợi ích khi sử dụng View nhé.
Khi sử dụng Database View sẽ giúp bạn đơn giản hóa các câu truy vấn phức tạp bởi vì một View
được tạo ra bởi một câu truy vấn SQL
, nên dữ liệu mà nó lưu trữ ta có thể gọi là thành phẩm của một công đoạn truy vấn nên giải quyết được nhiều vấn đề về tốc độ, đặc biệt là những câu truy vấn JOIN
. Bạn có thể sử dụng View để che giấu đi sự phức tạp của mô hình dữ liệu trong hệ thống bởi những gì mà họ thấy chỉ là môt View
rất đơn giản.
Database View giúp giới hạn dữ liệu cho người sử dụng, nghĩa là một View
sẽ có bấy nhiêu dữ liệu thôi và người dùng chỉ được thấy con số bấy nhiêu đó. Ngoài ra khi tạo View
bạn có thể gán quyền truy cập sử dụng cho một nhóm nào đó. Tôi lấy một ví dụ thực tế như sau, giả sử hệ thống của bạn có phần danh sách hóa đơn và bạn muốn những nhân viên trong bộ phận Kế Toán được phép xem các hóa đơn trong tháng này thôi. Như vậy bạn sẽ tạo một View
và dữ liệu của nó là danh sách hóa đơn của tháng hiện tại, sau đó gán quyền vào nhóm Kế Toán này.
Database View giúp tăng tính bảo mật hơn bởi vì View
chi đọc mà không ghi được (Read Only) nên việc hacker tấn công cập nhật dữ liệu là điều không thể.
Database View cho phép tăng hoặc giảm các Fields
tùy theo yêu cầu sử dụng bởi vì nó được tạo từ một câu truy vấn SELECT
nên bạn có thể JOIN
nhiều Table
lại với nhau và lưu vào View
.
Database View tăng khả năng phát triển lại ứng dụng hoặc tương thích với nhiều ứng dụng chạy chung một CSDL
. Ví dụ như bạn có một hệ thống dữ liệu khách hàng và một này nào đó bạn cần đổi cấu trúc thì lúc này trong Source
của bạn phải đổi theo. Nhưng nếu sử dụng View
thì ta có thể hạn chế được việc này.
+ Nhược điểm khi sử dụng View trong MYSQL
Ưu điểm có thì đương nhiên nhược điểm cũng phải có. Và sau đây là những nhược điểm của Database View.
Khi truy vấn trong View
có thể sẽ chậm hơn trong table
Bị phụ thuộc vào Table
gốc, nếu Table
gốc thay đổi cấu trúc thì đòi hỏi View
cũng phải thiết kế lại cho phù hợp
+ Database View trong MySql
MYSQL có hỗ trợ View
nhưng từ phiên bản 5.X
mới có và hầu hết những đặc tính của nó theo chuẩn View
của năm năm 2003.
MYSQL sẽ lưu file lưu trữ View
với tên là view_name.frm-00001
, sau này bạn cập nhật lại bố cục của nó thì nó sẽ lưu lại là view_name.frm-00002
và cứ tăng dần như thế.
Hãy bắt đầu bằng một ví dụ, điều này sẽ giúp bạn hiểu rõ vấn đề hơn.
Giả sử ta có 2 table gồm:
payments: Lưu lịch sử thanh toán của khách hàng
customers: Lưu danh sách khách hàng
Cấu trúc như hình sau:
Bây giờ hãy viết câu truy vấn lấy ra tất cả khách hàng và thông tin lịch sử thanh toán của khách hàng đó. Rất đơn giản, ta chỉ việc sử dụng lệnh Inner Join hai bảng.
|
Giả sử kết quả như sau:
Câu truy vấn đã trả về kết quả thành công. Nhưng vào một ngày kia ta cần lấy lại thông tin này thì bạn sẽ phải viết lại câu truy vấn này một lần nữa. Câu hỏi đặt ra là có giải pháp nào tối ưu cho những lần sau không?
Có một cách truyền thống đó là bạn lưu dữ liệu vào file word hoặc txt, thậm chí là file sql. Tuy nhiên như vậy thì kích thước file quá lớn, và ta rất khó để sàn lọc dữ liệu.
Cách tốt hơn đó là sử dụng View trong MySQL, đây là một table ảo nhưng có đầy đủ tính năng của một table thật. Hãy xem cú pháp tạo View ho trường hợp trên như sau:
|
Sau khi chạy câu truy vấn này thì trên hệ thống database xuất hiện thêm một View có tên là customerPayments. Đây là một table ảo nên bạn co thể viết câu truy vấn trên này một cách bình thường.
Vì vậy ở những lần sau nếu bạn muốn lấy dữ liệu thì chỉ việc chạy câu SQL này.
|
Thật tuyệt vời phải không các bạn! Những lần sau này truy vấn sẽ rất đơn giản.
Vì một View sẽ được lưu trữ trên ổ đĩa vật lý nên thực chất nó cũng là một table, vì vậy bạn có thể thực hiện truy vấn với câu Select. Tuy nhiên với lệnh Delete hoặc Update thì không thực hiện trên View được.
Áp dụng phương pháp này ta có thể tạo ra một View chứa dữ liệu từ nhiều bảng khác nhau. Hãy xem hình minh họa dưới đây:
Trong hình này ta tạo ra một view chứa liệu của cả 3 bảng bằng cách sử dụng lệnh Join để kết hợp đồng bộ dữ liệu giữa chúng.
Một ví dụ khác: Giả sử ta cần tạo ra một View chứa tất cả các ngày trong tuần thì viết như sau:
|
Sau đó muốn lấy dữ liệu trong tuần thì chạy câu SQL sau:
|
Kết quả:
Nếu sử dụng View trong quản lý database ta sẽ có những ưu thế sau.
- Đơn giản hóa truy vấn phức tạp
View giúp đơn giản hóa những câu truy vấn phức tạp cho những lần sử dụng sau, bở khi dữ liệu ở bảng chính thay đổi thì trong View cũng sẽ được thay đổi theo nên vấn đề đồng bộ dữ liệu rất chính xác.
- Làm giảm độ phức tạp tính toán
Giả sử bạn phải viết cùng một công thức cho mọi truy vấn. Hoặc bạn có một truy vấn có logic phức tạp. Để làm cho logic này nhất quán trên các truy vấn thì có thể sử dụng View để lưu trữ phép tính.
- Phân quyền và bảo mật
Với tính chất chỉ Xem chứ không được thay đổi dữ liệu nên khi muốn cho ai đó xem thì bạn có thể cho họ xem View thay vì xem Table.
Ngoài ra nó là một table nên bạn có thể giới hạn phân quyền cho table một cách dễ dàng.
Giả sử thông tin thanh toán của khách hàng thì chỉ có bộ phận kế toán thấy mà thôi, vì vậy ta có thể tạo ra một view chứa thông tin này và cấp quyền cho bộ phận kết toán.
Lệnh Create View sẽ tạo ra một View mới trong Database, sau đây là cú pháp:
|
Ý nghĩa của các từ khóa như sau:
Từ khóa Create View cho thấy bạn đang muốn tạo một View mới có tên là view_name, năm trong database db_name.
Nếu bạn dùng thêm từ khóa Or Replace thay thì nó sẽ thay thế View có tên là view_name bằng nội dung SQL mới. Nếu view_name chưa tồn tại thì nó sẽ tạo view mới.
Column_list là danh sách các cột sẽ được đặt tên lại. Nếu bạn không truyền vào thì nó sẽ lấy tên cột mặc định của các table.
Select-statement chính là câu lệnh Select trả về danh sách dữ liệu sẽ được lưu vào trong View. Bạn có thể sử dụng kết hợp các lệnh Join, Order By, Limit, ... miễn là nó trả về kết quả là danh sách dữ liệu.
Theo mặc định thì lệnh Create View sẽ tạo mới một view nằm trong DB hiện tại đang kết nối. Nhưng nếu bạn muốn chỉ định rõ DB nào thì sẽ đặt nó ở vị trí của db_name.
Cách sử dụng lệnh Create View
Hãy làm một vài ví dụ để hiểu rõ hơn lệnh này.
Ví dụ 1: Tạo một view đơn giản
Hãy xem cấu trúc của table orderdetails như trong hình dưới đây.
Bây giờ mình sẽ viết một View lưu trữ doanh thu của mỗi đơn hàng, bằng cách sử dụng lệnh Select kết hợp Group By.
|
Để kiểm tra View đã xuất hiện chưa thì hãy sử dụng lệnh sau:
|
Lệnh này sẽ hiển thị danh sách tất cả các table đang có trong DB hiện tai. Vì View cũng là table nên cũng sẽ xuất hiện trong đó.
Ngoài ra, để xem chi tiết đâu là table và đâu là view thì bạn sử dụng lệnh sau:
|
Kết quả lệnh này như sau:
Bây giờ nếu bạn muốn xem tổng doanh thu của mỗi đơn hàng thì chỉ cần truy vấn trong View SalePerOrder mà đã tạo ở trên.
|
Kết quả dạng như sau:
Ví dụ 2: Tạo mới một View từ một View khác
Nghe có vẻ kì lạ phải không? Thực tế bạn có thể tạo mới một view với dữ liệu được lấy từ một View khác.
Giả sử mình muốn lấy danh sách những đơn hàng có giá trị hơn 6000, lúc này chỉ cần lấy trong view salesPerOrder mà ta đã tạo ở trên là được.
|
Bây giờ dữ liệu trong view bigSalesOrder chính là những đơn hàng có doanh thu hơn 6000. Hãy thử chạy một câu truy vấn xem sao.
|
Ví dụ 3: Tạo View với lệnh Join
Hãy xem ví dụ dưới đây, mình tạo ra một view có tên là customerOrders để lưu trữ thông tin khách hàng và tổng số tiền mà khách hàng đã mua. Mình đã JOIN 3 bảng gồm: orderDetails
, orders
và customers
|
Bây giờ sử dụng lệnh Select trong view customerOrders
là được.
|
Kết quả tham khảo:
Ví dụ 4: Tạo View với truy vấn con
Trong ví dụ dưới đây mình có sử dụng cấu trúc truy vấn con để tạo ra View.
|
Lệnh Drop View sẽ xóa một view ra khỏi database, sau khi thực hiện bạn sẽ không thể phục hồi lại view đó được.
Cú pháp như sau:
|
Trong đó:
Để xóa nhiều view cùng một lúc thì ta sử dụng cú pháp sau:
|
Trong lệnh này nếu bạn không sử dụng từ khóa IF EXISTS thì chỉ cần một view không tồn tại là kết quả lênh SQL không được thực hiện, đồng nghĩa với việc không có view nào được xóa.
* Lưu ý: Phiên bản MySQL 5.7 trở về trước thì lại khác, nó trả về lỗi các view không tồn tại nhưng vẫn xóa những view tồn tai.
Cách dùng Drop View trong MySQL để xóa View
Hãy làm một vài ví dụ về xóa view để hiểu rõ hơn câu lệnh này.
Vi dụ 1: Xóa một view
Giả sử dùng lệnh sau để tạo view customerPayments
.
|
Sau đó mình dùng lệnh Drop View để xóa view vừa tạo đó.
|
Ví dụ 2: Xóa nhiều view cùng một lệnh
Cho hai table có cấu trúc như sau:
Bây giờ tạo mới một view có tên là employeeOffices
.
|
do chỉ mới tạo một view thôi, nên câu lệnh xóa 2 view dưới đây sẽ bị lỗi.
|
Lỗi thu được đó là:
|
Nhưng nếu ta thêm từ khóa IF EXISTS
:
|
Kết quả thu được:
|
Như vậy câu lệnh vẫn chạy thành công, chỉ là xuất hiện một cảnh báo lỗi.
Giả sử ta tạo thêm một view nữa dựa trên hai bảng sau:
|
Bây giờ chạy lênh Drop View để xóa hai views: productCatalogs và employeeOffices quá đơn giản.
|
Kết quả trả về thành công!
Bởi vì View và Table có chung cấu trúc và đặc tính nên bạn hoàn toàn có thể sử dụng lệnh RENAME TABLE
để thay đổi tên cho View.
Dưới đây là cú pháp đơn giản để đổi tên View:
|
Ví dụ: Tạo view có tên là productLineSales
|
Bây giờ mình muốn đổi tên từ productLineSales sang productLineQtySales thì cách làm như sau:
|
Muốn kiểm tra thì hãy dùng lệnh SHOW FULL TABLES để xem tên đã đổi chưa nhé.
|
Kết hợp Drop View và Create View để đổi tên View
Có một cách khác để đổi tên View trong MySQL đó là sử dụng lệnh Drop View để xóa view đó, sau đó sử dụng Create View để tạo lại View mới. Cách này nhìn chung hơi phiền hà, nhưng bạn có thể di chuyển view từ DB này sang DB khác.
Giả sử muốn đổi tên view từ productLineQtySales sang categorySales thì thực hiện các bước như sau:
Bước 1: Dùng lệnh SHOW CREATE VIEW để xem cấu trúc SQL của View đó.
|
Ta thu được kết quả như sau:
Hãy copy lại nội dung SQL ở cột Create View đã khoanh tròn và lưu vào file txt nhé.
Bước 2: Dùng lệnh Drop View để xóa view.
|
Bước 3: Dùng lệnh Create View để tạo view mới với nội dung đã lưu trong file txt .
|
Chắc hẳn bạn đã biết được khái niệm về Hàm trong PHP rồi nhỉ? Trong Mysql Procedure cũng có ý nghĩa tương tự như vậy, nghĩa là chúng ta sẽ tạo những hàm (Procedure) để thực hiện những dòng lệnh liên quan trong đó, ví dụ như thao tác Update hay Insert.
Thông thường một hàm có thể gọi lại chính nó và ta gọi là đệ quy (tham khảo đệ quy trong php). Nhưng trong MYSQL thì bạn hãy kiểm tra Version của bạn có hỗ trợ không nhé. Nếu có thì mình cũng nghĩ là không nên sử dụng bởi vì bản chất MYSQL không sử dụng để tính toán nhiều.
Với MYSQL thì khái niệm Stored Procedure chỉ được thêm vào kể từ Version 5 với mục đích là giúp việc xử lý dữ liệu linh hoạt và mạnh mẽ hơn.
+ Ưu điểm của Stored Proccedure
Thông thường chúng ta sử dụng Procedure để tăng hiệu xuất xử lý của ứng dụng, sau khi được tạo ra thì tất cả các thủ tục được lưu trong hệ quản trị cơ sở dữ liệu. Tuy nhiên trong MYSQL việc lưu trữ các hàm này lại hơi khác nhau, khi được tạo ra thì thủ tục này sẽ được lưu trữ trong một bộ nhớ đệm (cache). Nếu trong một ứng dụng sử dụng gọi tới một Procedure nhiều lần trong một chuỗi kết nối thì lúc này sẽ coi nó như một chương trình cần biên dịch, nếu không thì sẽ coi như như là một câu truy vấn bình thường.
Stored Procedure giúp giảm thời gian giao tiếp giữa các ứng dụng với hệ quản trị MYSQL, bởi vì thay vì gửi nhiều câu lệnh dài thì ta chỉ cần gọi tới một thủ tục và trong thủ tục này sẽ thực hiện nhiều câu lệnh SQL.
Stored Procudure sẽ giúp các ứng dụng nhìn minh bạch hơn, nghĩa là khi ta định nghĩa các thao tác xử lý vào một Stored thì công việc của các ngôn ngữ lập trình khác chỉ quan tâm đến tên thủ tục, các tham số truyenf vào chứ không cần biết nó thực hiện như thế nào. Điều này giúp các team làm việc tốt hơn, ta sẽ phân ra bộ phận Coder riêng và bộ phận viết thủ tục riêng.
Mỗi thủ tục sẽ có các mức độ truy cập, nghĩa là ta có thể cấp quyền sử dụng cho một Uesr nào đó trong hệ quản trị (Lưu ý là user trong hệ quản trị chứ không phải là admin của ứng dụng website).
+ Nhược điểm của Stored Procedure
Nếu bạn tạo ra quá nhiều Procedure thì hệ quản trị sẽ sử dụng bộ nhớ để lưu trữ các thủ tục này khá nhiều. Ngoài ra nếu bạn thực hiện quá nhiều xử lý trong mỗi thủ tục thì đồng nghĩa với việc CPU sẽ làm việc nặng hơn, điều này không tốt chút nào.
Nếu sử dụng thủ tục thì sẽ rất khó phát triển trong ứng dụng, gây khó khăn ở mức logic business.
Một số hệ quản trị CSDL có những tool hỗ trợ Debug Store nhưng MYSQL thì không có.
Để phát triển ứng dụng thì bạn phải đòi hỏi có một kỹ năng thật siêu đăng mà không phải nhà thiết kế cơ sở dữ liệu nào cũng có. Điều này dễ bị khó cho vấn đề bảo trì và nâng cấp.
Bạn tạo một Database mới với tên bất kì, sau đó dán đoạn code sau vào trình chạy câu truy vấn để tạo bảng products.
|
Câu lệnh này sẽ tạo mới một bảng và thêm vào hai record để ta test. Bây giờ chúng ta sẽ bắt đầu tìm hiểu cú pháp khai báo và sử dụng Stored Procedure trong MYSQL.
+ Tạo Mysql Stored Procedure đầu tiên
Trong tài liệu này sử dụng IDE SqlYog để quản lý CSDL , nên nếu muốn học thuận tiện thì bạn nên cài đặt và sử dụng phần mềm này luôn.
- Tạo Stored Procedure
VD: viết một Procedure với tên là GetAllProducts()
, nhiệm vụ của thủ tục này là truy vấn lấy danh sách tất cả sản phẩm nằm trong bảng Products. Nhưng trước tiên chúng ta tìm hiểu cú pháp khai báo tạo mới một Procedure như sau:
|
Trong đó:
DELIMITER $$
dùng để phân cách bộ nhớ lưu trữ thủ tục Cache và mở ra một ô lưu trữ mới. Đây là cú pháp nên bắt buộc bạn phải nhập như vậyCREATE PROCEDURE procedureName()
dùng để khai báo tạo một Procedure mới, trong đó procedureName chính là tên thủ tục còn hai từ đầu là từ khóa.BEGIN
và END; $$
dùng để khai báo bắt đầu của Procedure và kết thúc Procedure DELIMITER ;
Lưu ý là phải tuân theo cú pháp như trên nhé, ban chỉ việc thay đổi tên của thủ tục là ok.
Bây giờ để tạo mới một Procedure với tên là GetAllProduct thì chúng ta sẽ làm như sau:
|
Sau đó bạn chạy câu SQL này và nó báo thành công tức là bạn đã tạo mới một thủ tục với tên là GetAllProduct rồi đấy. Nếu như bạn sử dụng SqlYog thì bạn vào table Products và vào mục Stored Procs sẽ thấy một thủ tục vừa được tạo:
- Gọi Stored Procedure
Tạo xong rồi bây giờ làm thế nào để gọi đến Store này? Đơn giản để gọi tới Store nào thì ta chỉ cần dùng cú pháp như sau:
|
Như vậy để gọi tới Procedure tên là GetAllProducts thì ta làm như sau:
|
Chạy câu truy vấn này và bạn sẽ thấy kết quả như sau, đây chính là hai records mà ta đã thêm ở phần 1:
Xem danh sách Stored Procedure trong hệ thống
Nếu bạn không sử dụng SqlYog thì rất khó để quản lý Procedure vì không nhìn thấy được nó. Yên tâm vì trong MYSQL có hỗ trợ một số lệnh hiển thị dánh sách Stored Procedure bằng cách chạy lệnh sau:
|
Sau khi chạy lên bạn sẽ nhận được kết quả dạng như sau:
Đây chính là Procedure mà ta đã tạo ở trên.
- Sửa Stored Procedure đã tạo
Trong Mysql không cung cấp lệnh sửa Stored nên thông thường chúng ta sẽ chạy lệnh tạo mới. Tuy nhiên có một lưu ý đó là nếu như bạn đã chạy lệnh tạo Procedure một lần rồi, sau đó bạn edit và chạy lại thì ngay lập tức sẽ bị báo lỗi ngay vì trùng tên. Để giải quyết vấn đề này thì chúng ta sẽ dùng lệnh Drop để xóa đi Procedure đó và tạo lại:
|
Và một lưu ý nữa là khi bạn dùng với quyền User nào thì Store đó sẽ có quyền thực hiện trong phạm vu của User đó. Ví dụ bạn không có quyền edit mà bạn tạo mới một Procedure Edit thì khi chạy sẽ bị báo lỗi ngay. Chính vì vậy thông thường khi edit bạn phải thêm người định nghĩa nó như sau:
|
Dòng chữ DEFINER=`root`@`localhost`
chính là tên người đã tạo ra nó.
Để định nghĩa một biến mới ta dùng cú pháp :
|
Trong đó:
Ví dụ:
|
+ Gán giá trị cho biến
Tạo biến rồi thì phải gán giá trị cho nó chứ đúng không nào? Để gán giá trị cho nó thì chúng ta sử dụng tư khóa SET:
|
Ví dụ: Định nghĩa biến age và gán giá trị 20 cho nó.
|
Ví dụ: Gán giá trị thông qua lệnh SELECT
|
Trong ví dụ này thì trước tiên nó sẽ thực hiện câu truy vấn SQL đếm tổng số record và sau đó gán vào biến total_products
bằng lệnh (COUNT(*) INTO total_products
).
+ Phạm vi hoạt động của biến
Nếu như bạn định nghĩa một biến bên trong phần thân của Procedure (giữa BEGIN và END) thì đó ta gọi là biến cục bộ của Procedure. Bạn có thể định nghĩa nhiều biến trong một Procedure.
Ví dụ:
|
Ở procedure này tôi đã định nghĩa các biến a,b,tong
và tính toán trên đó. Bạn chạy câu sql trên và sau đó gọi nó bằng lệnh call tinhTong
thì nó sẽ cho kết quả là 500.
Nếu một biến được khai báo bên ngoài Procedure thì bên trong Procedure sẽ không nhận được và sẽ thông báo lỗi.
Ví dụ:
|
Chương trình này lỗi vì biến tong
không tồn tại trong Procedure.
+ Tạo Cơ sở dữ liệu Products
Bạn tạo mới một Database và chạy lệnh SQL tạo bảng sau lệnh này sẽ tạo một bảng products
và thêm hai record:
|
+ Các loại tham số trong Mysql Stored Procedure
Chúng ta có hai loại tham số chính trong Procedure đó là:
IN
: Đây là chế độ mặc định (nghĩa là nếu bạn không định nghĩa loại nào thì nó sẽ hiểu là IN). Khi bạn sử dụng mức này thì nó sẽ được bảo vệ an toàn, có nghĩa là sẽ không bị thay đổi nếu như trong Procedure có tác động đếnOUT
: Chế độ này nếu như trong Procedure có tác động thay đổi thì nó sẽ thay đổi theo. Nhưng có điều đặc biệt là dù trước khi truyền vào mà bạn gán giá trị cho biến đó thì vẫn sẽ không nhận được vì mặc định nó luôn hiểu giá trị truyền vào là NULL.INOUT
: Đây là sự kết hợp giữa IN
và OUT
. Nghĩa là có thể gán giá trị trước và có thể bị thay đổi nếu trong Procedure có tác động tớiVí dụ:
|
Nếu muốn truyền vào nhiều hơn một tham số thì ta sẽ ngăn cách nó bởi dấu phẩy. Ví dụ:
|
Thông thường chúng ta viết các tham số xuống hàng để nhìn đẹp hơn. Ví dụ:
|
- Tham số loại IN trong Mysql Stored Procedure
Như trình bày ở trên tham số này sẽ được bảo vệ và không bị thay đổi trong quá trình sử dụng trong Procedure.
Ví dụ: Viết Store lấy chi tiết sản phẩm theo ID
|
Chạy Procedure này:
|
Và ta có giao diện kết quả trả về:
- Tham số loại OUT trong Mysql Stored Procedure
Loại out nếu trong quá trình thực thi mà Procedure có tác động đến tham số này thì bên ngoài nó ảnh hưởng theo. Khi nhận tham số này thì Procedure sẽ hiểu đó là giá trị NULL nên dù bạn có gán giá trị cho biến trước khi truyền vào nó vẫn lấy NULL.
@title
Ví dụ: Truyền tham số title kiểu OUT
vào Procedure và đổi giá trị cho nó, sau đó bên ngoài Procedure hiển thị giá trị của title.
|
Bây giờ ta gọi Procedure này như sau:
|
Thì kết quả sẽ như sau:
Như vậy ra rút ra kết luận như sau:
OUT
mục đích là lấy dữ liệu trong Proedure và sử dụng ở bên ngoài.OUT
phải có chữ @
đằng trước biếnOUT
không cần định nghĩa trước, chính vì vậy khởi đầu nó có giá trị NULL- Tham số dạng INOUT trong Mysql Stored Procedure
INOUT
là sự kết hợp giữa IN
và OUT
, nghĩa là:
OUT
thì không thể được nhưng IN
thì được.IN
không được nhưng OUT
thì không được.Ví dụ: Tạo Procedure
|
Gọi sử dụng:
|
Và kết quả là 2.
Nhưng nếu ta dùng dạng OUT
thì kết quả sẽ là NULL
. Lý do là bên trong có tăng lên 1 nhưng nó lấy giá trị truyền vào dạng OUT
là NULL
nên 1 + NULL
sẽ là NULL
.
+ Tìm hiểu mệnh đề if else trong MySql
Mệnh đề if cho phép bạn tạo luồng xử lý rẻ nhánh, nếu đúng thì thực thì và ngược lại mệnh đề sai thì nó sẽ không thực thi. Thông thường chúng ta kết hợp các toán tử, toán hạng và biến trong mysql để tạo ra các mệnh đề đúng sai trong điều kiện của lệnh IF
. Không những chỉ có IF
mà ta có thể sử dụng mệnh đề IF ELSE
trong MYSQL
cũng được.
Cú pháp:
|
Luồng đi như sau:
if_expression
đúng thì nó sẽ thực thi câu lệnh bên dưới nó, ngược lại nó bỏ qua và nhảy xuống IFELSEIFELSE
, nếu mệnh đề này đúng thì nó xử lệnh bên dưới, ngươc lại thì nó bỏ qua và nhảy tiếp xuống dưới.ELSE
nên thực thi luôn chứ không cần kiểm tra điều kiện nữa.Lưu ý với bạn là ta có thể có nhiều IFELSE chứ không phải chỉ 1 cái như trong ví dụ trên.
+ Ví dụ mềnh đề if else trong MySql Stored Procedure
Bây giờ ta sẽ làm một ví dụ cho bạn dễ hiểu hơn. Trước tiên ta cần tạo một bảng thành viên và insert một số thông tin Username và Password. Bạn chạy lệnh sau để tạo bảng:
|
Trong bảng này ta cần chú ý đến field us_level
như sau:
us_level = 0
=> tài khoản bị khóaus_level = 1
=> adminus_level = 2
=> memberBây giờ ta viết Procedure đăng nhập với yêu cầu như sau:
us_level = 0
=> tài khoản bị khóaus_level = 1
=> là adminus_level = 2
=> là memberÝ tưởng:
username
và password
thuộc loại IN
, còn result
thuộc loại OUT
để lấy sử dụng. us_level
của người dùng, giá trị khởi tạo của nó là -1
. Sau khi thực hiện lệnh SELECT
nếu giá trị flag = -1
tức là không tồn tại username
và password
trong CSDL, ngược lại thì ta sẽ check flag
để trả về kết quả tương ứng.Bài giải:
|
Sử dụng:
|
|
Trong đó:
WHEN
dịch tiếng anh nghĩa là KHI, tức là KHI MỆNH ĐỀ ĐIỀU KIỆN ĐÚNG thì sẽ thực thi câu lệnh bên trong nó.ELSE
chính là trường hợp cuối cùng nếu như các điều kiện WHEN
ở trên không đúng thì nó sẽ được chạy. Nếu để ý kĩ thì bạn thấy đây chính là lệnh default
trong Switch Case của các ngôn ngữ lập trình khác.Lệnh CASE
được ứng dụng rất nhiều nơi nhưng điển hình vẫn là trong Procedure. Tuy nhiên nó vẫn không bằng lệnh IF ELSE
bởi tính linh hoạt của nó không cao. Để các bạn rõ hơn thì ta sẽ làm một ví dụ như sau:
Ví dụ: Viết Stored Procedure nhập vào một số từ 0 tới 9 và đọc số đó thành chữ. Yêu cầu sử dụng lệnh CASE
trong MYSQL
|
Chạy thủ tục:
|
Ngoài cách sử dụng ở phần 2 thì lệnh CASE còn thường hay sử dụng trong câu lệnh SELECT. Ví dụ khi bạn truy vấn lấy danh sách khách hàng và bạn muốn nếu field gender = 1
thì kết quả là nam và ngược lại kết quả là nữ. Để làm được thì ta phải sử dụng lệnh CASE trong MYSQL. Để dễ hiểu hơn bây giờ ta làm một bài tập nhỏ như sau:
Giả sử ta có bảng member như sau:
|
Trong đó bạn cần chú ý đến field us_gender
như sau:
us_gender = 1
=> NamOk, câu lệnh chúng ta như sau:
|
Và kết quả như hình:
Như vậy lệnh CASE
trong câu SELECT
hơi khác chút xíu đó là chỉ có lệnh END
thay vì END CASE
như trong Procedure.Các bạ lưu ý điểm này để viết câu truy vấn cho đúng nhé.
+ Cú pháp vòng lặp while trong MYSQL
Ta có cú pháp vòng lặp while như sau:
|
Nếu biểu thức expression có giá trị là sai thì vòng lặp sẽ dừng, ngược lại thì vòng lặp sẽ được thực hiện. Chính vì vậy khi sử dụng bạn cẩn thận nếu như bị lặp vô hạn thì ưng dụng của bạn sẽ bị chết ngay lập tức.
+ Ví dụ vòng lặp While trong MYSQL
Ví dụ: Viết chương trình Stored Procedure in ra màn hình các số từ a tới b bằng vòng lặp While.
Trong ví dụ này mình có sử dụng hàm CONCAT
, đây là một hàm nỗi chuỗi trong MYSQL
và tham số truyền vào của nó là danh sách các chuỗi cần nối cách nhau bởi dấu phẩy. Hàm này không giới hạn tham số truyền vào. Nếu như bạn sử dụng cú pháp dấu +
để nối chuỗi thì sai nhé, trong MYSQL
không hỗ trợ phép toan này đối với chuỗi mà chỉ dành cho number.
|
Chạy thử:
|
Kết quả:
+ MySQL Index là gì?
Index là dữ liệu có cấu trúc như B-Tree giúp cải thiện tốc độ tìm kiếm trên một bảng, làm giảm chi phí thực hiện truy vấn. Việc tối ưu hóa chỉ mục sẽ giúp xác định được vị trí của dữ liệu cần tìm thay vì phải dò theo thứ tự hàng triệu record trong bảng.
Khi bạn tạo một khóa chính thì đồng nghĩa với việc bạn đã tạo một chỉ mục duy nhất, các hàng không được trùng dữ liệu với nhau, tên gọi của nó là PRIMARY, thuộc thể loai clustered index.
Những chỉ mục khác khong phải là PRIMARY thì ta gọi là chỉ mục phụ secondary index hoặc non-clustered indexes.
+ Đặt vấn đề về Index trong MySQL
Giả sử bạn có một danh bạ điện thoại chứa tất cả tên và số điện thoại của người dân trong thành phố. Bây giờ bạn muốn tìm số điện thoại của Bob Cat. Vì các tên được sắp xếp theo thứ tự abc nên trước tiên bạn tìm trang có tên cuối cùng là Cat, sau đó bạn tìm Bob và số điện thoại của anh ấy.
Nếu tên trong danh bạ điện thoại không được sắp xếp theo thứ tự abc, bạn sẽ cần phải đi qua tất cả các trang, đọc mọi tên trên đó cho đến khi bạn tìm thấy Bob Cat. Điều này được gọi là [tìm kiếm tuần tự]. Bạn đi qua tất cả các hàng cho đến khi bạn tìm thấy người có số điện thoại mà bạn đang tìm kiếm.
Liên hệ danh bạ điện thoại với bảng cơ sở dữ liệu, nếu bạn có bảng phonebooks và bạn phải tìm số điện thoại của Bob Cat, bạn sẽ thực hiện truy vấn sau:
|
Truy vấn thực hiện khá nhanh trong trường hợp table có một vài ngàn dữ liệu. Nhưng nếu bảng có hàng triệu record và không được đánh dấu chỉ mục thì truy vấn này sẽ mất khá nhiều thời gian xử lý.
+ Lệnh CREATE INDEX trong MySQL
Thông thường ta sẽ tạo chỉ mục ngay lúc tạo bảng bằng lệnh Create Table. Ví dụ dưới đây mình đã tạo một bảng và thiết lập chỉ mục cho nó luôn.
|
Ngoài ra, để thêm một chỉ mục vào bảng có sẵn thì ta sử dụng lệnh CREATE INDEX, cú pháp như sau:
|
Để tạo chỉ mục cho một cột hoặc nhiều cột thì tốt nhất ta đặt cho nó một cái tên, phía sau là danh sách các cột.
|
Theo mặc định, MySQL tạo chỉ mục B-Tree nếu bạn không chỉ định loại chỉ mục. Sau đây cho thấy loại chỉ mục cho phép dựa trên kiểu lưu trữ của bảng:
Storage Engine | Allowed Index Types |
---|---|
InnoDB | BTREE |
MyISAM | BTREE |
MEMORY/HEAP | HASH, BTREE |
Lưu ý rằng câu lệnh CREATE INDEX ở trên là phiên bản đơn giản hóa của câu lệnh CREATE INDEX được giới thiệu bởi MySQL. Chúng ta sẽ học cách sử dụng nó nhiều hơn ở các ví dụ tiếp theo.
+ Dùng CREATE INDEX tạo Index trong MySQL
Câu lệnh SQL sau đây sẽ tìm các nhân viên có chức danh là Sales Rep:
|
Kết quả như sau:
Kết quả trả về 10 nhân viên đang làm việc với chức danh là Sales Rep. Và để xem chi tiết câu truy vấn này thì ta thêm lệnh EXPLAIN ngay đầu lệnh Select thì sẽ thu được kết quả như sau:
Như bạn thấy, câu truy vấn đã duyệt qua tổng cộng là 23 rows để trả về 10 kết quả như trên.
Bây giờ mình sẽ tạo một chỉ mục cho cột jobTitle
như sau:
|
Và chạy lại câu truy vấn thêm một lần nữa:
|
Kêt quả thu được như sau:
Thật tuyệt vời, MySQL chỉ duyệt qua 17 rows thay vì 23 rows như ở trên. Điều này chính là nhờ vào kỹ thuật tạo index mà mình đã làm.
Để xem tất cả những index thuộc một table nào đó thì ta sử dụng lệnh SHOW INDEXES
.
|
Kết quả như hình dưới đây là một ví dụ khá chi tiết, nó hiển thị thông tin và kiểu index rõ ràng.
+ Cách sử dụng DROP INDEX trong MySQL
Để xóa một Index đã có sẵn trong table thì ta sử dụng câu lệnh DROP INDEX, cú pháp như sau:
|
Trong đó:
Thông số algorithm_option là thuật toán àm bạn muốn sử dụng để xóa chỉ mục index, cú pháp của nó như sau:
|
Để xóa chỉ mục, các thuật toán sau được hỗ trợ:
Lưu ý rằng câu lệnh ALGORITHM là tùy chọn, nếu bạn không thiết lập thì nó sẽ sử dụng thuật toán INPLACE. Trong trường hợp INPLACE không hỗ trợ thì nó sẽ sử dụng COPY.
Nếu bạn sử dụng DEFAULT đồng nghĩa bạn bỏ qua mệnh đề ALGORITHM này.
Thông số Lock_option kiểm soát mức độ đọc và ghi đồng thời trên bảng trong khi chỉ mục đang bị xóa.
Sau đây cho thấy cú pháp của lock_option:
|
Các chế độ lock đuọc hỗ trợ như sau:
+Sử dụng lệnh DROP INDEX để xóa chỉ mục trong MySQL
Trước tiên hãy tạo một table leads có cấu trúc như sau:
|
Câu lệnh dưới đây xóa chỉ mục name
ra khỏi table leads
.
|
Câu lệnh sau sẽ loại bỏ chỉ mục email từ leads với một thuật toán và khóa cụ thể:
|
+ Xóa khóa chính Primary Key ra khỏi table
Chỉ mục Primary Key là loại đặc biệt có tên gọi là PRIMARY, vì vậy bạn sử dụng nó để áp dụng trong lệnh Drop Index.
|
Ví dụ: Tạo mới một table như sau.
|
Và đây là câu lệnh xóa khóa chính:
|
+ Giới thiệu Prefix Index trong MySQL
Khi bạn tạo một chỉ mục phụ (secondary index) cho một column thì MySQL sẽ lưu các giá trị của các column trong một cấu trúc dữ liệu riêng biệt, ví dụ: B-Tree và Hash.
Trong trường hợp các column có kiểu dữ liệu chuỗi thì chỉ mục sẽ tiêu tốn rất nhiều dung lượng đĩa và có khả năng làm chậm các hoạt động INSERT. Để giải quyết vấn đề này, MySQL cho phép bạn tạo chỉ mục chỉ cho phần đầu giá trị của column.
Ví dụ bạn lưu trữ 1 triệu bài viết và muốn đặt chỉ mục cho field post_title
để giúp việc truy vấn nhanh hơn. Lúc này nếu sử dụng index bình thường thì sẽ không tốt cho việc lưu trữ, vì vậy ta cần tìm giải pháp là chỉ index bao nhiêu ký tự đầu tiên thôi, miễn là chiều dài đủ để các tiêu đề là duy nhất.
Cú pháp như sau:
|
Ví dụ: câu lệnh sau đây tạo prefix index ngay tại thời điểm tạo table.
|
Hoặc thêm từ một bảng đã tồn tại sẵn.
|
Trong cú pháp này thì length chính là độ dài của các loại chuỗi không phải nhị phân như CHAR, VARCHAR và TEXT, và là số byte của các kiểu dữ liệu như BINARY, VARBINARY và BLOB.
MySQL cho phép bạn tùy ý tạo column prefix cho các cột kiểu CHAR, VARCHAR, BINARY và VARBINARY. Nếu bạn tạo chỉ mục cho các cột BLOB và TEXT thì bạn phải chỉ định các phần chính của column prefix.
Lưu ý rằng độ dài của tiền tố phụ thuộc vào công cụ lưu trữ. Đối với các bảng InnoDB có định dạng hàng REDUNDANT hoặc COMPACT có độ dài tiền tố tối đa là 767 byte. Tuy nhiên, đối với các bảng InnoDB có định dạng hng DYNAMIC hoặc COMPRESSED sẽ độ dài tiền tố là 3.072 byte. Các bảng MyISAM có độ dài tiền tố lên tới 1.000 byte.
+ Các ví dụ tạo Prefix Index trong MySQL
Giả sử chúng ta có table products gồm các field như sau:
Truy vấn sau đây tìm các sản phẩm có tên bắt đầu bằng chuỗi 1970
.
|
Do không có chỉ mục cho cột ProductName
nên truy vấn phải quét tất cả các hàng để trả về kết quả như trong hình dưới đây. Mình sử dụng lệnh EXPLAIN để xem chi tiết câu SELECT.
|
Theo như trong hình thì có tổng cộng 110 rows. Nếu bạn thường xuyên tìm kiếm trên cột này thì hãy tạo chỉ mục cho nó, điều này giúp tối ưu hóa thời gian tìm kiếm.
Giả sử kích thước của cột ProductName tối đa 70 ký tự, là kiểu chuỗi nên ta có thể áp dụng trong Prefix Index.
Câu hỏi tiếp theo là làm thế nào để bạn chọn độ dài của Prefix Index? Để làm điều này thì dựa vào dữ liệu hiện có, mục tiêu là tìm được con số chính xác giúp các record trong bảng là duy nhất. Hãy nhớ là ta có tổng công 110 record nhé.
Để làm điều này, bạn làm theo các bước sau:
Bước 1. Tìm số lượng hàng trong bảng:
|
Theo như kết quả ở trên thì có 110 records.
Bước 2. Sử dụng câu lệnh dưới đây để tìm kiếm độ dài khúc đầu của chuỗi là bao nhiêu sẽ tạo ra được tính duy nhất cho các record. Bạn có thể thay con số 20 bằng con số bất kì để test.
|
Theo như hình thì 20 là độ dài tiền tố tốt trong trường hợp này bởi vì nếu chúng ta sử dụng 20 ký tự đầu tiên của tên sản phẩm cho chỉ mục thì tất cả các tên sản phẩm là duy nhất.
Hãy để tạo một chỉ mục với độ dài tiền tố 20 cho cột ProductName:
|
Và thực hiện truy vấn tìm các sản phẩm có tên bắt đầu bằng chuỗi 1970 một lần nữa:
|
Bây giờ câu truy vấn chạy nhanh hơn và hiệu quả hơn nhiều so với trước đây.
+ Khi nào tạo chỉ mục tổng hợp nhiều cột?
Chỉ mục tổng hợp là một chỉ mục trên nhiều cột. MySQL cho phép bạn tạo một chỉ mục tổng hợp bao gồm tối đa 16 cột. Việc tạo chỉ mục này giúp các câu truy vấn được xử lý tối ưu hơn.
Ví dụ bạn muốn truy vấn tìm danh sách sinh viên thuộc khoa CNTT và lớp CN01 thì có thể tạo chỉ mục cho 2 field (khoa_id, lop_id). Với cách làm này sẽ giúp tất cả những câu truy vấn có sử dụng cặp điều kiện này đều chạy nhanh hơn.
Để tạo một chỉ mục tổng hợp tại thời điểm tạo bảng thì ta sử dụng câu lệnh sau:
|
Trong ví dụ này thì mình đã tạo chỉ mục có tên là index_name
gồm ba cột (c2, c3 ,c4).
Hoặc bạn cũng có thể sử dụng lệnh Create Index để tạo chỉ mục.
|
Lưu ý rằng nếu bạn tạo chỉ mục cho n
cột thì những câu truy vấn có số cột ít hơn đều được tối ưu hóa, và phải tuân theo thứ tự từ trái sang phải. Như trong ví dụ trên thì những trường hợp sau:
|
Ví dụ:
|
Trình tối ưu hóa truy vấn bắt buộc phải theo thứ tự từ trái qua phải, nếu không thì index không có tác dụng gì cả. Như trong ví dụ trên thì nếu bạn thiết lập where:
+ Một ví dụ đặt chỉ mục nhiều cột trong MySQL
Giả sử chúng ta có bảng employees có cấu trúc như sau:
Sau đó mình tạo index gồm hai cột firstName và lastName.
|
Như vậy chúng ta chỉ có thể truy vấn cho hai trường hợp:
lastName
lastName
và firstName
Hãy tìm kiếm nhân viên có tên là "Patterson".
|
Câu truy vấn này có áp dụng index ở trên bởi vì lệnh where đầu tiên chính là lastName. Hãy đặt thêm lệnh EXPLAIN đằng trước câu Select để xem kết quả.
|
Kết quả:
Như vậy câu SQL trên đã duyệt qua 3 rows.
Trường hợp có cả lastName và firstName:
|
Tương tự trường hợp này vẫn được áp dụng index ở trên, bởi vì theo thứ tự index là lastName -> fistName.
Như trong hình là câu SQL đã duyệt qua 1 rows.
Trường hợp truy vấn mỗi firstName:
|
Trường hợp này index trên không có tác dụng gì cả.
Như vậy là mình đã hướng dẫn xong cách tạo ra index trên nhiều column, cũng như nguyên tắc áp dung index vào nhiều trường hợp khác nhau.
+ Giới thiệu Descending Index
Descending Index còn gọi là chỉ mục giảm dần, lưu trữ các giá trị theo thứ tự giảm dần. Các phiên bản MySQL 8.0 trở về trước bạn có thể chỉ định DESC trong khai báo chỉ mục, tuy nhiên trong phiên bản MySQL này đã bỏ qua nó.
Thực tế MySQL cũng thể quét chỉ mục theo thứ tự giảm dần nhưng chi phí thực hiện quá cao, hay nói cách khác là không tối ưu.
Câu lệnh SQL dưới đây tạo ra một table và một index trong table đó.
|
Khi bạn sử dụng lệnh SHOW CREATE TABLE
trong MySQL 5.7, bạn sẽ thấy rằng DESC bị bỏ qua như dưới đây:
|
Bắt đầu từ MySQL 8.0 thì các giá trị được lưu trữ theo thứ tự giảm dần nếu bạn sử dụng từ khóa DESC để tạo chỉ mục.
Sau đây cho thấy cấu trúc bảng trong MySQL 8.0:
|
+ Ví dụ Descending Index trong MySQL
Đầu tiên hãy tạo ra bảng t và nhiều index khác nhau như sau:
|
Tiếp theo hãy sử dụng đoạn code SQL Procedure dưới đây để insert 10000 dòng dữ liệu vào table này.
|
Chạy lệnh sau để insert:
|
Bây giờ mình có một vài yêu cầu cụ thể như sau:
Yêu cầu 1: Sắp xếp giảm dần theo hai cột a và b
|
Kết quả:
Yêu cầu 2: Sắp xếp cột a tăng dần, cột b giảm dần
|
Kêt quả:
Yêu cầu 3: Sắp xếp cột a giảm dần, cột b tăn dần.
|
Kết quả:
+ Invisible Index là gì?
Invisible Index là trạng thái chỉ mục ẩn, tức là những chỉ mục nào được thiết lập trạng thái này thì sẽ không được sử dụng. Muốn bật lại thì thay đổi trạng thái từ INVISIBLE sang VISIBLE.
Để tạo invisible index thì ta sử dụng cú pháp sau:
|
Trong đó lệnh CREATE INDEX thông báo cho MySQL tạo ra một index có tên là index_name
nằm trong table table_name
và xác định chỉ mục ẩn bằng từ khóa INVISIBLE.
Như trong ví dụ dưới đây mình đã tạo ra một chỉ mục ẩn có tên là extension nằm trong bảng employees
.
|
Để thay đổi trạng thái của một chỉ mục từ INVISIBLE sang VISIBLE và ngược lại thì ta dùng cú pháp sau:
|
Ví dụ sau đổi trạng tháu của chỉ mục extension từ INVISIBLE sang VISIBLE.
|
Bạn có thể kiểm tra trạng thái của chỉ mục bằng cách sử dụng lệnh select truy vấn vào table statistics nằm trong information_schema của MySQL.
|
Kết quả dạng như sau:
Một cách khác, bạn cũng có thể dùng lệnh SHOW INDEXES và nhận kết quả tương đương.
|
Như mình đã nói ở đầu bài, trình tối ưu hóa sẽ không sử dụng những chỉ mục bị ẩn (INVISIBLE). Vậy câu hỏi đặt ra là tại sao MySQL lại đưa ra trạng thái chỉ mục này? Hãy suy nghĩ rằng có một số trường hợp bạn muốn tắt index tạm thời thay vì xóa chúng để xem tốc độ có tối ưu hơn không, sau đó bật lại để so sánh.
Lưu ý rằng với chỉ mục primary key thì bạn không thể thiết lập chúng là invisible được, bởi đây là loại đặc biệt và duy nhất trong mỗi table, nó ảnh hưởng đến ràng buộc toàn vẹn của dữ liệu.
Giả sử bạn không muốn tạo khóa chính và thay vào đó là dùng UNIQUE, lúc này liệu có thay đổi được chỉ mục này sang trạng thái ẩn?
Câu trả lời là MySQL sẽ tự động hiểu chỉ mục UNIQUE đó là primary key nhé, mặc dù bạn chưa tạo primary key. Hãy xem ví dụ dưới đây để hiểu rõ hơn.
Trước tiên hãy tạo một table discounts có cấu trúc như sau:
|
Mình đã không tạo khóa chính, thay vao fđó tạo UNIQUE. Bây giờ bạn hãy thử đổi trạng thái của key UNIQUE này sang invisible xem thế nào nhé.
|
Chạy câu SQL này bạn sẽ gặp lỗi như sau:
|
Full Text Search là một kỹ thuật tìm kiếm các tài liệu không phù hợp với tiêu chí tìm kiếm. Tài liệu ở đây có thể là một mô tả sản phẩm, một bài viết được lưu trữ trong Database của MySQL.
Không phù hợp với tiêu chí tìm kiếm là như thế nào? Ví dụ bạn có 1000 bài viết và muốn tìm kiếm từ khóa "học lập trình miễn phí", nhưng trong đoạn văn đó không có từ khóa này, vì vậy cần phải tách nhỏ từ khóa đó ra để tìm kiếm đến bài có chứa các từ phù hợp nhất.
Thực tế bạn có thể sử dụng từ khóa Like trong MySQL đẻ tìm kiếm bằng cú pháp "%word1%word2%word3word1..." nhưng về tốc độ thì nó tốn khá nhiều tài nguyên, điều này xảy ra tình trạng overload nếu dữ liệu quá dài và quá nhiều.
+ Nên sử dụng Full Text Search trong MySQL?
Để trả lời câu hỏi này thì a hay cùng phân tích đến một vài đặc điểm.
Hiệu suất
MySQL phải quét toàn bộ bảng để tìm văn bản chính xác dựa trên một mẫu trong câu lệnh LIKE, nhưng full text search là một thể loại index nên sẽ cho tốc độ tốt hơn nhiều.
Sự linh hoạt
Tìm kiếm nội dung trọng từ khóa rất linh hoạt, không cần phải xuất hiện nguyên từ khóa mà nó sẽ phân nhỏ từng từ ra để tìm kiếm.
Xếp hàng tìm kiếm
Thực tế thì MySQL không trả về một dãy dự liệu có xếp hạng, vì vậy bạn không thể biết được đâu là bài có nội dung phù hợp nhất.
Tuy nhiên kể từ phiên bản 5.6 trở đi thì bạn có thể sử dụng order by để sắp xếp. Chi tiết thế nào thì mình sẽ trình bày ở các bài tiếp theo.
+ Những đặc điểm của Full Text Search trong MySQL
Sau đây là một số tính năng quan trọng của full-text-search trong MySQL:
- Hoạt động giống lệnh Like:
MySQL cung cấp câu LIKE để thực hiện tìm kiếm toàn văn. Bạn có thể áp dụng nó như Full Text Serch.
- Tự động cập nhật Index:
MySQL tự động cập nhật chỉ mục của cột văn bản bất cứ khi nào dữ liệu của cột đó thay đổi.
- Kích thước index vừa phải
Kích thước của chỉ số FULLTEXT tương đối nhỏ.
- Tốc độ
Cuối cùng nhưng không kém phần quan trọng, full-text-search cho tốc độ tìm kiếm nhanh hơn những câu truy vấn thông thường, bởi nó đã được lập chỉ mục khi mới thêm / cập nhật data.
+ Một số thao tác với Full Text Search
Dưới đây là một số thao tác với full text search.
* Lưu ý: Không phải tất cả các kiểu định dạng của table đều có thể áp dụng full text search. Trong phiên bản MySQL 5.6 trở lên, chỉ có các table thiết lập kiểu MyISAM và InnoDB là có hỗ trợ.
+ Tạo Full Text Search ngay trong lúc tạo bảng
Trong lệnh Create Table bạn dễ dàng thêm index full text search bằng cách sử dụng từ khóa FULLTEXT. Cú pháp như sau:
|
Như vậy, để tạo ra nhiều index thì bạn hãy thêm nó và cahs nhau bởi dấu phẩy. Xem ví dụ dưới đây, mình đã thêm full text search cho field post_content nằm trong table posts khi vừa mới khởi tạo chúng.
|
+ Tạo Full Text Search trong lệnh Alter Table
Cú pháp dưới đây giúp bạn tạo được index full text search bằng cách sử dụng lệnh alter table trong MySQL.
|
Nếu bạn xem kỹ thì thực ra nó cũng không khác gì việc tạo các loại index khác, chỉ có điều kỹ thuật index mỗi loại mỗi khác.
Như phần 1, bạn có thể thêm nhiều column và cách nhau bởi dấu phâỷ.
Như trong ví dụ dưới đây mình đã thêm index cho 2 field productDescription và productLine.
|
+ Tạo Full Text Search bằng CREATE INDEX trong MySQL
Một cách khác cũng khá đơn giản đó là sử dụng lệnh CREATE INDEX để tạo index full text search cho một table bất kì.
|
Ví dụ: Tạo chỉ mục full text cho hai field (addressLine1, addressLine2) nằm trong table offices
.
|
Lưu ý rằng với những table đã có lượng dữ liệu lớn thì lệnh này sẽ chạy chậm hơn bình thường,bởi MySQL sẽ mất thời gian lập chỉ mục cho những record có sẵn đó. Vì vậy bạn nên thiết kế dữ liệu thuật chuẩn và tạo chỉ mục cẩn thận thì sẽ giúp MySQL hoạt động tốt hơn.
Nếu bạn lỡ tay tạo rồi nhưng sau muốn xóa index full text search thì sử dụng lệnh ALTER TABLE DROP INDEX nhé.
|
Theo như cú pháp này thì bạn phải đặt tên cho Index thì mới xóa được. Việc đặt tên cũng là cách tốt nhất để giúp bạn quản lý các chỉ mục tốt hơn.
Ví dụ dưới đây mình đã xóa Index có tên là Address
từ bảng offices
.
|
Trong kỹ thuật tìm kiếm Full Text Search, MySQL tìm kiếm các hàng hoặc tài liệu có liên quan đến truy vấn ngôn ngữ tự nhiên của văn bản một cách tự do. MySQL tính toán mức độ liên quan dựa trên các yếu tố khác nhau bao gồm số lượng từ trong tài liệu, số lượng từ duy nhất trong tài liệu, tổng số từ trong collection và số lượng tài liệu (hàng) có chứa một từ cụ thể.
Để thực hiện tìm kiếm toàn văn bản bằng tìm kiếm tự nhiên ta sử dụng các hàm MATCH()
và AGAINST()
. Hàm MATCH ()
chỉ định cột nơi bạn muốn tìm kiếm và hàm AGAINST()
xác định biểu thức tìm kiếm sẽ được sử dụng.
+ Cách sử dụng hàm MATCH () và AGAINST ()
Trước khi bắt đầu thì ta hãy tạo một table có cấu trúc như sau:
Sau đó mình tạo Index Full Text Search cho cột productLine
.
|
Tiếp theo, để truy vấn tìm kiếm cho cột này bằng kỹ thuật Full Text Search thì ta sử dụng hai hàm trên như sau:
|
Kết quả trả về là những record mà cột productLine
có xuất hiện từ Classic.
Để tìm theo hai từ Classic hoặc Vintage thì ta sẽ ngăn cách nó bằng dấu phẩy.
|
Kết quả:
Hàm AGAINST()
theo mặc định sẽ nằm ở chế độ IN NATURAL LANGUAGE MODE
.
+ MySQL Boolean Full-Text Searches là gì?
Đây là các chế độ tìm kiếm khi sử dụng để truy vấn cho những column có sử dụng chỉ mục full text search. Theo tìm kiếm tự nhiên thì trong văn bản chỉ cần xuất hiện một trong số những từ mà ta đặt nó ở đầu vào là sẽ trả kết quả về. Tuy nhiên có một số trường hợp mình muốn phải xuất hiện ít nhất 2 từ nào đó thì lúc này phải sử dụng các chế độ MODE.
Ví dụ: Tìm kiếm từ khóa Truck trong chế độ bắt buộc phải có (IN BOOLEAN MODE)
|
Kết quả:
Hai sản phẩm trả về do có chứa từ Truck.
Để tìm kiếm các record xuất hiện từ Truck nhưng không được xuất hiện từ Pickup thì bạn sử dụng dấu trừ -
đằng trước cụm từ không muốn xuất hiện.
|
+ Các toán tử trong Boolean Full-Text Searches
Dưới đây là danh sách những toán tử giúp bạn có thể xư lý tìm kiếm một cách linh động hơn.
Toán tử | Mô tả |
---|---|
+ | Từ phải xuất hiện |
– | Từ không được xuất hiện |
> | Bao gồm từ này, và tăng giá trị xếp hạng |
< | Bao gồm từ này, và giảm giá trị xếp hạng |
() | Nhóm các từ thành các biểu thức con (cho phép chúng được bao gồm, loại trừ, xếp hạng, v.v. như một nhóm). |
~ | Phủ định một từ được xếp hạng |
* | Ký tự đại diện ở cuối từ |
“” | Xác định một cụm từ (trái ngược với danh sách các từ riêng lẻ, toàn bộ cụm từ được khớp để đưa vào hoặc loại trừ). |
Các ví dụ sau minh họa cách sử dụng các toán tử toàn văn bản boolean trong truy vấn tìm kiếm:
Ví dụ 1: Để tìm kiếm các hàng có ít nhất một trong hai từ: mysql hoặc tutorial.
|
Ví dụ 2: Để tìm các hàng xuất hiện cả hai từ mysql và tutorial.
|
Ví dụ 3: Để tìm các hàng bắt buộc có từ mysql nhưng từ tutorial thì có thể có hoặc không.
|
Ví dụ 4: Để tìm các hàng xuất hiện từ mysql nhưng không được xuất hiện từ tutorial.
|
Ví dụ 5: Để tìm kiếm các hàng có chứa từ mysql và xếp hạng hàng thấp hơn nếu nó chứa từ tutorial.
|
Ví dụ 6: Để tìm kiếm các hàng có chứa các từ mysql và tutorial, hoặc tutorial và training, nhưng đặt các hàng có chứa mysql tutorial cao hơn so với mysql training.
|
Ví dụ 7: Để tìm kiếm các từ bắt đầu bằng chữ my, ví dụ mysql, myadmin.
|
+ Các tính chất của Boolean Full-Text Searches
F. Query Expansion Full Text Search trong MySQL
Query Expansion Full Text Search là cách mở rộng truy vấn tìm kiếm trong Full Text Search MySQL, dựa trên phản hồi liên quan tự nhiên.
+ Hiểu hơn về query expansion trong Full Text Search
Thông thường, người dùng tìm kiếm thông tin dựa trên kiến thức của họ. Họ sử dụng kinh nghiệm của mình để đưa ra các từ khóa để tìm kiếm thông tin và đôi khi, những từ khóa này quá ngắn.
Để giúp người dùng tìm thông tin dựa trên những từ khóa quá ngắn này, công cụ tìm kiếm toàn văn bản MySQL giới thiệu một khái niệm gọi là mở rộng truy vấn.
Việc mở rộng truy vấn được sử dụng để mở rộng kết quả tìm kiếm của các full text search dựa trên phản hồi liên quan (hoặc mở rộng truy vấn mù). Về mặt kỹ thuật, MySQL full text search thực hiện các bước sau khi mở rộng truy vấn được sử dụng:
Bạn có thể sử dụng mở rộng truy vấn khi kết quả tìm kiếm quá ít. Bạn thực hiện lại các tìm kiếm với việc mở rộng truy vấn nhằm cung cấp cho người dùng nhiều thông tin liên quan và phù hợp với những gì họ đang tìm kiếm.
Để sử dụng query expansion thì ta sử dụng từ khóa WITH QUERY EXPANSION đặt trong hàm AGAINST (). Dưới đây là minh họa cú pháp của truy vấn bằng cách sử dụng WITH QUERY EXPANSION.
|
Ví dụ query expansion trong MySQL
Hãy xem một vài ví dụ sẽ giúp bạn hiểu rõ hơn là đống lý thuyết cồng kềnh phía trên.
Trong ví dụ này chúng ta sẽ sử dụng cột ProductName của bảng products để thử nghiệm tính năng mở rộng truy vấn.
Bước 1: Hãy tạo một full text search vào cột productName.
|
Bước 2: Tìm kiếm với từ khóa 1992, bước này chưa sử dụng query expansion.
|
Bạn sẽ nhận được kết quả như sau:
Như bạn thấy trong kết quả trả về có 2 record bởi vì chỉ có hai dòng này là xuất hiện từ 1992.
Bước 3: Hãy thử sử dụng Query Expansion xem thế nào nhé.
|
Kết quả trả về nhiều hơn 2 hàng bởi vì ta sử dụng mở rộng tìm kiếm. Hai hàng đầu tiên là tương quan giống nhất nên được xếp lên đầu, các hàng khác chính là phần mở rộng bởi nó chứa những từ nằm trong hai hàng đầu tiên. Ví dụ từ "Ferrari".
Trigger là trình kích hoạt, sẽ được kích hoạt khi một tác động nào đó vào nó.
Trong MySQL Trigger có nghĩa là một đoạn code SQL xử lý một chức năng nào đó, nó hoạt động ngầm và được chạy khi một trong các tác động như Insert / Update / Delete vào table.
Ví dụ: Bạn muốn khi thêm một nhân viên mới thì sẽ gửi 1 tin nhắn đến người dùng. Như vậy bạn sẽ viết trigger vào table employee, khi sự kiện insert xảy ra thì đồng thời nó sẽ thêm luôn một dòng dữ liệu vào bảng message.
Lênh CREATE TRIGGER
sẽ giúp bạn tạo ra một trigger mới, bằng cách sử dụng cú pháp sau:
|
Trong đó:
trigger_name
là tên của trigger mà bạn muốn đặt.INSERT | UPDATE| DELETE
: Mỗi trigger sẽ được gắn cho một trong ba hành động này.BEFORE | AFTER
: Nếu bạn chọn Before thì trigger sẽ chạy trước khi hành động xảy ra, ngược lại After là sau hành động xảy ra.ON table_name
là table sẽ được gắn trigger này.FOR EACH ROW
là sẽ duyệt qua từng row.Phần trigger_body
chính là nội dung xử lý khi xảy ra sự kiện. Ta sẽ dựa vào hai biến tạm là OLD và NEW để biết đâu là dữ liệu cũ va đâu là dữ liệu mới.
Ví dụ: Khi cập nhật thông tin khách hàng thì lưu thông tin cũ vào một table backup. Lúc này để lấy thông tin cũ thì bạn sử dụng OLD.ten_field
, còn thông tin mới chuẩn bị update là NEW.ten_field
.
Tham khảo bảng mô tả dưới đây để biết được khi nào thì hai biến đó tồn tại.
Trigger Event | OLD | NEW |
INSERT | No | Yes |
UPDATE | Yes | Yes |
DELETE | Yes | No |
Trigger nói chung không khó nếu bạn hiểu bản chất của nó. Và đương nhiên để học được thì bạn phải tìm hiểu qua Procedure.
Bước 1: Hãy tạo một table employees
có cấu trúc như sau.
Sau đó bạn tạo thêm một table tên là employees_audit
, table này chứa thông tin cũ mà bạn đã cập nhật cho khách hàng.
|
Bây giờ mình sẽ tạo một Trigger như sau:
before_employee_update
.BEFORE UPDATE
.employees
.
|
Như bạn thấy, mình sử dụng OLD.lastname
và OLD.employeeNumber
để lấy thông tin cũ trong table employees
.
Chạy lệnh này xong mình dùng lệnh SHOW TRIGGERS
để xem trong hệ thóng có không.
|
Ngoài ra, nếu bạn xem lược đồ bằng MySQL Workbench thì chọn employees > triggers bạn sẽ thấy trigger before_employee_update như trong ảnh chụp màn hình bên dưới:
Sau đó hãy thử cập nhật một row bất kì trong table employees
.
|
Cuối cùng, truy vấn bảng employees_audit
để kiểm tra xem kích hoạt có được kích hoạt bởi câu lệnh UPDATE không:
|
Kết quả đã xuất hiện một dòng dữ liệu trong bảng này, mặc dù mình không hề chạy lệnh insert trên nó.
+ Cú pháp lệnh Drop Trigger
Lệnh Drop Trigger
khá đơn giản, bạn chỉ việc sử dụng theo cú pháp sau:
|
Trong đó trigger_name
chính là tên trigger mà bạn đã tạo. schema_name
chính là schema mà trigger này thuộc về, mặc định nó là schema hiện tai mà bạn đang kết nối.
Lệnh IF EXISTS
sẽ giúp việc xóa trigger được an toàn hơn, nghĩa là nó chỉ xóa chỉ khi nào trigger đó tồn tại. Nếu bạn không có dòng này thì sẽ xuất hiện lỗi trong trường hợp không có trigger nào tên là trigger_name
.
Bạn phải lưu ý rằng một khi đã xóa trigger thì nó sẽ mất vĩnh viễn, ko còn việc gọi tự động nào kể từ lúc đó về sau.
+ Ví dụ xóa trigger với lệnh Drop Trigger
Chúng ta sẽ làm một bài tập thực hành. Trước tiên hãy tạo một table có tên là billings
với cấu trúc như lệnh SQL sau:
|
Tiếp theo tạo một trigger có tên là BEFORE UPDATE
và gán vào table billings
.
|
Nếu bạn không biết dòng DELIMITER
là gì thì hãy đọc lại kiến thức Store Procedure.
Trigger sẽ được kích hoạt trước khi chạy lệnh update. Nội dung của nó là hiển thị lỗi nếu giá mới lớn hơn gấp 10 lần giá cũ.
Tiếp theo hãy dùng lệnh SHOW TRIGGERS;
để xem tồn tại không nhé.
|
Bây giờ hãy chạy lệnh Drop trigger xem thế nào nhé.
|
Chạy lại lệnh SHOW TRIGGERS;
để xem có gì khác biệt không.
|
Và kết quả là nó đã bị xóa ra khỏi database.
Giống như tên gọi của nó, đây là loại trigger sẽ được gọi trước khi insert dữ liệu vào table, điều này khác hoàn toàn với After Trigger.
Cú pháp dưới đây giúp bạn tạo ra loại trigger này:
|
Về cú pháp cơ bản thì chúng ta đã được học ở bài trước, riêng bài này thì công thức xác định rõ là loại BEFORE INSERT
.
Nếu trong trigger của bạn có nhiều câu lệnh SQL thì phải đặt nó trong cặp BEGIN .. END
nhé.
|
Lưu ý rằng trong loại trigger này bạn có thể truy cập lấy dữ liệu mới, nhưng riêng dữ liệu cũ là không được bởi vì bản chất nó chưa đụng gì tới dữ liệu cũ nên không được lưu vết. Tức là dùng NEW.field_name
thì được, OLD.field_name
là không được.
Chúng ta sẽ tạo một before insert trigger để ...
-Tạo database:
Đầu tiên mình tạo một table có tên là WorkCenters
như sau:
|
Tiếp theo tạo một bảng WorkCenterStats
để lưu trữ tổng công suất làm việc.
|
-Tạo Trigger:
Bây giờ tạo một before insert trigger, khi thêm một dữ liệu mới vào table WorkCenterStats
thì sẽ cập nhật tổng công suất làm việc.
|
Đoạn code trên xảy ra hai trường hợp:
Nếu thêm vào lần đầu tiên thì ta phải sử dụng lệnh insert, lúc này capacity chính là giá trị nhập vào luôn.
|
Nếu thêm vào các lần tiếp theo thì ta update cộng dồn capacity lên.
|
-Test trigger:
Bây giờ hãy thử insert một vài dòng dữ liệu vào để xem trigger có hoạt động không nhé.
Đầu tiên chạy lệnh thêm dữ liệu như sau:
|
Tiếp theo truy vấn vào table WorkCenterStats
xem có gì trong đó không.
|
Dữ liệu đã tự động thêm vào.
Hãy thử insert thêm một dòng dữ liệu nữa.
|
Chạy câu lệnh này xong thì totalCapacity
phải có giá trị 300. Và đúng như thế thật.
Như vậy là trigger đã hoạt động đúng. Đây là các bước test quan trọng để kiểm tra trigger có hoạt động tốt hay không.
Đây là loại trigger được gọi sau khi dữ liệu được insert vào table.
+ Cú pháp After Insert Trigger trong MySQL
After Insert Trigger sẽ được tự động gọi sau khi sự kiện insert xảy ra và hoàn thành.
Cú pháp dưới đây sẽ giúp bạn tạo được After Insert Trigger.
|
Trong cú pháp này thì bạn chú ý lệnh AFTER INSERT
, nó giúp MySQL phân biệt được đây là loại After Insert Trigger.
Nếu bạn có nhiều dòng lệnh trong phần body thì hãy đặt nó trong cặp BEGIN .. BODY
.
|
Với loại trigger này bạn có thể truy xuất được giá trị của NEW nhưng không thay đổi được chúng, bởi bản chất nó đã insert xong rồi. Riêng đối với OLD thì không được vì nó không hỗ trợ trong loại INSERT
Trigger.
+ Ví dụ After Insert Trigger trong MySQL
Chúng ta sẽ làm các bước tương tự như bài trước, nhưng sẽ tạo một một số table mới và thao tác trên đó.
-Tạo database:
Bạn hãy chạy lệnh SQL dưới đây để tạo bảng members
.
|
Tiếp theo tạo thêm bảng reminders
để lưu trữ tin nhắn nhắc nhở cho các thành viên.
|
-Tạo trigger:
Đoạn code trigger dưới đây sẽ thêm một tin nhắn vào bảng reminders
nếu người dùng không nhập dữ liệu cho field birthDate
.
|
Ở trên sử dụng lệnh if else để kiểm tra giá trị NEW.birthDate
có null hay không, nếu null thì thêm một dòng tin nhắn cho member đó.
-Test Trigger:
Ok, bước cuối cùng và quan trọng nhất là test xem trigger hoạt động có ổn định hay không. Khá đơn giản, ta chỉ cần thêm một vài dòng dữ liệu và xem data của 2 table là được.
Thêm 2 dòng dữ liệu, trong đó có 1 dòng ngày sinh có giá trị null.
|
Dữ liệu lúc này trong bảng members sẽ như sau:
Nếu trigger hoạt động thì trong bảng reminders
phải sẽ được thêm một record tin nhắn, và đó chính là dòng đầu tiên.
|
Dữ liệu trả về là:
Như vậy là trigger đã hoạt động đúng.
Đây là loại trigger có tác dụng trước khi bạn update một row nào đó trong database.
+ Cú pháp Before Update Trigger
MySQL BEFORE UPDATE
Trigger sẽ được tự động gọi khi bạn thực hiện một hành động update các row trong table. Trigger sẽ chạy trước rồi mới update nên bạn hoàn toàn có thể thay đổi dữ liệu truyền vào.
Cú pháp của nó như sau:
|
Trong đó lệnh BEFORE UPDATE
thông báo cho MySQL biết là đang muốn tạo trigger kiểu BEFORE UPDATE
.
Về phần trigger body nếu bạn có nhiều câu lệnh trong đó thì phải đặt trong cặp BEGIN .. END
nhé.
|
Trong loại BEFORE UPDATE
trigger bạn có thể thay đổi dữ liệu cho NEW nhưng không thể thay đổi dữ liệu của OLD nhé.
+ Ví dụ Before Update Trigger
Không có gì dễ hiểu bằng một ví dụ. Hãy cùng mình làm một chức năng nhỏ nhé.
- Tạo database:
Đầu tiên hãy tạo một table có tên là sales, cấu trúc như sau:
|
Tiếp theo mình sẽ thêm một vài dòng dữ liệu để sau này thử nghiệm trigger bằng lệnh update.
|
Để chắc ăn thì hãy kiểm tra trong table sales đã có dữ liệu chưa nhé.
|
- Tạo Before Update trigger:
Mình sẽ viết một trigger bắt sự kiện update, kiểm tra nếu quantity
update lớn gấp 3 lần hiện tại thì thông báo lỗi "cannot be 3 times greater than the current quantity".
|
Trong lệnh SQL trên thì bạn chú ý một số điểm như sau:
Mình đã đặt tên cho trigger là before_sales_update
, gắn vào table sales
. Vì có nhiều lệnh bên trong nên đặt trong cặp BEGIN .. END
.
Đoạn code dưới đây là khai báo một biến thông báo lỗi.
|
Đoạn code dưới đây kiểm tra nếu giá trị nhập vào (NEW) gấp 3 lần hiện tại (OLD) thì gán lỗi và trả kết quả về.
|
- Thử nghiệm Trigger:
Bây giờ hãy thử nghiệm xem trigger hoạt động có đúng không nhé.
Bạn hãy để ý dòng dữ liệu thứ nhất có quanlity
là 150. Bây giờ mình thử update nó lên 500 xem có xuất hiện lỗi không nhé.
|
Bạn sẽ nhận được thông báo lỗi như sau:
|
Đây là loại trigger được gọi sau khi lệnh update hoàn thành, vì vậy bạn có thể truy cập OLD và NEW nhưng không thay đổi được giá trị của chúng.
+ Cú pháp After Update Trigger
Như thường lệ, bạn phải hiểu rằng đây là loại trigger hoạt động sau khi lệnh update thực hiện xong.
Cú pháp của nó như sau:
|
MySQL nhận biết đây là loại AFTER UPDATE
trigger thông qua từ khóa này.
Về phần trigger body bạn nên đặt trong thẻ BEGIN .. END
để tường minh, vì dù sao body nếu có nhiều lệnh SQL thì đó là điều bắt buộc.
|
Như mình đã nói ở trên, bạn có thể truy xuất đến giá trị của OLD
và NEW
nhưng không thể cập nhật được, vì MySQL đã thực hiện update xong mới gọi đến trigger này.
+ Ví dụ After Update Trigger trong MySQL
Hãy xem ví dụ dưới đây, chúng ta vẫn làm việc với bảng sales như ở bài trước. Hãy chạy lệnh SQL này để tạo bảng.
- Tạo table sale:
Dưới đây là cấu trúc của bảng Sales
.
|
Tiếp theo hãy insert 3 dòng dữ liệu vào, khác hoàn toàn với bài trước.
|
Nào, bây giờ hãy sử dụng lệnh Select
để xem trong bảng có gì nhé.
|
Tạo thêm một table SalesChanges
để lưu trữ thông tin cập nhật của bảng sales
.
|
- Tạo trigger:
Tiếp theo hãy tạo trigger xử lý tình huống sau: Nếu giá trị của quantity
cập nhật khác với gí trị cũ thì hãy lưu vết vào table SalesChanges
để sau này biết lịch sử thay đổi của nó.
|
Về các hàm mình sử dụng ở trên khá quen thuộc với các bạn rồi nên mình không giải thích gì thêm.
- Thử trigger:
Bây giờ hãy thử update dữ liệu xem trigger có hoạt động không nhé.
Bạn hãy để ý dòng dữ liệu ID = 1
có giá trị của quantity
là 140, bây giờ mình sẽ cập nhật nó thành 350.
Nếu như trigger hoạt động đúng thì nó sẽ lưu vết vào bảng SalesChanges
.
|
Hãy chạy câu SQL trên nhé, sau đó chạy lệnh dưới đây để xem trong bảng SalesChanges
có gì không.
|
Như vậy là nó đã hoạt động. Bây giờ hãy thử cập nhật tất của quantity
tăng lên 10%.
|
Xem lại bảng SalesChanges:
|
Vùng khoanh đỏ chính là ba dòng dữ liệu đã được thêm vào bởi vì đã update tăng 10% quantity
cho 3 rows trong bảng Sales
.
Đây là loại trigger sẽ xảy ra trước khi hệ thống thực hiện lệnh DELETE xóa dữ liệu.
Before Delete Trigger trong MySQL sẽ được gọi tự động thì bạn chạy câu lệnh delete, và nó sẽ thực hiện trước khi MySQL thực hiên câu lệnh delete đó.
Dưới đây là cú pháp cơ bản của nó:
|
Trong đó trigger_name
là tên của trigger mà bạn muốn đặt, table_name
là tên của table mà bạn gắn trigger này.
Riêng trigger_body
chính là nội dung xử lý quan trọng nhất, và nếu có nhiều lệnh thì bạn sẽ đặt trong cặp BEGIN .. END
.
|
Trong trigger này bạn có thể truy cập đến các giá trị OLD
nhưng không thể thay đổi chúng. Riêng NEW
thì không có bởi vì ta đang thực hiện câu lệnh Delete.
+ Ví dụ Before Delete Trigger
Hãy thực hành một ví dụ về Before Delete Trigger bạn sẽ thấy dễ hiểu hơn.
- Tạo table:
Đầu tiên hãy tạo table Salaries
lưu trữ thông tin lương của các nhân viên.
|
Tiếp theo hãy thêm một vai dòng dữ liệu để qua phần tiếp theo kiểm thử trigger.
|
Bây giờ hãy tạo thêm bảng SalaryArchives
dùng để lưu trữ lịch sử mức lương của nhân viên đã bị xóa.
|
- Tạo trigger:
Chương trình Trigger dưới đây sẽ thêm một dòng cần xóa vào bảng SalaryArchives
.
|
Tên của trigger là before_salaries_delete
, dòng BEFORE DELETE cho thấy đây là loại Before Delete Trigger. Dòng ON salaries
khai báo trigger sẽ hoạt động trên table này.
- Kiểm tra trigger:
Ta hãy chạy một câu lệnh delete để xem có dòng nào được tự động thêm trong bảng SalaryArchives
không nhé.
|
Hãy chạy câu Select để xem dữ liệu thế nào.
|
Trigger before_salaries_delete
đã tự động gọi nên nhân viên có ID là 1002 được thêm vào bảng này.
Bây giò ta hãy xóa hai dòng còn lại bằng cách chạy câu SQL Delete tất cả.
|
Nếu như trigger hoạt động ổn định thì sẽ xuất hiện thêm 2 dòng nữa trong bảng SalaryArchives. Cùng xem kết quả thế nào nhé.
|
Trigger đã hoạt động thêm hai lần vì lệnh Delete ở trên đã thực hiện xóa 2 lần.
Đây là loại trigger xảy ra khi bạn thực hiện lệnh delete, và nó sẽ chạy sau khi hệ thống chạy lênh delete đó thành công.
Về công dụng thì nó giống như Before Delete, điểm khác duy nhất là nó hoạt động sau khi lệnh delete hoàn thành, còn before delete thì nó hoạt động trước khi lệnh delete thục hiện.
Cú pháp như sau:
|
Trong đó trigger_name
là tên của trigger, table_name
chính là tên của table mà bạn muốn trigger này gắn vào.
Nếu có nhiều dòng SQL bên trong phần trigger_body thì hãy đặt nó trong cặp BEGIN .. END
.
|
Tương tự như Before Trigger, bạn có thể truy xuất đến giá trị của OLD nhưng không thể thay đổi chúng. Riêng NEW thì không có bởi vi đây là lệnh delete.
+ Vi dụ After Delete Trigger trong MySQL
Trước tiên hãy tạo một vài table để thực hành.
- Tạo table:
Hãy tạo một table Salaries
có cấu trúc đơn giản như sau:
|
Nó chỉ có 2 field như sau:
employeeNumber
là mã số nhân viênsalary
là mức lương của nhân viênHãy thêm một vài dòng dữ liệu.
|
Bây giờ tạo thêm một table SalaryBudgets
dùng để lưu trữ tổng số lương của tất cả nhân viên.
|
Đúng ra ta sẽ tạo After Insert Trigger để bắt sự kiện khi thêm nhân viên thì update tổng số mức lương trong bảng SalaryBudgets, hoặc After Update Trigger cho trường hợp bạn chỉnh mức lương. Vì để bài toán đơn giản hơn nên mình sẽ thêm thủ công như sau:
|
Thử truy vấn xem tổng số lương là bao nhiêu nhé. Nếu đúng ra thì sẽ là 5000 + 7000 + 8000 = 20000.
|
Quá chuẩn!
- Tạo trigger:
Viết trigger khi xóa một nhân viên trong bảng Salaries
thì update tổng số mức lương ở bảng SalaryBudgets
.
|
- Chạy thử trigger:
Trước tiên hãy thử xóa một nhân viên có id = 1002
xem thế nào nhé.
|
Hãy xem tổng số mức lương ở bảng SalaryBudgets có được update không nhé.
|
Kết quả 15000 là đúng rồi, tại vì nhân viên đã xóa có mức lương là 5000.
Bây giờ hãy thử xóa tất cả 2 nhân viên còn lại xem tổng số có update về 0 không nhé.
|
Xem kết quả thế nào.
|
+ Lấy record ngẫu nhiên bằng ORDER BY RAND()
MySQL không tích hợp bất kỳ câu lệnh nào để chọn các record ngẫu nhiên từ một bảng. Để thực hiện điều này ta bạn sử dụng hàm RAND ().
Truy vấn sau đây chọn một hàng ngẫu nhiên trong một table bất kì:
|
Giải thích:
Nếu bạn muốn chọn N bản ghi ngẫu nhiên từ bảng cơ sở dữ liệu, bạn cần thay đổi mệnh đề LIMIT như sau:
|
Giả sử ta có table như sau:
Và câu SQL dưới đây sẽ lấy record ngẫu nhiên từ bảng này.
|
Lưu ý rằng khi bạn chạy lệnh này thì mỗi lần chạy kêt quả sẽ khác nhau, bởi giá trị mà hàm RAND sinh ra là ngẫu nhiên.
Kỹ thuật này hoạt động rất tốt với table nhỏ. Tuy nhiên, nó sẽ chậm đối với table lớn vì MySQL phải sắp xếp toàn bộ dữ liệu để chọn ngẫu nhiên.
Tốc độ của truy vấn cũng phụ thuộc vào số hàng trong bảng. Bảng càng có nhiều hàng, thì càng mất nhiều thời gian để tạo số ngẫu nhiên cho mỗi hàng.
+ Lấy record ngẫu nhiên bằng lệnh JOIN
Kỹ thuật này yêu cầu bảng phải có khóa chính tăng tự động (auto increment) và không có khoảng trống trong dãy thứ tự tăng dần. Tức là tăng dần 1, 2 ,3 thì ok, còn bị khuyết một giá trị như 1, 2, 5 ,6 thì không được.
Truy vấn sau tạo một số ngẫu nhiên dựa trên cột khóa chính:
|
Chúng ta có thể JOIN table với kết quả này để lấy record ngẫu nhiên.
|
Sử dụng kỹ thuật này bạn phải thực hiện truy vấn nhiều lần nếu muốn lấy nhiều record ngẫu nhiên, bởi vì câu lệnh sub query chỉ trả về một giá trị random mà thôi.
Truy vấn sau đây trả về một khách hàng ngẫu nhiên từ bảng customer
.
|
+ Lấy record ngẫu nhiên bằng JOIN và tạo biến
Trong trường hợp bảng có cột khóa chính id
với các giá trị nằm trong phạm vi 1..N, bạn có thể sử dụng kỹ thuật sau:
|
+ Lấy ngày hiện tại bằng build-in function
Thỉnh thoảng bạn muốn lấy ngày giờ hiện tại trong MySQL để phục vụ cho mội tính năng nào đó của phần mềm. Ví dụ bạn làm web tin tức là có tính năng hẹn giờ đăng bài, bạn sẽ truy vấn những bài nào có ngày đăng bé hơn hoặc bằng ngày hiện tai.
Lúc này một là bạn truyền giá trị ngày hiện tại vào, hai là dùng các hàm lấy ngày giờ trong MySQL.
- Hàm CURDATE():
Hàm CURDATE() sẽ trả vè ngày hiện tại trong hệ thống server. Hãy xem ví dụ dưới đây.
|
Hoặc bạn có thể sử dụng hàm now() như cách dưới đây.
- Kết hợp DATE() và NOW():
Bạn sẽ dùng hàm now()
để lấy ngày giờ hiện tại định dạng milisecond kiểu số nguyên, sau đó đặt nó trong hàm date(
) để trả về ngày hiện tại.
|
+ Tự tạo Store Procedure lấy ngày hiện tại
Thực tế thì cách này ít người sử dụng bởi nó tốn quá nhiều thời gian. Tuy nhiên nếu bạn thích vọc vạch MySQL thì hãy thử xem sao nhé.
Mình sẽ tạo một store procedure function có tên là today.
|
Bây giờ bạn có thể thực hiện truy vấn và sử dụng hàm đó một cách bình thường.
|
Lấy ngày mai thì sao, bạn chỉ việc cộng nó lên 1 day là được.
|
Tương tự, để lấy ngày hôm qua thì ta trừ nó đi 1 day.
|
Đầu tiên bạn cần viết câu truy vấn lấy danh sách kết quả trả về. Tuy thuộc vào nhu cầu mà bạn thiết lập sắp xếp tăng hay giảm dần.
Tiếp theo sử dụng lệnh LIMIT trong MySQL để lấy thứ tự của record mong muốn.
SELECT * FROM table_name ORDER BY column_name DESC LIMIT n - 1, 1;
Nếu bạn muốn lấy record thứ 5 thì n = 5.
Bạn đang tạo khóa ngoại nhưng gặp lỗi "missing index for constraint" trong MySQL? Vậy thì hãy tham khảo bài hướng dẫn này nhé, mình sẽ giúp bạn fix lỗi đó.
Nếu dịch ra tiếng Anh thì nó rất rõ nghĩa, đây là lỗi bị thiếu chỉ mục cho ràng buộc khóa ngoại. Theo như quy tắc thì khá ngoại của bảng con phải trỏ đến khóa chính của bảng cha, nhưng bạn đã không tạo khóa chính cho bảng cha.
Vì vậy giải pháp là tạo chỉ mục khóa chính trước khi tạo khóa ngoai nhé.
- Tạo bảng con med_pharmacy
|
- Tạo bảng cha medication
|
- Bây giờ thêm khóa ngoại bằng lệnh SQL:
|
Xuất hiện lỗi:
#1822 - Failed to add the foreign key constaint. Missing index for constraint 'fk_med_pharmacy_medication1' in the referenced table 'medication'
Nó nói khá rõ ràng, trong bảng medication bạn đã không tạo khóa chính cho med_id.
Vì vậy hãy thêm nó bằng lệnh sau:
|
Rồi chạy lại lệnh tạo kháo ngoại ở trên nhé.
|
E. Cơ chế lưu trữ kiểu ngày tháng trong mysql
Như bạn biết trong mysql chúng ta có thể lưu định dạng ngày tháng là kiểu date, kiểu datetime. Đây là hai kiểu chính và các bạn rất thường hay sử dụng để tìm kiếm. Nhưng có một sự thật mà ít bạn quan tâm đến đó là nếu như bạn truy vấn với kiểu INT và có index thì tốc độ sẽ rất nhanh, tức là thay vì ta lưu kiểu date hoặc datetime thì ta tạo thêm một field kiểu UNSIGN INT nữa và dùng code PHP để chuyển ngày tháng sang trước khi lưu vào CSDL.
Trong mysql năm bắt đầu ta sẽ tính bắt đầu từ năm 1970 và tới nay là năm 2014, tính ra khoảng 44 năm. Và tổng khoảng thời gian 44 năm này chuyển sang kiểu INT sẽ có tổng cộng 1407612770 giây. Nhưng trong mysql, kiểu UNSIGN INT chỉ lưu trữ tới mức khoảng 4294967295 là hết, như vậy tính ra ta còn có thể lưu thê được ít nhất hơn 60 năm nữa mới max. Tới lúc đó thì công nghệ phát triển rồi nên chắc chắn sẽ ko bị hạn chế như vậy.
Ý tưởng thứ nhất chính là phần phân tích cơ chế lưu trữ trong mysql trên.
Ý tưởng thứ 2 dựa vào ý tưởng thứ nhất ta sẽ lưu trữ một field gọi là news_timer (kiểu UNSIGN INT), field này có giá trị chính là thời gian mà người dùng chọn đăng bài. Như vậy khi hiển thị danh sách tin tức thì ta chỉ cần thêm điều kiện lọc ngày hiện tại phải lơn hoặ hoặc bằng ngày hiển thị trong timer.
Khi làm việc với tin tức ta sẽ có một field gọi là status, giá trị bằng 1 là hiển thị, giá trị bằng 0 là không hiển thị. Vậy gộp lại ta có điều kiện lọc nhu sau:
Select * from news where status = 1 and timer <= $bien_kieu_int_time_hien_tai
Với điều kiện lọc như vậy thì ta thấy cũng tối ưu rồi, vì cả hai điều kiện ta có thể đánh chỉ số index. Nhưng có cách nào tối ưu hơn không? Để trả lời câu hỏi này các bạn chú ý đến phần phân tích thời gian từ 1970 tới nay sẽ có ý tưởng như sau:
Chọn thời gian max chính là giá trị max của kiểu UNSIGN INT, tức là số 4294967295, chọn 4200000000 cho chẵn.
Như vậy ta sẽ bỏ đi field status, còn mỗi field timer thôi. Nếu người dùng chọn ẩn tin thì ta sẽ thiết lập gái trị cho timer bằng 4200000000, còn không thì ta lưu thời gian bình thường. Như vậy khi hiển thị tin chỉ lọc đúng một điều kiện timer duy nhất và đánh chỉ mục index nữa thì tốc độ sẽ nhanh hơn rất nhiều.
Về bài trước |
+ Lê Văn Thuyên-0379136392:Cảm ơn quý vị và các bạn đã vào Website của Lê Thuyên! Lê thuyên rất mong nhận được sự góp ý của quý vị và các bạn cho sự phát triển của website này. Xin chân thành cảm ơn!
* Dũng Trung-090567448:Lê Văn Thuyên0379136392--->Ok.Anh!
* Bé Nguyễn-benguyen@gmail,com:Lê Văn Thuyên0379136392--->Good job!
+ -:
+ -: