Lập trình database với MySQL
Công ty Vĩnh Cửu tuyển dụng lập trình viên Java

TÀI LIỆU ĐANG ĐƯỢC CẬP NHẬP 70%

1- Bắt đầu với MySQL Workbench

Trong tài liệu này tôi sẽ hướng dẫn bạn lập trình MySQL trên công cụ trực quan MySQL Workbench. Nếu bạn sử dụng một công cụ trực quan khác để viết code thì cũng không có nhiều khác biệt gì cả.
Đây là hình ảnh MySQL Workbench trong khi bạn mới mở nó. Click vào vùng khoanh đỏ để chọn SCHEMA làm việc.
Có một số SCHEMA ví dụ có sẵn khi bạn cài đầy đủ phiên bản MySQL Community.
Hoặc bạn có thể tạo SCHEMA learningsql, một SCHEMA ví dụ, được sử dụng trong một vài tài liệu hướng dẫn MySQL trên o7planning.org
Nhấn phải chuột vào một SCHEMA, chọn "Set as Default Schema" để làm việc với schema này.
Nhấn vào biểu tượng tạo mới một cửa sổ SQL để viết code (1). Nhấn vào vùng khoanh tròn (2) để ẩn một số cửa sổ không cần thiết.

2- Hàm (Function)

Giống như thủ tục, hàm (function) là nhóm các lệnh PL/SQL thực hiện chức năng nào đó. Khác với thủ tục, các hàm sẽ trả về một giá trị ngay tại lời gọi của nó.
Hàm cũng có thể được lưu giữ ngay trên database dưới dạng Store procedure.
Cú pháp tạo Hàm.
-- function_name:  Tên hàm
-- argument:  Tên tham số
-- mode:  IN hoặc không cần viết.
-- datatype:  Kiểu dữ liệu của tham số

CREATE FUNCTION <function_name>
            (
               [
                [mode1] argument1 datatype1,
                [mode2] argument2  datatype2,
                ...
                ]
             )
            RETURNS  datatype
BEGIN
  -- Khai báo biến sử dụng
  -- Code nội dung của hàm
  -- Trả về giá trị của hàm.
END;
Ví dụ:
-- Một hàm có tham số:
CREATE FUNCTION Sum_Ab(a Integer, b Integer)
RETURNS Integer
Begin
   return a + b;
End;

-- Một hàm không tham số:
CREATE FUNCTION Get_Current_Datetime()
RETURNS Date
Begin
   return current_date();
End;
Hủy Function:
-- Hủy Function

DROP FUNCTION <function_name>;

-- Ví dụ:

DROP FUNCTION My_Function;

-- Ví dụ:

DROP FUNCTION IF EXISTS  `My_Function`;
Gọi hàm.
-- Khi gọi hàm phải khai báo một biến trả về

-- Khai báo một biến c.
Declare c Integer;
....

-- Gọi hàm.
Set c = Sum_Ab(10, 100);

Ví dụ tạo một hàm.

Đây là một ví dụ tạo một hàm đầu tiên của bạn với MySQL:
  1. Tạo một hàm (Function)
  2. Biên dịch hàm này
  3. Chạy hàm

Nếu bạn mới bắt đầu với hàm và thủ tục MySQL bạn hãy xem chi tiết mục này, nó sẽ giúp bạn dễ dàng hiểu các vấn đề tiếp theo được đề cập trong tài liệu.

-- Huỷ function My_Sum nếu nó đã tồn tại.
-- (Để cho phép tạo lại)
DROP function  if Exists `My_Sum`;

-- Khi lập trình hàm/thủ tục bạn cần sử dụng dấu chấm phẩy để
-- ngăn cách các câu lệnh khác nhau.
-- Sử dụng DELIMITER $$ để cho phép sử dụng dấu chấm phẩy.
DELIMITER $$

Create Function My_Sum(P_a Float, p_B Float) returns Float
Begin  

 -- Khai báo một biến Float
  Declare v_C Float;
 
  -- Sét giá trị cho biến v_C
  Set V_C = p_A + p_B;
 
  -- Giá trị trả về của hàm.
  return v_C;

