Chuyển đến nội dung chính

Top 20 câu hỏi phỏng vấn PL/SQL và đáp án năm 2026

Chuẩn bị cho phỏng vấn PL/SQL với các chủ đề then chốt như hàm, trigger, SQL động, xử lý lỗi, thực hành tốt nhất và câu hỏi theo tình huống thực tế.
Đã cập nhật 16 thg 4, 2026  · 10 phút đọc

PL/SQL, là phần mở rộng ngôn ngữ thủ tục của SQL do Oracle phát triển, là thứ bạn cần phải biết nếu sẽ làm việc với cơ sở dữ liệu Oracle. Điều này đúng với các vai trò quan trọng như quản trị viên cơ sở dữ liệu, nhà phát triển và nhà phân tích dữ liệu, những người cần xử lý logic nghiệp vụ phức tạp ngay trong cơ sở dữ liệu.

Theo tôi, người phỏng vấn đánh giá ứng viên dựa trên ba khía cạnh chính:

  • Hiểu biết khái niệm: Nguyên lý cốt lõi, kiểu dữ liệu, cấu trúc điều khiển và xử lý ngoại lệ.
  • Khả năng mã hóa thực tiễn: Viết thủ tục lưu trữ, hàm, trigger và package hiệu quả.
  • Cân nhắc hiệu năng: Tối ưu mã PL/SQL để cải thiện thời gian thực thi, giảm tiêu thụ tài nguyên và tránh lỗi như chuyển ngữ cảnh quá mức giữa SQL và PL/SQL.

Để hỗ trợ, tôi cung cấp một hướng dẫn về các câu hỏi phỏng vấn PL/SQL. Tôi sẽ bắt đầu với các khái niệm nền tảng và tiến tới các chủ đề nâng cao như xử lý hàng loạt, SQL động và tinh chỉnh hiệu năng.

Nếu bạn muốn hiểu các khái niệm nền tảng của cơ sở dữ liệu Oracle, tôi khuyến nghị tham gia khóa học Introduction to Oracle SQL của chúng tôi, một tài nguyên rất quan trọng để học cách tương tác với cơ sở dữ liệu Oracle và cách cơ sở dữ liệu sử dụng PL/SQL để xử lý truy vấn.

Câu hỏi phỏng vấn PL/SQL cho người mới bắt đầu

Ở giai đoạn đầu của buổi phỏng vấn, người phỏng vấn có thể hỏi các câu hỏi nền tảng để đánh giá kiến thức của bạn về cơ sở dữ liệu và các khái niệm PL/SQL cơ bản. Hãy học các câu hỏi và câu trả lời này để chuẩn bị cho giai đoạn khởi đầu của buổi phỏng vấn.

1. PL/SQL là gì?

PL/SQL là phần mở rộng thủ tục của SQL do Oracle cung cấp. Khác với SQL mang tính khai báo và tập trung vào truy xuất cũng như thao tác dữ liệu, PL/SQL cho phép nhà phát triển triển khai logic thủ tục, khiến nó trở thành công cụ mạnh mẽ để viết các quy tắc nghiệp vụ phức tạp ngay trong cơ sở dữ liệu. Nó hỗ trợ biến, vòng lặp, điều kiện, xử lý ngoại lệ và lập trình mô-đun thông qua thủ tục, hàm và package.

2. Cấu trúc cơ bản của một khối PL/SQL là gì?

Một khối PL/SQL là đơn vị thực thi cơ bản trong PL/SQL và gồm bốn phần chính:

  • DECLARE (Tùy chọn): Dùng để định nghĩa biến, hằng, con trỏ và kiểu do người dùng định nghĩa.

  • BEGIN: Phần thực thi nơi viết các truy vấn SQL và câu lệnh thủ tục.

  • EXCEPTION (Tùy chọn): Xử lý lỗi và ngoại lệ khi chạy để đảm bảo khôi phục lỗi êm ái.

  • END;: Đánh dấu kết thúc khối.

3. Các kiểu dữ liệu thiết yếu trong PL/SQL là gì?

PL/SQL hỗ trợ nhiều kiểu dữ liệu, được phân loại như sau:

  • Kiểu vô hướng: Kiểu đơn giá trị như NUMBER, VARCHAR2, DATE, BOOLEAN.

  • Kiểu tổng hợp: Tập hợp như RECORD (cấu trúc tùy chỉnh) và TABLE/VARRAY (mảng).

  • Kiểu tham chiếu: Con trỏ tới đối tượng cơ sở dữ liệu, như REF CURSOR để xử lý truy vấn động.

4. Các cấu trúc điều khiển cơ bản trong PL/SQL là gì?

PL/SQL bao gồm một số cấu trúc điều khiển giúp quản lý luồng chương trình:

  • Vòng lặp: Gồm LOOP, FOR LOOPWHILE LOOP, cho phép thực thi lặp lại các câu lệnh.

  • Câu lệnh điều kiện: Bao gồm IFCASE, thực thi các khối mã khác nhau dựa trên điều kiện. Hàm DECODE() là một ví dụ hay về điều kiện mà bạn nên nghiên cứu. 

Câu hỏi phỏng vấn PL/SQL mức trung cấp

Sau khi đã đề cập các câu hỏi cơ bản, giờ hãy chuyển sang một số câu hỏi phỏng vấn về cấu trúc dữ liệu ở mức trung cấp. Sau khi kiểm tra kiến thức nền, người phỏng vấn sẽ đánh giá khả năng kỹ thuật của bạn trong việc triển khai và sử dụng các khái niệm PL/SQL.

