Skip to content

ORA-00979 - Not a GROUP BY Expression

The ORA-00979 error occurs when a SELECT statement contains columns in the SELECT list that are not included in the GROUP BY clause, while using aggregate functions. Oracle requires all non-aggregate columns in the SELECT list to be specified in the GROUP BY clause.

  • Error Code: ORA-00979
  • Error Message: “not a GROUP BY expression”
  • Error Type: SQL Syntax Error
  • Severity: Compilation Error

Using aggregate functions without proper GROUP BY clause.

Not including all non-aggregate columns in the GROUP BY clause.

Using column aliases incorrectly in GROUP BY clause.

Differences between SELECT list expressions and GROUP BY expressions.

Incorrect use of aggregate functions in subqueries.

Problem:

-- Trying to mix aggregate and non-aggregate columns
SELECT department_id, COUNT(*)
FROM employees;
-- ORA-00979: not a GROUP BY expression

Solution:

-- Add GROUP BY clause for non-aggregate columns
SELECT department_id, COUNT(*)
FROM employees
GROUP BY department_id;
-- Or remove non-aggregate column if not needed
SELECT COUNT(*)
FROM employees;

Problem:

-- Multiple non-aggregate columns, incomplete GROUP BY
SELECT department_id, job_id, AVG(salary)
FROM employees
GROUP BY department_id; -- Missing job_id
-- ORA-00979: not a GROUP BY expression

Solution:

-- Include all non-aggregate columns in GROUP BY
SELECT department_id, job_id, AVG(salary)
FROM employees
GROUP BY department_id, job_id;
-- Alternative: Use only one grouping column
SELECT department_id, AVG(salary)
FROM employees
GROUP BY department_id;

Problem:

-- Using alias in GROUP BY (Oracle doesn't allow this in all cases)
SELECT department_id AS dept, COUNT(*) AS emp_count
FROM employees
GROUP BY dept; -- May cause ORA-00979 in some Oracle versions
-- ORA-00979: not a GROUP BY expression

Solution:

-- Use actual column name in GROUP BY
SELECT department_id AS dept, COUNT(*) AS emp_count
FROM employees
GROUP BY department_id;
-- Or use column position number
SELECT department_id AS dept, COUNT(*) AS emp_count
FROM employees
GROUP BY 1;

Problem:

-- Expression in SELECT doesn't match GROUP BY
SELECT UPPER(last_name), COUNT(*)
FROM employees
GROUP BY last_name; -- Missing UPPER function
-- ORA-00979: not a GROUP BY expression

Solution:

-- Match expressions exactly
SELECT UPPER(last_name), COUNT(*)
FROM employees
GROUP BY UPPER(last_name);
-- Or simplify to use base column
SELECT last_name, COUNT(*)
FROM employees
GROUP BY last_name;

Problem:

-- Complex expression mismatch
SELECT
CASE
WHEN salary > 5000 THEN 'High'
ELSE 'Low'
END AS salary_category,
COUNT(*)
FROM employees
GROUP BY salary; -- Wrong grouping expression
-- ORA-00979: not a GROUP BY expression

Solution:

-- Group by the exact expression
SELECT
CASE
WHEN salary > 5000 THEN 'High'
ELSE 'Low'
END AS salary_category,
COUNT(*)
FROM employees
GROUP BY
CASE
WHEN salary > 5000 THEN 'High'
ELSE 'Low'
END;
-- Alternative: Use subquery
SELECT salary_category, COUNT(*)
FROM (
SELECT
CASE
WHEN salary > 5000 THEN 'High'
ELSE 'Low'
END AS salary_category
FROM employees
)
GROUP BY salary_category;

Problem:

-- Date function in SELECT but not in GROUP BY
SELECT TO_CHAR(hire_date, 'YYYY'), COUNT(*)
FROM employees
GROUP BY hire_date; -- Should group by the expression
-- ORA-00979: not a GROUP BY expression

Solution:

-- Group by the exact date expression
SELECT TO_CHAR(hire_date, 'YYYY') AS hire_year, COUNT(*)
FROM employees
GROUP BY TO_CHAR(hire_date, 'YYYY');
-- Alternative: Use EXTRACT function
SELECT EXTRACT(YEAR FROM hire_date) AS hire_year, COUNT(*)
FROM employees
GROUP BY EXTRACT(YEAR FROM hire_date);

Problem:

-- Join with aggregation
SELECT d.department_name, e.job_id, AVG(e.salary)
FROM employees e
JOIN departments d ON e.department_id = d.department_id
GROUP BY d.department_name; -- Missing e.job_id
-- ORA-00979: not a GROUP BY expression

Solution:

-- Include all non-aggregate columns
SELECT d.department_name, e.job_id, AVG(e.salary)
FROM employees e
JOIN departments d ON e.department_id = d.department_id
GROUP BY d.department_name, e.job_id;

Problem:

-- Wanting to show detail with aggregates
SELECT employee_id, last_name, department_id, COUNT(*)
FROM employees
GROUP BY department_id; -- Can't include employee details
-- ORA-00979: not a GROUP BY expression

Solution:

-- Use window functions instead
SELECT
employee_id,
last_name,
department_id,
COUNT(*) OVER (PARTITION BY department_id) AS dept_count
FROM employees;
-- Or use correlated subquery
SELECT
employee_id,
last_name,
department_id,
(SELECT COUNT(*)
FROM employees e2
WHERE e2.department_id = e1.department_id) AS dept_count
FROM employees e1;

Problem:

-- Incorrect subquery with aggregation
SELECT department_id,
(SELECT last_name, COUNT(*)
FROM employees e2
WHERE e2.department_id = e1.department_id)
FROM employees e1;
-- ORA-00979: not a GROUP BY expression (in subquery)

Solution:

-- Fix subquery aggregation
SELECT department_id,
(SELECT COUNT(*)
FROM employees e2
WHERE e2.department_id = e1.department_id) AS emp_count
FROM employees e1
GROUP BY department_id;
-- Or use proper join
SELECT d.department_id, COUNT(e.employee_id) AS emp_count
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id;
-- Check which columns need to be in GROUP BY
-- This query helps identify the issue
SELECT DISTINCT table_name, column_name
FROM user_tab_columns
WHERE table_name IN ('EMPLOYEES', 'DEPARTMENTS')
ORDER BY table_name, column_name;
-- Start simple and build complexity
-- Step 1: Basic aggregation
SELECT COUNT(*) FROM employees;
-- Step 2: Add one grouping column
SELECT department_id, COUNT(*)
FROM employees
GROUP BY department_id;
-- Step 3: Add more columns incrementally
SELECT department_id, job_id, COUNT(*)
FROM employees
GROUP BY department_id, job_id;
-- Find queries with GROUP BY in SQL cache
SELECT sql_text
FROM v$sql
WHERE UPPER(sql_text) LIKE '%GROUP BY%'
AND UPPER(sql_text) LIKE '%SELECT%'
ORDER BY last_active_time DESC;
-- Original problematic query
SELECT col1, col2, SUM(col3)
FROM table1
GROUP BY col1; -- Add col2
-- Fixed query
SELECT col1, col2, SUM(col3)
FROM table1
GROUP BY col1, col2;
-- Remove non-essential columns
SELECT SUM(salary), AVG(salary)
FROM employees
-- No GROUP BY needed if no non-aggregate columns
-- Move aggregation to subquery
SELECT emp_data.*, dept_stats.avg_salary
FROM employees emp_data
JOIN (
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
) dept_stats ON emp_data.department_id = dept_stats.department_id;
-- Replace GROUP BY with window functions
SELECT
employee_id,
last_name,
department_id,
salary,
AVG(salary) OVER (PARTITION BY department_id) AS dept_avg_salary
FROM employees;
-- Rule: Every column in SELECT (except aggregates) must be in GROUP BY
-- Good example
SELECT department_id, job_id, COUNT(*), AVG(salary)
FROM employees
GROUP BY department_id, job_id;
-- Bad example (missing job_id in GROUP BY)
-- SELECT department_id, job_id, COUNT(*), AVG(salary)
-- FROM employees
-- GROUP BY department_id;
-- Use positional notation for complex expressions
SELECT
CASE WHEN salary > 5000 THEN 'High' ELSE 'Low' END,
COUNT(*)
FROM employees
GROUP BY 1; -- Position 1 refers to the CASE expression
-- Keep expressions consistent between SELECT and GROUP BY
SELECT TRUNC(hire_date, 'MM') AS hire_month, COUNT(*)
FROM employees
GROUP BY TRUNC(hire_date, 'MM'); -- Exact same expression
-- Document complex grouping logic
SELECT
-- Grouping by department and job
department_id,
job_id,
-- Aggregated metrics
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary,
MAX(salary) AS max_salary
FROM employees
GROUP BY
department_id, -- All employees in same department
job_id; -- Further grouped by job role
-- Monthly sales report
SELECT
TO_CHAR(order_date, 'YYYY-MM') AS order_month,
COUNT(*) AS order_count,
SUM(total_amount) AS total_sales
FROM orders
GROUP BY TO_CHAR(order_date, 'YYYY-MM')
ORDER BY order_month;
-- Department and job level summary
SELECT
d.department_name,
e.job_id,
COUNT(*) AS employee_count,
AVG(e.salary) AS avg_salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id
GROUP BY d.department_name, e.job_id
ORDER BY d.department_name, e.job_id;
-- Summary with conditional counts
SELECT
department_id,
COUNT(*) AS total_employees,
COUNT(CASE WHEN salary > 5000 THEN 1 END) AS high_earners,
COUNT(CASE WHEN salary <= 5000 THEN 1 END) AS regular_earners
FROM employees
GROUP BY department_id;

ORA-00979 occurs when non-aggregate columns in the SELECT list are not included in the GROUP BY clause. Resolution requires either adding missing columns to GROUP BY, removing columns from SELECT, or restructuring the query using subqueries or window functions. Always ensure that every non-aggregate column in the SELECT list appears in the GROUP BY clause with the exact same expression.