ORA-00928: Missing SELECT Keyword — Causes, Fixes & Prevention
ORA-00928 is a SQL syntax error thrown by the Oracle parser when it encounters a position where a SELECT keyword is expected but finds something else entirely. This typically happens in INSERT ... SELECT statements, CREATE VIEW definitions, subqueries, or UNION blocks where the SELECT keyword has been accidentally omitted. It is one of the most common parse-time errors in Oracle and is almost always caused by a simple typo or copy-paste mistake.
Top 3 Causes
1. Missing SELECT in INSERT ... SELECT Statements
The most frequent trigger for ORA-00928 is forgetting to include SELECT in an INSERT ... SELECT statement.
-- BAD: Causes ORA-00928
INSERT INTO emp_backup (emp_id, emp_name, salary)
emp_id, emp_name, salary
FROM employees
WHERE department_id = 10;
-- GOOD: Correct syntax
INSERT INTO emp_backup (emp_id, emp_name, salary)
SELECT emp_id, emp_name, salary
FROM employees
WHERE department_id = 10;
2. Missing SELECT in CREATE VIEW or Subqueries
When creating a view or writing an inline view, the SELECT keyword must immediately follow the AS keyword or the opening parenthesis of a subquery.
-- BAD: Causes ORA-00928
CREATE OR REPLACE VIEW v_active_emp AS
emp_id, emp_name
FROM employees
WHERE status = 'ACTIVE';
-- GOOD: Correct syntax
CREATE OR REPLACE VIEW v_active_emp AS
SELECT emp_id, emp_name
FROM employees
WHERE status = 'ACTIVE';
-- BAD inline view
SELECT a.emp_name
FROM employees a,
(dept_id FROM departments WHERE location_id = 1800) b
WHERE a.department_id = b.dept_id;
-- GOOD inline view
SELECT a.emp_name
FROM employees a,
(SELECT dept_id FROM departments WHERE location_id = 1800) b
WHERE a.department_id = b.dept_id;
3. Missing SELECT in UNION / UNION ALL Blocks
In a UNION or UNION ALL query, every individual query block must begin with SELECT. Dropping it from any block — usually the second or third — triggers ORA-00928 immediately.
-- BAD: Causes ORA-00928
SELECT emp_id, emp_name, 'CURRENT' AS emp_status
FROM employees
UNION ALL
emp_id, emp_name, 'RETIRED' AS emp_status
FROM retired_employees;
-- GOOD: SELECT present in every block
SELECT emp_id, emp_name, 'CURRENT' AS emp_status
FROM employees
UNION ALL
SELECT emp_id, emp_name, 'RETIRED' AS emp_status
FROM retired_employees;
Quick Fix Solutions
-
Always check the first keyword of every query block — standalone queries, subqueries, CTE bodies, and each
UNIONbranch must all start withSELECT. -
Validate CTEs (WITH clauses) — every CTE body inside parentheses needs its own
SELECT:
-- GOOD: CTE with proper SELECT
WITH dept_summary AS (
SELECT dept_id, COUNT(*) AS emp_count
FROM employees
GROUP BY dept_id
)
SELECT d.dept_name, s.emp_count
FROM departments d
JOIN dept_summary s ON d.dept_id = s.dept_id;
- Use Oracle SQL Developer or Toad — both tools highlight missing keywords in real time before you even run the query.
Prevention Tips
Build queries incrementally — write and test the innermost subquery first, confirm it runs cleanly, then wrap it in the outer query. This approach surfaces missing
SELECTkeywords at the earliest possible stage and avoids hunting through hundreds of lines of SQL.Adopt a SQL linter in your CI/CD pipeline — tools like
SQLcl,SQLFluff, or IDE-level syntax checkers can be integrated into code review workflows to catch ORA-00928-class errors before they ever reach a production database.
Related Oracle Errors
| Error Code | Message | When It Occurs |
|---|---|---|
| ORA-00900 | invalid SQL statement | Entire statement is unrecognizable |
| ORA-00923 | FROM keyword not found | FROM missing after SELECT list |
| ORA-00936 | missing expression | Expression missing in SELECT or WHERE |
| ORA-00907 | missing right parenthesis | Unclosed parenthesis in subquery |
📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.
Top comments (0)