5. Sự khác nhau giữa thủ tục lưu trữ và hàm là gì?

Thủ tục lưu trữ và hàm đều là các khối mã PL/SQL có thể tái sử dụng, nhưng chúng phục vụ mục đích khá khác nhau.

Thủ tục lưu trữ được dùng để thực hiện các thao tác không trả về giá trị, như chèn, cập nhật hoặc xóa dữ liệu. Chúng dùng cho các tác vụ sửa đổi dữ liệu hoặc thực hiện các thao tác phức tạp mà không trả về kết quả.

Ví dụ, thủ tục dưới đây cập nhật salary của nhân viên có employee_id được cung cấp bằng cách cộng thêm p_increment chỉ định để lương được cập nhật động dựa trên tham số đầu vào,

CREATE PROCEDURE update_salary(p_emp_id NUMBER, p_increment NUMBER) AS  
BEGIN  
    -- Update the salary of the employee with the given ID  
    UPDATE employees  
    SET salary = salary + p_increment  
    WHERE employee_id = p_emp_id;  
END;

Mặt khác, hàm trả về một giá trị sau khi thực hiện thao tác. Chúng phù hợp cho các phép tính toán hoặc truy xuất dữ liệu cần trả về kết quả.

Hàm dưới đây giúp đơn giản hóa việc lấy lương của một nhân viên, giúp tái sử dụng trong các truy vấn SQL hoặc thủ tục khác.

CREATE FUNCTION get_employee_salary(p_emp_id NUMBER) RETURN NUMBER AS  
    v_salary NUMBER;  
BEGIN  
    -- Retrieve the salary for the given employee ID  
    SELECT salary INTO v_salary  
    FROM employees  
    WHERE employee_id = p_emp_id;  
    
    -- Return the retrieved salary  
    RETURN v_salary;  
END;

6. PL/SQL package là gì?

Package trong PL/SQL là tập hợp các thủ tục, hàm và biến có liên quan, giúp đóng gói mã để tổ chức và tái sử dụng tốt hơn. Chúng gồm hai phần:

  • Định nghĩa package (Specification): Khai báo các thành phần public (thủ tục, hàm, biến).
  • Thân package (Body): Chứa chi tiết triển khai của các thủ tục và hàm được khai trong specification.

Ví dụ, truy vấn dưới đây tạo package employee_pkg định nghĩa một thủ tục tăng lương cho nhân viên và một hàm lấy tổng số nhân viên, với phần triển khai sẽ được cung cấp trong thân package.

-- Create a package named 'employee_pkg'
CREATE PACKAGE employee_pkg AS  

    -- Procedure to increase an employee's salary by a percentage  
    PROCEDURE raise_salary(p_emp_id NUMBER, p_percent NUMBER);  

    -- Function to return the total number of employees  
    FUNCTION get_total_employees RETURN NUMBER;  

END employee_pkg;

7. Trigger trong PL/SQL là gì?

Trigger là các khối PL/SQL tự động thực thi để phản hồi các sự kiện cụ thể trong cơ sở dữ liệu, như chèn, cập nhật hoặc xóa. Trigger được dùng để áp đặt quy tắc nghiệp vụ. Chúng cũng thường được dùng để phục vụ kiểm toán. Chúng được phân loại thành:

  • Trigger mức dòng (Row-Level): Thực thi một lần cho mỗi dòng bị ảnh hưởng.
  • Trigger mức câu lệnh (Statement-Level): Thực thi một lần cho mỗi câu lệnh SQL, bất kể số dòng bị ảnh hưởng.

Truy vấn dưới đây tạo trigger AFTER UPDATE trg_salary_audit trên bảng employees để ghi lại các thay đổi lương vào bảng salary_audit, lưu ID nhân viên, lương cũ và mới, cùng thời điểm cập nhật.

-- Create or replace a trigger 'trg_salary_audit'
CREATE OR REPLACE TRIGGER trg_salary_audit  
AFTER UPDATE OF salary ON employees  -- Fires after salary updates in 'employees' table
FOR EACH ROW  -- Executes for each updated row
BEGIN  
    -- Inserts old and new salary details into 'salary_audit' table
    INSERT INTO salary_audit (employee_id, old_salary, new_salary, change_date)  
    VALUES (:OLD.employee_id, :OLD.salary, :NEW.salary, SYSDATE);  
END;

Hãy học bài hướng dẫn về SQL Triggers của chúng tôi để sẵn sàng nếu chủ đề trigger được nêu trong buổi phỏng vấn. 

8. Các phương pháp xử lý ngoại lệ trong PL/SQL là gì?

PL/SQL cung cấp cơ chế xử lý lỗi nhằm đảm bảo ổn định và ngăn chặn sập hệ thống. Các loại ngoại lệ bao gồm:

  • Ngoại lệ định nghĩa sẵn: Các ngoại lệ tích hợp như NO_DATA_FOUND, TOO_MANY_ROWSZERO_DIVIDE.

  • Ngoại lệ do người dùng định nghĩa: Ngoại lệ tùy chỉnh được khai báo bằng EXCEPTION và kích hoạt bằng RAISE.

Ví dụ, khối PL/SQL sau truy xuất lương của nhân viên có ID 100, kích hoạt một ngoại lệ tùy chỉnh nếu lương dưới 1000, và xử lý các lỗi có thể xảy ra, bao gồm không tìm thấy bản ghi nhân viên và các ngoại lệ không mong đợi.