End;
Nhấn biểu tượng   để biên dịch hàm.
Các hàm có thể tham gia vào trong câu lệnh SELECT.
SELECT
    acc.account_id,
    acc.cust_id,
    acc.avail_balance,
    acc.pending_balance,
    MY_SUM(acc.avail_balance, acc.pending_balance) balance
FROM
    account acc;
Kết quả chạy câu lệnh SQL trên:

3- Thủ tục (Procedure)

Một nhóm các lệnh thực hiện chức năng nào đó có thể được gom lại trong một thủ tục (procedure) nhằm làm tăng khả năng xử lý,khả năng sử dụng chung,tăng tính bảo mật và an toàn dữ liệu,tiện ích trong phát triển.
Thủ tục có thể được lưu giữ ngay trong database như một đối tượng của database, sẵn sàng cho việc tái sử dụng. Thủ tục lúc này được gọi là Store procedure. Với các Store procedure, ngay khi lưu giữ Store procedure, chúng đã được biên dịch thành dạng p-code vì thế có thể nâng cao khả năng thực hiện.
Thủ tục không trả về giá trị trực tiếp như hàm. Nhưng nó có thể có không hoặc nhiều tham số đầu ra.
Cú pháp tạo một thủ tục:
-- procedure_name:  Tên thủ tục
-- argument:  Tên tham số
-- mode:  Loại tham số: IN hoặc OUT hoặc INOUT, mặc định là IN
-- datatype:  Kiểu dữ liệu của tham số

CREATE PROCEDURE <procedure_name>
       (
        [
         [mode1]  argument1  datatype1,
         [mode2]  argument2   datatype2,
        ...
         ]
       )
BEGIN
-- Khai báo biến sử dụng
-- Nội dung của thủ tục.
END;
Ví dụ:
-- Ví dụ một thủ tục không tham số.
CREATE Procedure Do_Something()
Begin
   -- Khai báo biến tại đây.
  Declare v_a Integer;
  -- Làm gì đó tại đây.
  -- ....
End;

-- Ví dụ một thủ tục có tham số
--  Có một tham số đầu vào và một tham số đầu ra.

CREATE Procedure Do_Something(p_Param1 Varchar(20),
                                                 OUT v_Param2 Varchar(50) )
Begin
    -- Khai báo biến tại đây.  
   Declare v_a Integer;

   -- Làm gì đó tại đây.
   -- ...
End;
Hủy thủ tục:
-- Hủy một thủ tục:

DROP PROCEDURE <Procedure_Name>
Các bước thực hiện một thủ tục:

Ví dụ tạo một thủ tục:

Ở đây tôi tạo một thủ tục đơn giản, với tham số truyền vào là p_Emp_ID và có 3 tham số đầu ra, v_First_Name, v_Last_Name, v_Dep_ID.
Get_Employee_Infos
-- Hủy thủ tục Get_Employee_Infos nếu nó đã tồn tại.
-- (Để cho phép tạo lại)
DROP Procedure if Exists `Get_Employee_Infos`;

-- Khi lập trình hàm/thủ tục bạn cần sử dụng dấu chấm phẩy để
-- ngăn cách các câu lệnh khác nhau.
-- Sử dụng DELIMITER $$ để cho phép sử dụng dấu chấm phẩy.
DELIMITER $$

-- Thủ tục truyền vào p_Emp_Id
-- Và trả về v_First_Name, v_Last_Name, v_Dept_Id.
Create Procedure Get_Employee_Infos(p_Emp_Id     Integer
                                ,Out v_First_Name Varchar(50)
                                ,Out v_Last_Name    Varchar(50)
                                ,Out v_Dept_Id      Integer)  
