ORA-00979 - Not a GROUP BY Expression
ORA-00979: Not a GROUP BY Expression
Section titled “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 Details
Section titled “Error Details”- Error Code: ORA-00979
- Error Message: “not a GROUP BY expression”
- Error Type: SQL Syntax Error
- Severity: Compilation Error
Common Causes
Section titled “Common Causes”1. Missing GROUP BY Clause
Section titled “1. Missing GROUP BY Clause”Using aggregate functions without proper GROUP BY clause.
2. Incomplete GROUP BY List
Section titled “2. Incomplete GROUP BY List”Not including all non-aggregate columns in the GROUP BY clause.
3. Column Alias Issues
Section titled “3. Column Alias Issues”Using column aliases incorrectly in GROUP BY clause.
4. Expression Mismatches
Section titled “4. Expression Mismatches”Differences between SELECT list expressions and GROUP BY expressions.
5. Subquery Problems
Section titled “5. Subquery Problems”Incorrect use of aggregate functions in subqueries.
Examples and Solutions
Section titled “Examples and Solutions”Example 1: Missing GROUP BY Clause
Section titled “Example 1: Missing GROUP BY Clause”Problem:
-- Trying to mix aggregate and non-aggregate columnsSELECT department_id, COUNT(*)FROM employees;-- ORA-00979: not a GROUP BY expression
Solution:
-- Add GROUP BY clause for non-aggregate columnsSELECT department_id, COUNT(*)FROM employeesGROUP BY department_id;
-- Or remove non-aggregate column if not neededSELECT COUNT(*)FROM employees;
Example 2: Incomplete GROUP BY List
Section titled “Example 2: Incomplete GROUP BY List”Problem:
-- Multiple non-aggregate columns, incomplete GROUP BYSELECT department_id, job_id, AVG(salary)FROM employeesGROUP BY department_id; -- Missing job_id-- ORA-00979: not a GROUP BY expression
Solution:
-- Include all non-aggregate columns in GROUP BYSELECT department_id, job_id, AVG(salary)FROM employeesGROUP BY department_id, job_id;
-- Alternative: Use only one grouping columnSELECT department_id, AVG(salary)FROM employeesGROUP BY department_id;
Example 3: Column Alias in GROUP BY
Section titled “Example 3: Column Alias in GROUP BY”Problem:
-- Using alias in GROUP BY (Oracle doesn't allow this in all cases)SELECT department_id AS dept, COUNT(*) AS emp_countFROM employeesGROUP BY dept; -- May cause ORA-00979 in some Oracle versions-- ORA-00979: not a GROUP BY expression
Solution:
-- Use actual column name in GROUP BYSELECT department_id AS dept, COUNT(*) AS emp_countFROM employeesGROUP BY department_id;
-- Or use column position numberSELECT department_id AS dept, COUNT(*) AS emp_countFROM employeesGROUP BY 1;
Example 4: Expression Mismatch
Section titled “Example 4: Expression Mismatch”Problem:
-- Expression in SELECT doesn't match GROUP BYSELECT UPPER(last_name), COUNT(*)FROM employeesGROUP BY last_name; -- Missing UPPER function-- ORA-00979: not a GROUP BY expression
Solution:
-- Match expressions exactlySELECT UPPER(last_name), COUNT(*)FROM employeesGROUP BY UPPER(last_name);
-- Or simplify to use base columnSELECT last_name, COUNT(*)FROM employeesGROUP BY last_name;
Example 5: Complex Expressions
Section titled “Example 5: Complex Expressions”Problem:
-- Complex expression mismatchSELECT CASE WHEN salary > 5000 THEN 'High' ELSE 'Low' END AS salary_category, COUNT(*)FROM employeesGROUP BY salary; -- Wrong grouping expression-- ORA-00979: not a GROUP BY expression
Solution:
-- Group by the exact expressionSELECT CASE WHEN salary > 5000 THEN 'High' ELSE 'Low' END AS salary_category, COUNT(*)FROM employeesGROUP BY CASE WHEN salary > 5000 THEN 'High' ELSE 'Low' END;
-- Alternative: Use subquerySELECT salary_category, COUNT(*)FROM ( SELECT CASE WHEN salary > 5000 THEN 'High' ELSE 'Low' END AS salary_category FROM employees)GROUP BY salary_category;
Example 6: Date Functions
Section titled “Example 6: Date Functions”Problem:
-- Date function in SELECT but not in GROUP BYSELECT TO_CHAR(hire_date, 'YYYY'), COUNT(*)FROM employeesGROUP BY hire_date; -- Should group by the expression-- ORA-00979: not a GROUP BY expression
Solution:
-- Group by the exact date expressionSELECT TO_CHAR(hire_date, 'YYYY') AS hire_year, COUNT(*)FROM employeesGROUP BY TO_CHAR(hire_date, 'YYYY');
-- Alternative: Use EXTRACT functionSELECT EXTRACT(YEAR FROM hire_date) AS hire_year, COUNT(*)FROM employeesGROUP BY EXTRACT(YEAR FROM hire_date);
Advanced Examples
Section titled “Advanced Examples”Multiple Table Joins
Section titled “Multiple Table Joins”Problem:
-- Join with aggregationSELECT d.department_name, e.job_id, AVG(e.salary)FROM employees eJOIN departments d ON e.department_id = d.department_idGROUP BY d.department_name; -- Missing e.job_id-- ORA-00979: not a GROUP BY expression
Solution:
-- Include all non-aggregate columnsSELECT d.department_name, e.job_id, AVG(e.salary)FROM employees eJOIN departments d ON e.department_id = d.department_idGROUP BY d.department_name, e.job_id;
Window Functions (Alternative Approach)
Section titled “Window Functions (Alternative Approach)”Problem:
-- Wanting to show detail with aggregatesSELECT employee_id, last_name, department_id, COUNT(*)FROM employeesGROUP BY department_id; -- Can't include employee details-- ORA-00979: not a GROUP BY expression
Solution:
-- Use window functions insteadSELECT employee_id, last_name, department_id, COUNT(*) OVER (PARTITION BY department_id) AS dept_countFROM employees;
-- Or use correlated subquerySELECT employee_id, last_name, department_id, (SELECT COUNT(*) FROM employees e2 WHERE e2.department_id = e1.department_id) AS dept_countFROM employees e1;
Subquery Aggregation
Section titled “Subquery Aggregation”Problem:
-- Incorrect subquery with aggregationSELECT 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 aggregationSELECT department_id, (SELECT COUNT(*) FROM employees e2 WHERE e2.department_id = e1.department_id) AS emp_countFROM employees e1GROUP BY department_id;
-- Or use proper joinSELECT d.department_id, COUNT(e.employee_id) AS emp_countFROM departments dLEFT JOIN employees e ON d.department_id = e.department_idGROUP BY d.department_id;
Diagnostic Queries
Section titled “Diagnostic Queries”Validate GROUP BY Requirements
Section titled “Validate GROUP BY Requirements”-- Check which columns need to be in GROUP BY-- This query helps identify the issueSELECT DISTINCT table_name, column_nameFROM user_tab_columnsWHERE table_name IN ('EMPLOYEES', 'DEPARTMENTS')ORDER BY table_name, column_name;
Test Query Structure
Section titled “Test Query Structure”-- Start simple and build complexity-- Step 1: Basic aggregationSELECT COUNT(*) FROM employees;
-- Step 2: Add one grouping columnSELECT department_id, COUNT(*)FROM employeesGROUP BY department_id;
-- Step 3: Add more columns incrementallySELECT department_id, job_id, COUNT(*)FROM employeesGROUP BY department_id, job_id;
Analyze Existing Queries
Section titled “Analyze Existing Queries”-- Find queries with GROUP BY in SQL cacheSELECT sql_textFROM v$sqlWHERE UPPER(sql_text) LIKE '%GROUP BY%'AND UPPER(sql_text) LIKE '%SELECT%'ORDER BY last_active_time DESC;
Resolution Strategies
Section titled “Resolution Strategies”1. Add Missing Columns to GROUP BY
Section titled “1. Add Missing Columns to GROUP BY”-- Original problematic querySELECT col1, col2, SUM(col3)FROM table1GROUP BY col1; -- Add col2
-- Fixed querySELECT col1, col2, SUM(col3)FROM table1GROUP BY col1, col2;
2. Remove Columns from SELECT
Section titled “2. Remove Columns from SELECT”-- Remove non-essential columnsSELECT SUM(salary), AVG(salary)FROM employees-- No GROUP BY needed if no non-aggregate columns
3. Use Subqueries
Section titled “3. Use Subqueries”-- Move aggregation to subquerySELECT emp_data.*, dept_stats.avg_salaryFROM employees emp_dataJOIN ( 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;
4. Use Window Functions
Section titled “4. Use Window Functions”-- Replace GROUP BY with window functionsSELECT employee_id, last_name, department_id, salary, AVG(salary) OVER (PARTITION BY department_id) AS dept_avg_salaryFROM employees;
Best Practices
Section titled “Best Practices”1. Query Design Rules
Section titled “1. Query Design Rules”-- Rule: Every column in SELECT (except aggregates) must be in GROUP BY-- Good exampleSELECT department_id, job_id, COUNT(*), AVG(salary)FROM employeesGROUP 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;
2. Use Column Positions
Section titled “2. Use Column Positions”-- Use positional notation for complex expressionsSELECT CASE WHEN salary > 5000 THEN 'High' ELSE 'Low' END, COUNT(*)FROM employeesGROUP BY 1; -- Position 1 refers to the CASE expression
3. Consistent Expression Usage
Section titled “3. Consistent Expression Usage”-- Keep expressions consistent between SELECT and GROUP BYSELECT TRUNC(hire_date, 'MM') AS hire_month, COUNT(*)FROM employeesGROUP BY TRUNC(hire_date, 'MM'); -- Exact same expression
4. Documentation and Comments
Section titled “4. Documentation and Comments”-- Document complex grouping logicSELECT -- Grouping by department and job department_id, job_id, -- Aggregated metrics COUNT(*) AS employee_count, AVG(salary) AS avg_salary, MAX(salary) AS max_salaryFROM employeesGROUP BY department_id, -- All employees in same department job_id; -- Further grouped by job role
Common Patterns and Solutions
Section titled “Common Patterns and Solutions”Pattern 1: Reporting Queries
Section titled “Pattern 1: Reporting Queries”-- Monthly sales reportSELECT TO_CHAR(order_date, 'YYYY-MM') AS order_month, COUNT(*) AS order_count, SUM(total_amount) AS total_salesFROM ordersGROUP BY TO_CHAR(order_date, 'YYYY-MM')ORDER BY order_month;
Pattern 2: Hierarchical Grouping
Section titled “Pattern 2: Hierarchical Grouping”-- Department and job level summarySELECT d.department_name, e.job_id, COUNT(*) AS employee_count, AVG(e.salary) AS avg_salaryFROM employees eJOIN departments d ON e.department_id = d.department_idGROUP BY d.department_name, e.job_idORDER BY d.department_name, e.job_id;
Pattern 3: Conditional Aggregation
Section titled “Pattern 3: Conditional Aggregation”-- Summary with conditional countsSELECT 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_earnersFROM employeesGROUP BY department_id;
Related Errors
Section titled “Related Errors”- ORA-00937: Not a Single-Group Group Function
- ORA-00934: Group Function Not Allowed Here
- ORA-00936: Missing Expression
- ORA-00904: Invalid Identifier
Summary
Section titled “Summary”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.