DECLARE  
    v_salary NUMBER;  -- Variable to store the employee's salary  
    e_low_salary EXCEPTION;  -- Custom exception for low salary  

BEGIN  
    -- Retrieve salary of employee with ID 100  
    SELECT salary INTO v_salary FROM employees WHERE employee_id = 100;  

    -- Raise exception if salary is below 1000  
    IF v_salary < 1000 THEN  
        RAISE e_low_salary;  
    END IF;  

EXCEPTION  
    WHEN NO_DATA_FOUND THEN  -- Handle case where employee ID is not found  
        DBMS_OUTPUT.PUT_LINE('Employee not found.');  
    WHEN e_low_salary THEN  -- Handle custom low salary exception  
        DBMS_OUTPUT.PUT_LINE('Salary is below the allowed threshold.');  
    WHEN OTHERS THEN  -- Handle any other unexpected errors  
        DBMS_OUTPUT.PUT_LINE('An unexpected error occurred: ' || SQLERRM);  

END;

Các thực hành tốt nhất cho xử lý ngoại lệ gồm:

  • Luôn dự liệu các ngoại lệ tiềm ẩn.
  • Sử dụng thông điệp lỗi có ý nghĩa.
  • Ghi log ngoại lệ để kiểm toán.
  • Giữ mã xử lý ngoại lệ ngắn gọn và tập trung vào khôi phục.

9. Làm thế nào để xác minh câu lệnh UPDATE có được thực thi hay không?

Thuộc tính SQL %NOTFOUND có thể dùng để xác định liệu câu lệnh UPDATE có thay đổi bản ghi nào hay không. Nếu câu lệnh SQL cuối cùng chạy không ảnh hưởng đến dòng nào, biến này trả về TRUE.

Ví dụ, truy vấn dưới đây cập nhật lương của nhân viên ở phòng ban 10 tăng 10%, rồi kiểm tra xem có dòng nào bị ảnh hưởng bởi câu lệnh UPDATE bằng thuộc tính %NOTFOUND. Nếu không có dòng nào được cập nhật, nó in thông điệp "No rows were updated." Nếu có dòng được cập nhật, nó in số dòng bị ảnh hưởng bằng thuộc tính SQL%ROWCOUNT.

DECLARE
    -- Declare a variable to store the number of rows updated
    rows_updated INTEGER;
BEGIN
    -- Perform an UPDATE statement on the 'employees' table
    UPDATE employees
    SET salary = salary * 1.1
    WHERE department_id = 10;

    -- Check if any rows were updated by using %NOTFOUND
    IF SQL%NOTFOUND THEN
        -- If no rows were updated, print a message
        DBMS_OUTPUT.PUT_LINE('No rows were updated.');
    ELSE
        -- If rows were updated, print how many rows were affected
        rows_updated := SQL%ROWCOUNT;  -- Store the number of rows updated
        DBMS_OUTPUT.PUT_LINE(rows_updated || ' rows were updated.');
    END IF;
END;
/

Câu hỏi phỏng vấn PL/SQL nâng cao

Giờ hãy khám phá một số câu hỏi nâng cao trong trường hợp bạn ứng tuyển vai trò cao cấp hơn đòi hỏi nhiều kinh nghiệm.

10. Các phương pháp tối ưu hiệu năng trong PL/SQL là gì?

Giảm thiểu chuyển ngữ cảnh giữa SQL và PL/SQL là tối quan trọng để tối ưu hiệu năng. Mỗi lần chuyển gây ra chi phí, có thể làm chậm thời gian thực thi, đặc biệt trong các tình huống có nhiều lần chuyển qua lại.

11. Bạn sử dụng thao tác hàng loạt để giảm chuyển ngữ cảnh như thế nào?

PL/SQL cung cấp kỹ thuật xử lý hàng loạt để tối ưu tương tác SQL–PL/SQL bằng cách truy xuất hoặc sửa nhiều dòng cùng lúc.

Ví dụ, khối PL/SQL dưới đây truy xuất tất cả nhân viên từ phòng ban 10 bằng BULK COLLECT vào một collection và lặp qua để in tên từng nhân viên, cải thiện hiệu năng bằng cách giảm chuyển ngữ cảnh giữa SQL và PL/SQL.

DECLARE  
    -- Define a table-type collection based on the 'employees' table structure  
    TYPE emp_table IS TABLE OF employees%ROWTYPE;  
    v_emps emp_table;  -- Declare a variable of this type  

BEGIN  
    -- Bulk fetch employees from department 10 into the collection  
    SELECT * BULK COLLECT INTO v_emps FROM employees WHERE department_id = 10;  

    -- Loop through the collection and print employee names  
    FOR i IN 1..v_emps.COUNT LOOP  
        DBMS_OUTPUT.PUT_LINE('Employee: ' || v_emps(i).employee_name);  
    END LOOP;  

END;

Ngoài ra, khối PL/SQL này sử dụng FORALL để cập nhật hàng loạt, tăng lương 10% cho các nhân viên có ID 101, 102 và 103.

DECLARE  
    -- Define a table-type collection for employee IDs  
    TYPE t_emp_ids IS TABLE OF employees.employee_id%TYPE;  
    -- Initialize collection with specific employee IDs  
    v_emp_ids t_emp_ids := t_emp_ids(101, 102, 103);  

BEGIN  
    -- Bulk update salaries by 10% for specified employee IDs  
    FORALL i IN 1..v_emp_ids.COUNT  
        UPDATE employees SET salary = salary * 1.10 WHERE employee_id = v_emp_ids(i);  