Begin
-- Dành cho người lập trình biết chương trình chạy thế nào.
Select Concat('Parameter p_Emp_Id = ',p_Emp_ID)  as log_info1 ;
--
-- Sử dụng câu lệnh SELECT .. INTO để gán các giá trị truy vấn được vào biến
-- Câu lệnh này có thể gây lỗi nếu mệnh đề SELECT trả về nhiều hơn 1 dòng.
-- (Việc sử lý các lỗi như vậy sẽ được đề cập trong tài liệu này).
--
Select Emp.First_Name
     ,Emp.Last_Name
     ,Emp.Dept_Id
Into   v_First_Name
     ,v_Last_Name
     ,v_Dept_Id
From   Employee Emp
Where  Emp.Emp_Id = p_Emp_Id;
--
-- Log dành cho người lập trình
--
Select 'Found Record!' as log_info2 ;
Select concat(' v_First_Name= ', v_First_Name) as log_info3 ;
Select concat(' v_Last_Name= ', v_Last_Name) as log_info4 ;
Select concat(' v_Dept_Id= ', v_Dept_Id) as log_info5 ;

End;
Nhấn vào biểu tượng để biên dịch thủ tục.

Test thủ tục

Trong lập trình việc test một thủ tục và dò tìm lỗi là vấn đề rất quan trọng. Mở một cửa sổ mới và thực thi đoạn lệnh gọi thủ tục:
Call Get_Employee_Infos(1,@v_First_Name,@v_Last_Name,@v_Dept_Id);
Sử dụng @variable_name để lưu trữ các tham số đầu ra.

4- Các lệnh cơ bản

Ở đây tôi giới thiệu tổng quan về các lệnh cơ bản của PL/SQL. Bạn sẽ hiểu hơn về nó thông qua các ví dụ ở các phần tiếp theo.

4.1- Câu Lệnh rẽ nhánh If-elsif-else

Cú pháp:
IF <điều kiện 1> THEN
   Công việc 1;
[ELSEIF <điều kiện 2> THEN
    Công việc 2;
]
[ELSE
    Công việc n + 1;
]
END IF;
Ví dụ:
If v_Option = 1 Then
 v_Action := 'Run';
Elseif v_Option = 2 Then
 v_Action := 'Backup';
Elseif v_Option = 3 Then
 v_Action := 'Stop';
Else
 v_Action := 'Invalid';
End If;
Else_If_Example
DROP Procedure if Exists `Else_If_Example`;

DELIMITER $$

CREATE PROCEDURE Else_If_Example()
BEGIN

   -- Khai báo một biến
   Declare v_Option Integer;
   Declare v_Action Varchar(30);
   
   Set v_Option = 2;
   
   
   If v_Option = 1 Then
     Set v_Action  = 'Run';
   Elseif v_Option = 2 Then
     Set v_Action  = 'Backup';
   Elseif v_Option = 3 Then
     Set v_Action  = 'Stop';
   Else
     Set v_Action  = 'Invalid';
   End If;
   
   -- Ghi ra log
   SELECT Concat('v_Action= ', v_Action) as Log_Info;
   
END
Kết quả test thủ tục:

4.2- Câu lệnh rẽ nhánh CASE

Cú Pháp:
CASE case_value
   WHEN when_value THEN statement_list
   [WHEN when_value THEN statement_list] ...
   [ELSE statement_list]
END CASE

Hoặc:


CASE
   WHEN search_condition THEN statement_list
   [WHEN search_condition THEN statement_list] ...
   [ELSE statement_list]
END CASE
Case_When_Example1
DROP Procedure if Exists `Case_When_Example1`;

DELIMITER $$

CREATE PROCEDURE Case_When_Example1()
BEGIN

 -- Khai báo 2 biến x và y.
 Declare x integer;
 Set x  = 3 ;

 -- Kiểm tra các trường hợp giá trị của x
 Case x
  -- Khi x = 1 thì
  When 1 then    
    Select 'one' as log_info1;
 
  -- Khi x = 2 thì
  When 2 then
     Select 'Two' as log_info2;
   
  -- Khi x = 3 thì
  When 3 then
     Select 'Three' as log_info3;
   
  -- Các giá trị khác
  Else
     Select 'Other' as log_info3;
   
  End case;


END;
Case_When_Example2
DROP Procedure if Exists `Case_When_Example2`;

