In PL/SQL, when you execute a SELECT statement inside a PL/SQL block (such as in a procedure, function, or anonymous block), you must fetch the selected data into variables using the INTO clause.

The error:

PLS-00428: an INTO clause is expected in this SELECT statement

is triggered when PL/SQL expects you to store the result of a SELECT query in a variable, but you forget to include the INTO clause.

1. Why This Error Occurs

In SQL, you can write:

SELECT first_name FROM employees WHERE employee_id = 100;

and it simply displays the result.

But in PL/SQL, you must write:

SELECT first_name INTO v_name FROM employees WHERE employee_id = 100;

because PL/SQL requires a target for the result — a variable to store the value.

2. Example of the Error

Code That Triggers PLS-00428

BEGIN
SELECT first_name FROM employees WHERE employee_id = 100;
END;
/

Error Output

PLS-00428: an INTO clause is expected in this SELECT statement

3. Correct Usage with INTO Clause

Fixed Version

DECLARE
v_name employees.first_name%TYPE;
BEGIN
SELECT first_name INTO v_name FROM employees WHERE employee_id = 100;
DBMS_OUTPUT.PUT_LINE('Employee name: ' || v_name);
END;
/

Output:

Employee name: Steven

The INTO clause tells PL/SQL to store the result of the query into the variable v_name.

4. Handling Multiple Columns

If your SELECT returns multiple columns, you need multiple variables in the INTO clause.

Example: Multiple Columns

DECLARE
v_fname employees.first_name%TYPE;
v_lname employees.last_name%TYPE;
BEGIN
SELECT first_name, last_name
INTO v_fname, v_lname
FROM employees
WHERE employee_id = 100;

DBMS_OUTPUT.PUT_LINE('Full Name: ' || v_fname || ' ' || v_lname);
END;
/

5. Using INTO with ROWTYPE

If you’re selecting all columns, you can use %ROWTYPE for cleaner code:

DECLARE
v_emp employees%ROWTYPE;
BEGIN
SELECT * INTO v_emp FROM employees WHERE employee_id = 100;
DBMS_OUTPUT.PUT_LINE('Employee Email: ' || v_emp.email);
END;
/

6. When You Don’t Need an INTO Clause

The only time you don’t use an INTO clause is when you’re using:

  • A cursor FOR loop
  • Dynamic SQL with EXECUTE IMMEDIATE
  • SELECT INTO as a standalone SQL query (outside PL/SQL)

Cursor FOR Loop Example

BEGIN
FOR rec IN (SELECT employee_id, last_name FROM employees WHERE department_id = 10) LOOP
DBMS_OUTPUT.PUT_LINE(rec.employee_id || ' - ' || rec.last_name);
END LOOP;
END;
/

Notice that there’s no INTO clause here — PL/SQL automatically handles it via the loop variable rec.

7. Common Pitfalls and Fixes

MistakeFix
Writing SELECT ... alone in PL/SQLAdd INTO clause with variable(s)
Forgetting INTO for multi-column SELECTInclude all target variables in same order
Using SELECT in BEGIN...END; block without INTOUse cursor or dynamic SQL if needed
SELECT returns more than one rowUse SELECT INTO only when exactly one row is expected or use CURSOR/LOOP

8. Summary

PLS-00428 is a very common beginner mistake in PL/SQL programming. To resolve it:

  • Always include an INTO clause when using SELECT inside PL/SQL blocks.
  • Ensure the number and type of variables in the INTO clause match the SELECT list.
  • Use %TYPE and %ROWTYPE to ensure compatibility with column types.
  • Use cursor loops or collections for multi-row queries.

Want to Avoid This Error Completely?

  • Use cursor FOR loops for queries that return multiple rows.
  • Always test queries in SQL first, then add variables and INTO clause in PL/SQL.
  • Add exception handling to capture cases where rows are missing or duplicated.