END;

12. SQL động và Ref Cursor là gì?

SQL động cho phép thực thi câu lệnh SQL một cách động tại thời gian chạy, hữu ích khi làm việc với tên bảng, cột hoặc cấu trúc truy vấn thay đổi.

Khối PL/SQL sau sử dụng SQL động để đếm số dòng trong bảng employees và in kết quả. Tôi thích cách tiếp cận này vì nó mang lại tính linh hoạt.

DECLARE  
    v_table_name VARCHAR2(50) := 'employees';  -- Store table name  
    v_count NUMBER;  -- Variable to hold row count  

BEGIN  
    -- Dynamically count rows in the specified table  
    EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || v_table_name INTO v_count;  

    -- Print the total count  
    DBMS_OUTPUT.PUT_LINE('Total Employees: ' || v_count);  

END;  

Ref Cursor là các con trỏ động có thể được mở, đọc và đóng tại thời gian chạy, cho phép truyền kết quả truy vấn giữa các đơn vị chương trình.

Khối PL/SQL dưới đây dùng REF CURSOR để lấy và in tên nhân viên từ phòng ban 20. Con trỏ được mở động, lặp qua bằng vòng lặp và đóng sau khi xử lý.

DECLARE  
    -- Define a REF CURSOR type  
    TYPE emp_ref_cursor IS REF CURSOR;  
    v_cursor emp_ref_cursor;  -- Declare a cursor variable  
    v_name employees.employee_name%TYPE;  -- Variable to store employee name  

BEGIN  
    -- Open the cursor for employees in department 20  
    OPEN v_cursor FOR SELECT employee_name FROM employees WHERE department_id = 20;  

    -- Fetch and print employee names in a loop  
    LOOP  
        FETCH v_cursor INTO v_name;  
        EXIT WHEN v_cursor%NOTFOUND;  
        DBMS_OUTPUT.PUT_LINE('Employee: ' || v_name);  
    END LOOP;  

    -- Close the cursor  
    CLOSE v_cursor;  

END;

13. Bạn xử lý lỗi bảng biến đổi (mutating table) như thế nào?

Lỗi bảng biến đổi xảy ra khi một trigger cố gắng sửa đổi chính bảng kích hoạt nó. Trigger tổng hợp (compound trigger) cho phép chia quá trình thực thi thành nhiều pha như BEFORE, AFTERFOR EACH ROW để tránh vấn đề sửa đổi trực tiếp.

Trigger tổng hợp dưới đây ghi log thay đổi lương vào bảng salary_audit một cách hiệu quả bằng cách thu thập dữ liệu trước mỗi lần cập nhật dòng và thực hiện chèn hàng loạt sau câu lệnh, giảm chuyển ngữ cảnh và cải thiện hiệu năng.

CREATE OR REPLACE TRIGGER trg_salary_audit  
FOR UPDATE OF salary ON employees  -- Trigger fires on salary updates  
COMPOUND TRIGGER  

    -- Declare an associative array to store audit records  
    TYPE t_salary_audit IS TABLE OF salary_audit%ROWTYPE INDEX BY PLS_INTEGER;  
    v_audit_data t_salary_audit;  
    v_idx PLS_INTEGER := 0;  

    -- Before updating each row, store old and new salary details  
    BEFORE EACH ROW IS  
    BEGIN  
        v_idx := v_idx + 1;  
        v_audit_data(v_idx).employee_id := :OLD.employee_id;  
        v_audit_data(v_idx).old_salary := :OLD.salary;  
        v_audit_data(v_idx).new_salary := :NEW.salary;  
        v_audit_data(v_idx).change_date := SYSDATE;  
    END BEFORE EACH ROW;  

    -- After the statement, insert all audit records in bulk  
    AFTER STATEMENT IS  
    BEGIN  
        FORALL i IN 1..v_idx  
            INSERT INTO salary_audit VALUES v_audit_data(i);  
    END AFTER STATEMENT;  

END trg_salary_audit;

14. Nạp chồng (overloading) trong PL/SQL là gì?

Nạp chồng cho phép định nghĩa nhiều thủ tục hoặc hàm có cùng tên nhưng tham số khác nhau trong một package. Điều này nâng cao khả năng đọc và bảo trì mã bằng cách cung cấp nhiều cách để thực hiện các thao tác tương tự.

Trong truy vấn dưới đây, thân package triển khai hai thủ tục nạp chồng tên update_salary: Một thủ tục tăng lương của nhân viên theo một mức chỉ định, thủ tục còn lại đặt mức lương mới kèm ngày hiệu lực, cập nhật bảng employees tương ứng.

-- Create the package specification
CREATE OR REPLACE PACKAGE emp_pkg AS  
    -- Procedure to increment salary by a specified amount  
    PROCEDURE update_salary(p_emp_id NUMBER, p_increment NUMBER);

    -- Overloaded procedure to set a new salary with an effective date  
    PROCEDURE update_salary(p_emp_id NUMBER, p_new_salary NUMBER, p_effective_date DATE);
END emp_pkg;
/
-- Create the package body
CREATE OR REPLACE PACKAGE BODY emp_pkg AS  

    -- Procedure to increment salary by a specified amount  
    PROCEDURE update_salary(p_emp_id NUMBER, p_increment NUMBER) AS  
    BEGIN  
        UPDATE employees  
        SET salary = salary + p_increment  
        WHERE employee_id = p_emp_id;  
    END update_salary;  

    -- Overloaded procedure to set a new salary with an effective date  
    PROCEDURE update_salary(p_emp_id NUMBER, p_new_salary NUMBER, p_effective_date DATE) AS  
    BEGIN  
        UPDATE employees  
        SET salary = p_new_salary, last_update = p_effective_date  
        WHERE employee_id = p_emp_id;  
    END update_salary;  