DELIMITER $$

CREATE PROCEDURE Case_When_Example2()
BEGIN

 -- Khai báo 2 biến x và y.
 Declare x integer;
 Set x  = 3 ;

 -- Kiểm tra các trường hợp:
 Case  
  -- Khi x = 1 thì
  When x = 1 then    
    Select 'one' as log_info1;
 
  -- Khi x = 2 thì
  When x = 2 then
     Select 'Two' as log_info2;
   
  -- Khi x = 3 thì
  When x = 3 then
     Select 'Three' as log_info3;
   
  -- Các giá trị khác
  Else
     Select 'Other' as log_info3;
   
  End case;


END;

4.3- Vòng lặp không định trước (LOOP)

Cú pháp:
<Loop_Name> : LOOP
     -- Làm gì đó tại đây.
END LOOP   <Loop_Name>;
Trong vòng lặp LOOP bạn có thể sử dụng LEAVE để thoát ra khỏi vòng lặp, RETURN cũng có thể được sử dụng, nhưng nó là lệnh thoát ra khỏi thủ tục.
Sử dụng lệnh ITERATE để bỏ qua các dòng lệnh trong khối LOOP và ở bên dưới nó, để tiếp tục một vòng lặp mới.

LEAVE giống với BREAK, và ITERATE giống với CONTINUE trong ngôn ngữ lập trình khác như Java, C/C++, C#.

Loop_Example1
DROP Procedure if Exists `Loop_Example1`;

DELIMITER $$

CREATE PROCEDURE Loop_Example1()
BEGIN

  -- Khai báo 2 biến x và y.
  Declare x,y integer;
  Set x  = 0 ;
  Set y = 10;
 
  -- Bắt đầu vòng lặp có tên My_Loop
  My_Loop: Loop
      -- Mỗi lần vòng lặp chạy giá trị của x tăng lên 1
      Set x = x + 1;
      -- Mỗi lần vòng lặp chạy giá trị của y giảm đi 2
      Set y = y - 2;
     
      if x > y then
           -- Thoát khỏi vòng lặp My_Loop.
          Leave My_Loop;
      End if;
     
  End Loop My_Loop;
     
  -- Ghi ra log
  SELECT Concat('x,y = ', x,', ', y) as Log_Info;
 
END;
Loop_Example2
DROP Procedure if Exists `Loop_Example2`;

DELIMITER $$

CREATE PROCEDURE Loop_Example2()
BEGIN

  -- Khai báo 2 biến x và y.
  Declare x,y integer;
  Set x  = 0 ;
  Set y = 10;
 
  -- Bắt đầu vòng lặp có tên My_Loop
  My_Loop: Loop
      -- Mỗi lần vòng lặp chạy giá trị của x tăng lên 1
      Set x = x + 1;
      -- Mỗi lần vòng lặp chạy giá trị của y giảm đi 2
      Set y = y - 2;
     
      if x <= y then
           -- Bỏ qua các dòng lệnh trong LOOP và bên dưới
           -- để tiếp tục.
          Iterate My_Loop;
      End if;
     
      -- Thoát khỏi vòng lặp.
      Leave My_Loop;
     
  End Loop My_Loop;
     
  -- Ghi ra log
  SELECT Concat('x,y = ', x,', ', y) as Log_Info;
 
END;

4.4- Vòng lặp REPEAT .. UNTIL

Vòng lặp REPEAT .. UNTIL có nghĩa là lặp .. cho tới khi điều kiện không còn đúng nữa. Nó giống với vòng lặp do .. while trong ngôn ngữ Java, C/C++, C#
Cú pháp:
[Loop_label:] REPEAT

    -- Làm gì đó tại đây.

UNTIL  <điều kiện thoát khỏi vòng lặp>
END REPEAT [Loop_label];
Trong vòng lặp REPEAT.. UNTIL bạn cũng có thể sử dụng lệnh LEAVE, và ITERATE và ý nghĩa không có gì thay đổi (Xem thêm trong LOOP).
Repeat_Until_Example
DROP Procedure if Exists `Repeat_Until_Example`;

