Skip to content

ORA-00918 Column Ambiguously Defined - Complete Resolution Guide

Error Text: ORA-00918: column ambiguously defined

The ORA-00918 error occurs when a SQL statement references a column name that exists in multiple tables within the query, and Oracle cannot determine which table’s column you intended to use. This is one of the most common errors when writing JOIN queries.

  • Column exists in multiple joined tables
  • SELECT * combined with same-named columns
  • Subqueries with duplicate column names
  • Using NATURAL JOIN with additional column references
  • USING clause combined with table-qualified columns
  • Self-joins without proper aliases
  • Views containing duplicate column names
  • Inline views with ambiguous columns
  • UNION queries with column references
-- ERROR: Both tables have 'department_id'
SELECT employee_id, department_id, department_name
FROM employees
JOIN departments ON employees.department_id = departments.department_id;
-- ORA-00918: column ambiguously defined
-- SOLUTION 1: Qualify the ambiguous column
SELECT employee_id, employees.department_id, department_name
FROM employees
JOIN departments ON employees.department_id = departments.department_id;
-- SOLUTION 2: Use table aliases (recommended)
SELECT e.employee_id, e.department_id, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
-- ERROR: SELECT * includes duplicate column names
SELECT *
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE department_id = 10; -- Which one?
-- ORA-00918: column ambiguously defined
-- SOLUTION: Qualify all column references
SELECT *
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE e.department_id = 10;
-- BETTER: Explicitly list columns
SELECT e.employee_id, e.first_name, e.last_name,
e.department_id, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE e.department_id = 10;
-- ERROR: Self-join without aliases
SELECT employee_id, first_name, manager_id
FROM employees
JOIN employees ON employees.manager_id = employees.employee_id;
-- ORA-00918: column ambiguously defined
-- SOLUTION: Use distinct aliases
SELECT e.employee_id, e.first_name, m.first_name as manager_name
FROM employees e
JOIN employees m ON e.manager_id = m.employee_id;
-- NATURAL JOIN automatically matches common columns
-- But referencing matched columns can cause issues
-- ERROR: Can't qualify columns used in NATURAL JOIN
SELECT employees.department_id, department_name
FROM employees
NATURAL JOIN departments;
-- ORA-00918 or ORA-25155
-- SOLUTION 1: Don't qualify naturally-joined columns
SELECT department_id, department_name
FROM employees
NATURAL JOIN departments;
-- SOLUTION 2: Use explicit JOIN instead (recommended)
SELECT e.department_id, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
-- ERROR: Can't qualify columns in USING clause
SELECT e.department_id, department_name
FROM employees e
JOIN departments d USING (department_id);
-- ORA-25154: column part of USING clause cannot have qualifier
-- SOLUTION: Reference USING columns without qualifier
SELECT department_id, d.department_name, e.employee_id
FROM employees e
JOIN departments d USING (department_id);
-- ERROR: Outer query can't resolve column
SELECT department_id, total_salary
FROM (
SELECT e.department_id, d.department_id as dept_id, SUM(salary) as total_salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id
GROUP BY e.department_id, d.department_id
);
-- ORA-00918: column ambiguously defined (two department_id columns)
-- SOLUTION: Use distinct column names or aliases
SELECT department_id, total_salary
FROM (
SELECT e.department_id, SUM(salary) as total_salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id
GROUP BY e.department_id
);
-- ERROR: ORDER BY column is ambiguous
SELECT e.employee_id, e.hire_date, j.start_date, j.end_date
FROM employees e
JOIN job_history j ON e.employee_id = j.employee_id
ORDER BY employee_id; -- Exists in both tables
-- ORA-00918: column ambiguously defined
-- SOLUTION: Qualify the ORDER BY column
SELECT e.employee_id, e.hire_date, j.start_date, j.end_date
FROM employees e
JOIN job_history j ON e.employee_id = j.employee_id
ORDER BY e.employee_id;
-- Or use column position (less readable)
ORDER BY 1;
-- ERROR: GROUP BY references ambiguous column
SELECT department_id, COUNT(*)
FROM employees e
JOIN departments d ON e.department_id = d.department_id
GROUP BY department_id;
-- ORA-00918: column ambiguously defined
-- SOLUTION: Qualify GROUP BY column
SELECT e.department_id, COUNT(*)
FROM employees e
JOIN departments d ON e.department_id = d.department_id
GROUP BY e.department_id;
-- Find columns with same name across tables in your query
SELECT table_name, column_name
FROM all_tab_columns
WHERE owner = 'HR'
AND table_name IN ('EMPLOYEES', 'DEPARTMENTS', 'JOB_HISTORY')
AND column_name IN (
SELECT column_name
FROM all_tab_columns
WHERE owner = 'HR'
AND table_name IN ('EMPLOYEES', 'DEPARTMENTS', 'JOB_HISTORY')
GROUP BY column_name
HAVING COUNT(DISTINCT table_name) > 1
)
ORDER BY column_name, table_name;
-- Common columns that cause ORA-00918:
-- - ID columns (employee_id, department_id, etc.)
-- - Status columns (status, active, enabled)
-- - Audit columns (created_date, modified_date, created_by)
-- - Name columns (name, description)
-- GOOD: Clear, consistent aliases
SELECT
e.employee_id,
e.first_name,
e.last_name,
d.department_name,
l.city
FROM employees e
JOIN departments d ON e.department_id = d.department_id
JOIN locations l ON d.location_id = l.location_id;
-- BAD: No aliases, prone to errors
SELECT
employee_id,
first_name,
last_name,
department_name,
city
FROM employees
JOIN departments ON employees.department_id = departments.department_id
JOIN locations ON departments.location_id = locations.location_id;
-- Even if not ambiguous now, qualify for maintainability
SELECT
e.employee_id, -- Not ambiguous, but qualified
e.salary, -- Not ambiguous, but qualified
d.department_id, -- Ambiguous - must qualify
d.department_name -- Not ambiguous, but qualified
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
-- BAD: SELECT * hides column conflicts
SELECT * FROM table1 JOIN table2 ON ...
-- GOOD: Explicit column list
SELECT
t1.col1,
t1.col2,
t2.col3,
t2.col4
FROM table1 t1
JOIN table2 t2 ON t1.id = t2.id;
-- Consistent alias patterns
-- Option 1: First letter(s) of table name
SELECT e.*, d.*, l.*
FROM employees e
JOIN departments d ON e.department_id = d.department_id
JOIN locations l ON d.location_id = l.location_id;
-- Option 2: Abbreviated meaningful names
SELECT emp.*, dept.*, loc.*
FROM employees emp
JOIN departments dept ON emp.department_id = dept.department_id
JOIN locations loc ON dept.location_id = loc.location_id;
-- For self-joins, use role-based aliases
SELECT
worker.first_name as employee_name,
manager.first_name as manager_name
FROM employees worker
LEFT JOIN employees manager ON worker.manager_id = manager.employee_id;
-- Complex report with many joins
SELECT
e.employee_id,
e.first_name || ' ' || e.last_name as employee_name,
j.job_title,
d.department_name,
l.city,
c.country_name,
m.first_name || ' ' || m.last_name as manager_name
FROM employees e
JOIN jobs j ON e.job_id = j.job_id
JOIN departments d ON e.department_id = d.department_id
JOIN locations l ON d.location_id = l.location_id
JOIN countries c ON l.country_id = c.country_id
LEFT JOIN employees m ON e.manager_id = m.employee_id
ORDER BY d.department_name, e.last_name;
-- Avoid ambiguity in subqueries
SELECT
dept_summary.department_name,
dept_summary.emp_count,
dept_summary.total_salary
FROM (
SELECT
d.department_id as dept_id, -- Renamed to avoid conflict
d.department_name,
COUNT(e.employee_id) as emp_count,
SUM(e.salary) as total_salary
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name
) dept_summary
WHERE dept_summary.emp_count > 5;
-- Both queries should use same alias convention
SELECT e.employee_id, e.department_id, 'Current' as status
FROM employees e
WHERE e.department_id = 10
UNION ALL
SELECT jh.employee_id, jh.department_id, 'Historical' as status
FROM job_history jh
WHERE jh.department_id = 10;
ScenarioProblemSolution
Two tables, same columndepartment_id in bothUse e.department_id
Self-joinSame table twiceUse different aliases: e, m
SELECT * with JOINMultiple same-named columnsList columns explicitly
ORDER BYUnqualified columnQualify: ORDER BY e.id
GROUP BYUnqualified columnQualify: GROUP BY e.id
NATURAL JOINQualifying joined columnDon’t qualify or use explicit JOIN
USING clauseQualifying USING columnReference without qualifier
  1. Always use table aliases in queries with JOINs
  2. Qualify every column reference in multi-table queries
  3. **Avoid SELECT *** in production code
  4. Use consistent naming conventions for aliases
  5. Be careful with NATURAL JOIN and USING - they have special rules
  6. Qualify columns in ORDER BY and GROUP BY clauses too