END emp_pkg;
/

15. Chỉ thị biên dịch và pragma trong PL/SQL là gì?

PL/SQL cung cấp các chỉ thị biên dịch (PRAGMA) để tối ưu mã và xử lý ngoại lệ. Các pragma phổ biến gồm:

  • PRAGMA EXCEPTION_INIT: Gắn một ngoại lệ do người dùng định nghĩa với mã lỗi Oracle.

  • PRAGMA SERIALLY_REUSABLE: Tối ưu sử dụng bộ nhớ của package để mở rộng quy mô.

Khối PL/SQL dưới đây xử lý việc chèn một nhân viên có mức lương không hợp lệ bằng cách dùng ngoại lệ tùy chỉnh e_invalid_salary ánh xạ tới mã lỗi -20001. Nếu ngoại lệ được kích hoạt, nó in thông báo lỗi.

DECLARE  
    e_invalid_salary EXCEPTION;  -- Declare custom exception for invalid salary  
    PRAGMA EXCEPTION_INIT(e_invalid_salary, -20001);  -- Associate exception with error code -20001  
BEGIN  
    -- Attempt to insert an employee with invalid salary  
    INSERT INTO employees (employee_id, salary) VALUES (999, -1000);  
EXCEPTION  
    -- Handle the custom exception and print a message  
    WHEN e_invalid_salary THEN  
        DBMS_OUTPUT.PUT_LINE('Invalid salary detected!');  
END;  

16. Các phương pháp khác nhau để truy vết và gỡ lỗi mã PL/SQL là gì?

PL/SQL cung cấp một số package tích hợp để truy vết và gỡ lỗi hiệu năng mã. Các phương pháp phổ biến gồm dùng DBMS_TRACE để theo dõi luồng thực thi, DBMS_APPLICATION_INFO để giám sát hoạt động phiên, và DBMS_SESSION để thu thập thông tin chẩn đoán ở mức phiên.

Truy vấn dưới đây trước tiên bật truy vết SQL bằng DBMS_SESSION.set_sql_trace(TRUE), sau đó đặt thông tin phiên dành riêng cho client với DBMS_APPLICATION_INFO.set_client_info() để giám sát. Khối PL/SQL được thực thi, mô phỏng một thao tác UPDATE. Cuối cùng, tắt truy vết sau khi thực thi bằng DBMS_SESSION.set_sql_trace(FALSE).

-- Enable tracing for the current session using DBMS_SESSION
BEGIN
   -- Start session-level tracing
   DBMS_SESSION.set_sql_trace(TRUE);
END;
/

-- Set application information using DBMS_APPLICATION_INFO
BEGIN
   -- Set the application name and action for session monitoring
   DBMS_APPLICATION_INFO.set_client_info('Trace Debug Session');
   DBMS_APPLICATION_INFO.set_action('Debugging PL/SQL Code');
END;
/

-- Example PL/SQL block that simulates a process for debugging
DECLARE
   v_employee_id NUMBER := 100;
BEGIN
   -- Example query to fetch employee details
   FOR rec IN (SELECT first_name, last_name FROM employees WHERE employee_id = v_employee_id) LOOP
      DBMS_OUTPUT.put_line('Employee: ' || rec.first_name || ' ' || rec.last_name);
   END LOOP;
   
   -- Simulate some logic that could be traced
   IF v_employee_id = 100 THEN
      DBMS_OUTPUT.put_line('Employee ID is 100');
   END IF;
END;
/

-- Disable tracing after the session is complete
BEGIN
   -- Stop session-level tracing
   DBMS_SESSION.set_sql_trace(FALSE);
END;
/

Câu hỏi phỏng vấn PL/SQL theo tình huống

Trong các ứng dụng doanh nghiệp thực tế, PL/SQL được dùng để xử lý logic nghiệp vụ phức tạp. Người phỏng vấn thường đánh giá khả năng ứng viên áp dụng PL/SQL vào các kịch bản thực tiễn. Dưới đây là một số thách thức trọng yếu cùng chiến lược giải quyết.

Với phần cuối cùng tiếp theo, nếu bạn đang đi phỏng vấn, tôi khuyến khích bạn suy nghĩ cách trả lời các câu hỏi theo khuôn mẫu này, nhưng tất nhiên hãy điều chỉnh câu trả lời cho đúng với ngành của bạn hoặc phản ánh trải nghiệm riêng. Điều tương tự cũng đúng nếu bạn đang phỏng vấn ứng viên và muốn có ý tưởng về cách đặt câu hỏi và đánh giá mức độ đầy đủ của câu trả lời, trong trường hợp đó tôi hy vọng phần này sẽ là nguồn cảm hứng cho cả câu hỏi và tiêu chí đánh giá của bạn.

17. Bạn thiết kế trigger trong môi trường đồng thời cao như thế nào?

Giả sử tình huống bạn cần áp đặt quy tắc nghiệp vụ theo đó lương của một nhân viên không được cập nhật quá một lần mỗi ngày. Tuy nhiên, cơ sở dữ liệu có mức độ giao dịch đồng thời cao, và một trigger đơn giản có thể dẫn tới tranh chấp hoặc vấn đề hiệu năng.