DELIMITER $$

CREATE PROCEDURE Repeat_Until_Example()
BEGIN

 -- Khai báo 2 biến x và y.
 Declare x integer;
 Set x  = 0 ;

 -- Bắt đầu vòng lặp có tên My_Loop
 My_Loop: Repeat

     -- Mỗi lần vòng lặp chạy giá trị của x tăng lên 1
     Set x = x + 1;
   
     -- Ghi ra log
     SELECT Concat('x = ', x) as Log_Info;

 -- Thoát khỏi vòng lặp khi x > 3
 Until x > 3    
 End Repeat My_Loop;      


END;

4.5- Vòng lặp WHILE .. DO

Cú pháp:
[Loop_Name:] WHILE search_condition DO
    -- Làm gì đó tại đây.
END WHILE [Loop_Name]
Trong vòng lặp WHILE .. DO bạn cũng có thể sử dụng lệnh LEAVE, và ITERATE và ý nghĩa không có gì thay đổi (Xem thêm trong LOOP).
While_Do_Example
DROP Procedure if Exists `While_Do_Example`;

DELIMITER $$

CREATE PROCEDURE While_Do_Example()
BEGIN

  -- Khai báo 2 biến x và y.
  Declare x integer;
  Set x  = 0 ;
 
  -- Bắt đầu vòng lặp có tên My_Loop
  My_Loop: While x < 5 Do
 
      -- Mỗi lần vòng lặp chạy giá trị của x tăng lên 1
      Set x = x + 1;
     
      -- Ghi ra log
      SELECT Concat('x = ', x) as Log_Info;
     
  End While My_Loop;      
 
 
END;

5- Điều khiển ngoại lệ

Trong khi lập trình MySQL có thể một lỗi có thể phát sinh, chẳng hạn trèn vào một bản ghi trùng lặp khóa chính, MySQL sẽ ném ra một ngoại lệ có mã số tương ứng với nguyên nhân gây lỗi.

Trong MySQL để sử lý tình huống ngoại lệ bạn định nghĩa ra một biến, và bảo với MySQL sẽ gán một giá trị nào đó vào biến này khi có ngoại lệ phát sinh. Bạn có thể kiểm tra giá trị của biến để biết được lỗi vừa xẩy ra và sử lý nó.

5.1- Ví dụ bắt đầu với ngoại lệ

Hãy xem một thủ tục trèn dữ liệu vào bảng Department:
Handling_Exception_Example1
DROP Procedure if Exists `Handling_Exception_Example1`;

DELIMITER $$

CREATE PROCEDURE Handling_Exception_Example1()
BEGIN

 -- Ghi ra log
 Select 'Three';  

 -- Trèn vào bảng Department
 -- Cột Dept_Id là cột tự tăng giá trị.
 -- Câu lệnh Insert này không có vấn đề gì.
 Insert into department(Dept_Id,Name)
 values (null, 'Dept 1');

 -- Ghi ra log
 Select 'Two';  

 -- Trèn vào bảng Department
 -- Đã có một bản ghi trong DB với Dept_ID = 1
 --  
  Insert into department(Dept_Id,Name)
  values (1 , 'Dept 2');
 
 
  -- Ghi ra log
  Select 'One!! Lets go';
   
END
Khi bạn thực thi thủ tục trên:
  1. Các câu lệnh (1), (2), (3) sẽ chạy mà không có vấn đề gì
  2. Câu lệnh (4) sẽ gây ra lỗi do bạn trèn vào một bản ghi vào bảng Department trùng lặp khóa chính đã có sẵn trong DB, nó thoát ra khỏi thủ tục.
  3. Câu lệnh (5) sẽ không được thực thi.
Chúng ta sửa lại code của thủ tục trên, thêm vào các đoạn code sử lý ngoại lệ.
Handling_Exception_Example1 (2)
DROP Procedure if Exists `Handling_Exception_Example1`;

DELIMITER $$

CREATE PROCEDURE Handling_Exception_Example1()
BEGIN

-- Khai báo một bộ điều khiển lỗi 1062, Ghi ra một thông điệp.
-- Và tiếp tục làm việc (CONTINUE).
 DECLARE CONTINUE HANDLER FOR 1062 SELECT 'Found Error: Ignore to Continue'  as My_Message;

 -- Ghi ra log
 Select 'Three';  

 -- Trèn vào bảng Department
 -- Cột Dept_Id là cột tự tăng giá trị.
 -- Câu lệnh Insert này không có vấn đề gì.
 Insert into department(Dept_Id,Name)
 values (null, 'Dept 1');

 -- Ghi ra log
 Select 'Two';  

 -- Trèn vào bảng Department
 -- Đã có một bản ghi trong DB với Dept_ID = 1
 --  
  Insert into department(Dept_Id,Name)
  values (1 , 'Dept 2');
 
 
   -- Ghi ra log
  Select 'One!! Lets go';
   
END
Hãy xem hình minh họa khi bạn gọi thủ tục ở trên:
Bạn đã khai báo bộ điều khiển lỗi 1062, là loại CONTINUE nghĩa là sau bắt gặp lỗi 1062 chương trình vẫn tiếp tục chạy các dòng code phía dưới.

Khi bạn gọi thủ tục:
  1. Các dòng lệnh (1),(2),(3) chạy mà không có vấn đề gì cả.
  2. Câu lệnh (4) ném ra lỗi 1062 do đã tồn tại bản ghi Department có DEPT_ID = 1. Chương trình sử lý lỗi bằng cách ghi ra một dòng thông báo.
  3. Dòng code (5) vẫn tiếp tục được chạy.
Trong trường hợp bạn muốn chương trình thoát ra khỏi khối lệnh khi gặp lỗi, bạn có thể khai báo bộ điều khiển lỗi là kiểu EXIT.
-- Khai báo bộ sử lý lỗi là EXIT
-- Khi gặp lỗi 1062, nó ghi ra dòng thông báo của bạn và thoát ra khỏi khối lệnh
-- (Khối lệnh chứa đoạn lệnh gây lỗi).
DECLARE EXIT HANDLER FOR 1062 SELECT 'Found Error: Exit now!!' as My_Message;
Ví dụ 2:
Handling_Exception_Example2
DROP Procedure if Exists `Handling_Exception_Example2`;

DELIMITER $$

CREATE PROCEDURE Handling_Exception_Example2()
BEGIN

-- Khai báo một biến
Declare error1602 Varchar(1) default 'N';

-- Khai báo một bộ điều khiển lỗi 1062.
-- Nếu lỗi này xẩy ra trong khối, gán giá trị 'Y' vào cho biến error1602.  
-- Và tiếp tục làm việc (CONTINUE).
DECLARE CONTINUE HANDLER FOR 1062 Set error1602 = 'Y';

-- Ghi ra log
Select 'Three';  

-- Trèn vào bảng Department
-- Cột Dept_Id là cột tự tăng giá trị.
-- Câu lệnh Insert này không có vấn đề gì.
Insert into department(Dept_Id,Name)
values (null, 'Dept 1');

-- Ghi ra log
Select 'Two';  

-- Trèn vào bảng Department
-- Đã có một bản ghi trong DB với Dept_ID = 1
--  
 Insert into department(Dept_Id,Name)
 values (1 , 'Dept 2');

 if error1602 = 'Y' then
    Select 'Found error Duplicate Entry (1062)' as My_Message;  
 End if;

  -- Ghi ra log
 Select 'One!! Lets go';
 
END

5.2- Định nghĩa điều khiển ngoại lệ

Các ví dụ trên đã minh họa cách thức điều khiển ngoại lệ trong MySQL. Và giờ bạn có thể xem quy tắc tổng quát:
Cú pháp:
DECLARE <action> HANDLER FOR <condition_value> <statement>;

<action>