Thay vì dùng trigger mức dòng kích hoạt cho mọi cập nhật và có thể làm chậm hiệu năng, hãy dùng trigger mức câu lệnh kèm bảng log để ngăn nhiều lần cập nhật lương trong cùng một ngày.

Ví dụ, trigger dưới đây ngăn việc cập nhật lương nhiều lần cho một nhân viên trong cùng ngày bằng cách kiểm tra bảng salary_update_log trước khi cho phép cập nhật. Nếu lương đã được cập nhật hôm nay, sẽ phát sinh lỗi; nếu không, ngày cập nhật sẽ được ghi log.

-- Create a table to log the last salary update for each employee
CREATE TABLE salary_update_log (
    employee_id NUMBER PRIMARY KEY,  -- Employee ID as primary key
    last_update DATE  -- Date of the last salary update
);

-- Create or replace a compound trigger to prevent multiple salary updates on the same day
CREATE OR REPLACE TRIGGER trg_prevent_multiple_salary_update  
FOR UPDATE OF salary ON employees  
COMPOUND TRIGGER  

    -- Declare a variable to store last salary update date per row
    TYPE emp_log_type IS TABLE OF DATE INDEX BY PLS_INTEGER;
    emp_log emp_log_type;

    BEFORE STATEMENT IS  
    BEGIN  
        -- Load existing salary update logs into memory for reference
        FOR rec IN (SELECT employee_id, last_update FROM salary_update_log) LOOP  
            emp_log(rec.employee_id) := rec.last_update;  
        END LOOP;  
    END BEFORE STATEMENT;  

    BEFORE EACH ROW IS  
    BEGIN  
        -- Check if an entry exists for this employee
        IF emp_log.EXISTS(:NEW.employee_id) THEN  
            -- Validate if salary was updated today
            IF emp_log(:NEW.employee_id) = TRUNC(SYSDATE) THEN  
                RAISE_APPLICATION_ERROR(-20010, 'Salary can only be updated once per day');  
            ELSE  
                -- Update log in memory
                emp_log(:NEW.employee_id) := TRUNC(SYSDATE);  
            END IF;  
        ELSE  
            -- Insert a new log entry into memory
            emp_log(:NEW.employee_id) := TRUNC(SYSDATE);  
        END IF;  
    END BEFORE EACH ROW;  

    AFTER STATEMENT IS  
    BEGIN  
        -- Apply changes to the log table after all row updates
        FOR i IN emp_log.FIRST .. emp_log.LAST LOOP  
            MERGE INTO salary_update_log l  
            USING (SELECT i AS employee_id, emp_log(i) AS last_update FROM DUAL) s  
            ON (l.employee_id = s.employee_id)  
            WHEN MATCHED THEN  
                UPDATE SET l.last_update = s.last_update  
            WHEN NOT MATCHED THEN  
                INSERT (employee_id, last_update) VALUES (s.employee_id, s.last_update);  
        END LOOP;  
    END AFTER STATEMENT;  

END trg_prevent_multiple_salary_update;

Ví dụ trên sẽ giảm khóa mức dòng và tối thiểu hóa xung đột giao dịch trong quá trình cập nhật hàng loạt.

18. Bạn chia nhỏ giao dịch lớn thành các phần nhỏ hơn để đảm bảo độ tin cậy như thế nào?

Hãy tưởng tượng một hệ thống ngân hàng cần cập nhật hàng loạt để điều chỉnh lãi suất cho hàng triệu tài khoản khách hàng. Thực thi một giao dịch lớn duy nhất có thể khóa bảng quá lâu hoặc dẫn đến lỗi rollback.

Trong trường hợp này, hãy dùng xử lý hàng loạt với COMMIT theo lô để xử lý dữ liệu dần dần và tránh tranh chấp.

Khối PL/SQL dưới đây triển khai giải pháp bằng cách dùng BULK COLLECT để lấy các ID tài khoản rồi lặp qua để cập nhật lãi suất tăng 5%. Nó thực hiện commit sau mỗi 1000 cập nhật để cải thiện hiệu năng và giảm sử dụng tài nguyên. Một lần commit cuối đảm bảo lưu mọi cập nhật còn lại.

DECLARE  
    -- Define a collection type for account IDs  
    TYPE t_accounts IS TABLE OF NUMBER;  -- Use NUMBER instead of referencing accounts.account_id
    v_account_ids t_accounts;  -- Variable to store account IDs  
    v_batch_size CONSTANT NUMBER := 1000;  -- Batch size for commits  

BEGIN  
    -- Bulk collect all account IDs into the collection  
    SELECT account_id BULK COLLECT INTO v_account_ids FROM accounts;  

    -- Loop through each account ID to update the interest rate  
    FOR i IN 1 .. v_account_ids.COUNT LOOP  
        UPDATE accounts  
        SET interest_rate = interest_rate * 1.05  -- Increase interest rate by 5%  
        WHERE account_id = v_account_ids(i);  

        -- Commit after every 1000 updates  
        IF MOD(i, v_batch_size) = 0 THEN  
            COMMIT;  
        END IF;  
    END LOOP;  

    COMMIT; -- Final commit for any remaining updates  
END;
/

Giải pháp này sẽ đảm bảo độ tin cậy trong các hệ thống tài chính như trả lương bằng cách ngăn thất bại giao dịch.

19. Bạn tổ chức logic phức tạp vào package để dễ bảo trì như thế nào?

Một công ty bán lẻ cần triển khai hệ thống xử lý đơn hàng gồm nhiều bước: Kiểm tra tồn kho, giữ hàng, tính chiết khấu và ghi log lịch sử giao dịch.