Có thể nhận hai giá trị CONTINUE hoặc EXIT.
  • CONTINUE: Nói với chương trình rằng khi lỗi xẩy ra hãy thực thi <statement> và tiếp tục.
  • EXIT: Nói với chương trình rằng khi lỗi xẩy ra hãy thực thi <statement> và thoát khỏi khối lệnh cha.

<condition_value>

Mô tả các đặc điểm của lỗi mà khi lỗi phát sinh, bộ điều khiển này sẽ được hoạt động. Nó có thể là:
  1. Một mã lỗi cụ thể (là một con số), ví dụ:
    • 1062 là lỗi khi trèn thêm một bản ghi mà ID của nó đã tồn tại.
  2. Một chuỗi string có 5 ký tự (Mã SQLSTATE chuẩn), ví dụ:
    • HY000 là mã lỗi nói rằng ổ đĩa bị đầy.
    • HY001 là lỗi do tràn bộ nhớ.
  3. Một lớp lỗi thông dụng đã được MySQL đặt tên hoặc có thể do người dùng đặt tên, chẳng hạn:
    • SQLWARNING: là các cảnh báo mà có mã chuẩn bắt đầu bởi '01'.
    • NOTFOUND: là lớp các lỗi có mã chuẩn (SQLSTATE) bắt đầu bởi '02'. Thường liên quan tới sử lý con trỏ.
    • SQLEXCEPTION: Là lớp các lỗi mà có mã chuẩn không bắt đầu bởi '00', '01', '02'. Chú ý rằng mã bắt đầu bởi '00' là các thông báo thành công.
 
 
Chú ý: Nếu một điều kiện phát sinh, mà bạn không khai báo một bộ điều khiển (handler) nào, chương trình sẽ sử lý theo mặc định, tùy thuộc vào lớp điều kiện:
  1. Với các điều kiện SQLEXCEPTION, chương trình thủ tục sẽ bị ngừng tại vị trí lỗi.
  2. Đối với các điều kiện SQLWARNING hoặc NOTFOUND, chương trình vẫn tiếp tục thực thi, giống như có bộ điều khiển CONTINUE (CONTINUE handler).

Cụ thể:

  1. Mã lỗi của MySQL là một con số, hoặc giá trị SQLSTATE là một chuỗi chữ 5 ký tự. Bạn không nên sử dụng mã lỗi 0 hoặc SQLSTATE mà giá trị bắt đầu bởi '00', bởi vì nó thông báo sự thành công chứ không phải một lỗi.
  2. SQLWARNING là viết tắt cho lớp các SQLSTATE có giá trị bắt đầu bởi '01'.
  3. NOT FOUND là viết tắt cho lớp các SQLSTATE có giá trị bắt đầu bởi '02'. Cái này phù hợp trong bối cảnh sử dụng con trỏ (Cursor) và được sử dụng để kiểm soát những gì sẽ xẩy ra khi con trỏ tiến tới cuối của một tập hợp dữ liệu. Khi không còn một bản ghi nào, Điều kiện khi không có dữ liệu sẽ phát sinh, với SQLSTATE có giá trị là '02000'. Để phát hiện điều kiện này, bạn có thể khai báo bộ điều khiển (Handler) cho nó (NOT FOUND). Bạn có thể xem ví dụ ở phần nói về con trỏ (Cursor). Điều kiện này cũng phát sinh cho câu lệnh  SELECT ... INTO var_list statements khi nó không có dòng dữ liệu nào.
  4. SQLEXCEPTION là viết tắt cho lớp các SQLSTATE mà giá trị không bắt đầu bởi '00', '01', or '02'.

Nếu một điều kiện phát sinh, mà không có một bộ điều khiển nào khai báo, thì hành động sử lý tùy thuộc vào lớp điều kiện:
  1. Với các điều kiện SQLEXCEPTION, chương trình thủ tục sẽ bị ngừng tại vị trí lệnh phát sinh điều kiện (ngoại lệ).
  2. Đối với các điều kiện SQLWARNING hoặc NOT FOUND, chương trình vẫn tiếp tục thực thi, giống như có bộ điều khiển CONTINUE (CONTINUE handler).

Bạn có thể tra cứu mã lỗi và các thông điệp lỗi tại:

Ví dụ với mã lỗi:
-- Lỗi trùng lặp khóa chính. (Khi insert)
DECLARE CONTINUE HANDLER FOR 1062
SELECT 'Error, duplicate key occurred';
Ví dụ với SQLException:
-- Các SQLSTATE không bắt đầu bởi '00', '01', '02':
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
   ROLLBACK;
   SELECT 'An error has occurred, operation rollbacked and the stored procedure was terminated';
END;

5.3- Chuẩn đoán lỗi (Get DIAGNOSTICS)

Một khi ngoại lệ xẩy ra, bạn muốn lấy các thông tin về lỗi, chẳng hạn mã lỗi, SQLSTATE, và thông điệp lỗi, các thông tin khác...
  • TODO

6- Con trỏ (Cursor)

6.1- Con trỏ là gì?

Cursor là kiểu biến có cấu trúc, cho phép bạn xử lý dữ liệu gồm nhiều dòng. Số dòng phụ thuộc vào câu lệnh truy vấn dữ liệu sau nó.Trong quá trình xử lý, bạn có thể thao tác với cursor thông qua từng dòng dữ liệu. Dòng dữ liệu này được định vị bởi một con trỏ. Với việc dịch chuyển con trỏ, bạn có thể lấy được toàn bộ dữ liệu của một dòng hiện tại.

6.2- Ví dụ với Con trỏ

Cursor_Example
DROP Procedure if Exists `Cursor_Example`;

DELIMITER $$

CREATE PROCEDURE Cursor_Example()
BEGIN

 --  
 -- Khai báo biến:
 Declare v_Emp_ID Integer ;
 Declare v_First_Name Varchar(50);
 Declare v_Last_Name Varchar(50);

 Declare v_Count Integer;

 -- Biến thông báo đã duyệt hết các bản ghi trên CURSOR.
 -- 1 nghĩa là vẫn còn bản ghi, 0 là không còn bản ghi trên CURSOR.
 Declare v_Found Integer default 1;    

 -- (Chú ý: Khai báo điều khiển ngoại lệ sau các khai báo Cursor).  
 -- Khai báo một con trỏ (CURSOR).
 Declare My_Cursor CURSOR
 for
 Select Emp.EMP_ID,Emp.FIRST_NAME, Emp.LAST_NAME
 From Employee Emp;

 -- (Chú ý: Khai báo điều khiển ngoại lệ sau các khai báo Cursor).
 -- Duyệt con trỏ từ bản ghi đầu tiên tới cuối cùng.
 -- Khi duyệt hết các bản ghi, ngoại lệ NOT FOUND sẽ ném ra
 -- Khi đó gán giá trị 0 cho v_Found.
 DECLARE CONTINUE HANDLER FOR NOT FOUND Set v_Found = 0;

 --
 Set v_Count = 0;

 -- Mở Cursor
 Open My_Cursor;

 -- Bắt đầu vòng lặp
  My_Loop : loop
    -- Con trỏ đang đứng tại 1 bản ghi.
    -- Đọc các giá trị cột gán vào các biến.
    fetch My_Cursor into v_Emp_ID,v_First_Name,v_Last_Name;
 
       -- Nếu đã duyệt hết các bản ghi trên CURSOR, thì thoát khỏi vòng lặp.
       if v_Found = 0 then
          Leave My_Loop;
       End if;
     
       -- Nếu nhân viên có tên bắt đầu bởi S
       if Substr(v_First_Name,1,1) = 'S' then
              -- In ra
              Select Concat('First Name: ',v_First_Name) as log_info;
              -- Tăng biến đếm lên 1.
              Set v_Count = v_Count +1;
             
       end if;

  end loop My_Loop;

  -- Đóng con trỏ.
  close My_Cursor;

  -- In ra số nhân viên có tên bắt đầu bởi S
  Select Concat('Emp Count: ',v_Count) as log_info;
 
END;

7- Sử lý giao dịch (Transaction)

  • TODO