Thay vì viết các thủ tục độc lập riêng lẻ, bạn cần tổ chức logic này theo cách có cấu trúc, dễ bảo trì. Do đó, hãy dùng PL/SQL package để đóng gói các thủ tục và hàm liên quan, cải thiện khả năng tái sử dụng và bảo trì mã

Định nghĩa package dưới đây xác định các hàm và thủ tục cho xử lý đơn hàng, bao gồm kiểm tra tồn kho, giữ hàng, tính chiết khấu và ghi log trạng thái giao dịch. Nó cung cấp cách tiếp cận mô-đun để xử lý các tác vụ liên quan đến đơn hàng.

-- Create a package specification for order processing functions and procedures
CREATE PACKAGE order_processing_pkg AS  

    -- Function to check if enough inventory is available for the product
    FUNCTION check_inventory(p_product_id NUMBER, p_quantity NUMBER) RETURN BOOLEAN;  

    -- Procedure to reserve stock for a specific order
    PROCEDURE reserve_stock(p_order_id NUMBER, p_product_id NUMBER, p_quantity NUMBER);  

    -- Function to calculate discount based on customer and total amount
    FUNCTION calculate_discount(p_customer_id NUMBER, p_total_amount NUMBER) RETURN NUMBER;  

    -- Procedure to log the status of a transaction
    PROCEDURE log_transaction(p_order_id NUMBER, p_status VARCHAR2);  

END order_processing_pkg;

Sau đó, chúng ta dùng thân package sau để triển khai các hàm và thủ tục được định nghĩa trong specification. Nó xử lý các tác vụ chính như kiểm tra tồn kho, giữ hàng, tính chiết khấu và ghi log trạng thái giao dịch, đảm bảo hoạt động xử lý đơn hàng trơn tru.

-- Create the package body implementing the functions and procedures for order processing
CREATE PACKAGE BODY order_processing_pkg AS  

    -- Function to check if sufficient inventory is available for the product
    FUNCTION check_inventory(p_product_id NUMBER, p_quantity NUMBER) RETURN BOOLEAN AS  
        v_available_qty NUMBER;  -- Variable to store available quantity  
    BEGIN  
        -- Retrieve the available quantity from inventory
        SELECT stock_quantity INTO v_available_qty FROM inventory WHERE product_id = p_product_id;  
        -- Return true if enough stock is available, otherwise false
        RETURN v_available_qty >= p_quantity;  
    END check_inventory;  

    -- Procedure to reserve stock for a specific order
    PROCEDURE reserve_stock(p_order_id NUMBER, p_product_id NUMBER, p_quantity NUMBER) AS  
    BEGIN  
        -- Deduct the ordered quantity from the inventory
        UPDATE inventory SET stock_quantity = stock_quantity - p_quantity WHERE product_id = p_product_id;  
    END reserve_stock;  

    -- Function to calculate a discount based on the total order amount
    FUNCTION calculate_discount(p_customer_id NUMBER, p_total_amount NUMBER) RETURN NUMBER AS  
        v_discount NUMBER := 0;  -- Initialize discount to 0  
    BEGIN  
        -- Apply 10% discount if the total amount is greater than 500
        IF p_total_amount > 500 THEN  
            v_discount := p_total_amount * 0.10;  
        END IF;  
        -- Return the calculated discount
        RETURN v_discount;  
    END calculate_discount;  

    -- Procedure to log the transaction status
    PROCEDURE log_transaction(p_order_id NUMBER, p_status VARCHAR2) AS  
    BEGIN  
        -- Insert a log entry for the order status
        INSERT INTO order_log (order_id, status, log_date) VALUES (p_order_id, p_status, SYSDATE);  
    END log_transaction;  

END order_processing_pkg;

20. Bạn xử lý deadlock trong hệ thống giao dịch cao như thế nào?

Giả sử một hệ thống tài chính thường xuyên cập nhật đồng thời nhiều bảng liên quan. Deadlock xảy ra khi hai giao dịch chờ tài nguyên bị khóa của nhau, gây nút thắt hiệu năng.

Để giải quyết, hãy luôn khóa các dòng theo một thứ tự nhất quán giữa các giao dịch. Đồng thời, dùng mệnh đề NOWAIT hoặc SKIP LOCKED để tránh chờ vô thời hạn.

Ví dụ, khối PL/SQL dưới đây cố gắng khóa một dòng cụ thể trong bảng accounts để cập nhật bằng FOR UPDATE NOWAIT, khiến giao dịch thất bại ngay nếu phiên khác đã khóa dòng. Sau khi khóa, nó cập nhật trạng thái giao dịch và commit thay đổi. Nếu xảy ra lỗi, nó bắt ngoại lệ và in thông báo lỗi.

DECLARE  
    v_balance NUMBER(15,2);  -- Declare variable to store the account balance  
BEGIN  
    -- Lock the account row for update to prevent other sessions from modifying it  
    SELECT balance INTO v_balance FROM accounts  
    WHERE account_id = 101 FOR UPDATE NOWAIT;  

    -- Update the transaction status to 'Processed' for all transactions related to this account  
    UPDATE transactions  
    SET status = 'Processed'  
    WHERE account_id = 101 AND status = 'Pending';  -- Update only pending transactions  

    COMMIT;  -- Commit the changes  

EXCEPTION  
    -- Handle errors, such as locking issues or unexpected exceptions  
    WHEN OTHERS THEN  
        DBMS_OUTPUT.PUT_LINE('Transaction failed: ' || SQLERRM);  
        ROLLBACK;  -- Ensure rollback in case of failure  
END;
/

Tương tự, khối PL/SQL này xử lý các giao dịch đang chờ bằng cách khóa chúng để cập nhật với FOR UPDATE SKIP LOCKED, cho phép bỏ qua các dòng đang bị phiên khác khóa. Nó cập nhật trạng thái của từng giao dịch sang 'Processing' và commit các thay đổi ở cuối.

DECLARE  
    -- Declare a cursor to select pending transactions and lock rows for update
    CURSOR c_pending_txns IS  
        SELECT transaction_id FROM transactions WHERE status = 'Pending' FOR UPDATE SKIP LOCKED;  -- Skip locked rows  

BEGIN  
    -- Loop through the pending transactions
    FOR txn IN c_pending_txns LOOP  
        -- Update the status of each transaction to 'Processing'
        UPDATE transactions SET status = 'Processing' WHERE transaction_id = txn.transaction_id;  
    END LOOP;  
    COMMIT;  -- Commit the changes to finalize the transaction updates  
END;

Kết luận và Tài nguyên bổ sung

Với các vị trí tập trung vào Oracle, nhà tuyển dụng tìm kiếm ứng viên có thể viết mã PL/SQL đúng, xử lý các thách thức thực tế và tối ưu hiệu năng cơ sở dữ liệu. Tôi khuyến nghị xem tài liệu chính thức của Oracle về PL/SQL Language Reference để có hướng dẫn chuyên sâu và thực hành tốt nhất về PL/SQL. Oracle Live SQL (Interactive PL/SQL Playground) cung cấp quyền truy cập vào nền tảng đám mây của Oracle để chạy truy vấn PL/SQL trực tuyến. Tôi cũng khuyến khích bạn tham gia Oracle Community để trao đổi với chuyên gia và nhà phát triển Oracle, cùng diễn đàn Stack Overflow để tìm giải pháp cho các câu hỏi liên quan đến PL/SQL.

Tuy nhiên, không gì bằng một lộ trình học tập có cấu trúc. Vì lý do này, tôi rất, rất khuyến nghị tham gia khóa học Introduction to Oracle SQL của chúng tôi, theo quan điểm của tôi, là cách tốt nhất để thực sự trở thành chuyên gia. Ngoài ra, nếu bạn muốn nâng cao kiến thức làm việc với cơ sở dữ liệu, tôi cũng khuyến nghị khóa học Database Design, nơi bạn sẽ học cách tạo và quản lý cơ sở dữ liệu và chọn DBMS phù hợp với nhu cầu. Cuối cùng, hãy thử lộ trình nghề nghiệp Associate Data Engineer in SQL để học các nền tảng về kỹ thuật dữ liệu và kho dữ liệu. 

Cuối cùng nhưng không kém phần quan trọng, nếu bạn đang phỏng vấn ứng viên và cũng muốn nâng cao kỹ năng cho người khác, hãy kết nối với đội ngũ DataCamp for Business của chúng tôi. DataCamp có thể nâng cao kỹ năng cho cả đội ngũ với các dự án thực hành và lộ trình học tập tùy chỉnh. Đây là cách tuyệt vời để nhà tuyển dụng thu hẹp khoảng cách kỹ năng và nâng cao sự tự tin cũng như uy tín của đội ngũ, vậy nên hãy kết nối với đội ngũ của chúng tôi ngay hôm nay.


Allan Ouko's photo
Author
Allan Ouko
LinkedIn
Biên tập viên kỹ thuật về Khoa học dữ liệu với kinh nghiệm thực tế trong phân tích dữ liệu, trí tuệ doanh nghiệp và khoa học dữ liệu. Tôi viết nội dung thực tiễn, tập trung vào ngành về SQL, Python, Power BI, Databricks và kỹ thuật dữ liệu, dựa trên công việc phân tích trong thế giới thực. Bài viết của tôi kết nối chiều sâu kỹ thuật với tác động kinh doanh, giúp các chuyên gia chuyển đổi dữ liệu thành những quyết định vững chắc.

Câu hỏi thường gặp về PL SQL

PL/SQL là gì và khác SQL như thế nào?

PL/SQL là ngôn ngữ thủ tục do Oracle phát triển, cho phép cấu trúc điều khiển và xử lý lỗi, trong khi SQL mang tính khai báo và xử lý truy xuất/ thao tác dữ liệu.

PL/SQL package là gì?

Tập hợp các thủ tục, hàm, biến và con trỏ có liên quan nhằm hỗ trợ lập trình mô-đun và tái sử dụng.

Thủ tục khác hàm như thế nào?

Thủ tục thực hiện hành động không trả về giá trị, còn hàm trả về một giá trị có thể dùng trong truy vấn.

Trigger trong PL/SQL là gì?

Hành động tự động được thực thi trước hoặc sau các thao tác INSERT, UPDATE, DELETE.

Xử lý hàng loạt trong PL/SQL là gì?

Xử lý hàng loạt (BULK COLLECT, FORALL) nâng cao hiệu năng bằng cách giảm chuyển ngữ cảnh giữa các bộ máy PL/SQL và SQL khi xử lý tập dữ liệu lớn.

Chủ đề

Học SQL với DataCamp

Courses

Xử lý dữ liệu trong SQL

4 giờ
317K
Xem chi tiếtRight Arrow
Bắt đầu khóa học
Xem thêmRight Arrow