<DataInsights />
  • 🏠 Home
  • 📊 SQL
  • 🐍 Python
  • 📈 Power BI
  • 📗 Excel
  • 💼 Career
  • 🎯 Interview Q&A
  • 📁 Case Study
  • 📥 Downloads
  • 🚀 My Portfolio
<DataInsights />

Practical Data Analytics tutorials covering SQL, Python, Power BI, Excel and career guidance for aspiring analysts — 100% free.

Topics

  • SQL Tutorials
  • Python Guide
  • Power BI
  • Excel Tips
  • Career Guide

Quick Links

  • 🛠️ All Tools
  • 🗓️ Archive
  • 📬 Contact
  • 🔍 Search
  • Portfolio
  • Kaggle
  • GitHub

Legal & Info

  • About
  • Contact
  • Privacy Policy
  • Disclaimer
  • Terms & Conditions
  • DMCA
  • Sitemap
Copyright © 2026 Data Insights by Jatin Kumar. All Rights Reserved.Built with ❤️ for Data Analysts
Home/SQL/Aggregate Functions, GROUP BY And HAVING — Data An...

Aggregate Functions, GROUP BY And HAVING — Data Analytics with SQL

A
August 3, 2026 Jatin Kumar 28 min read SQL
Data Insights MySQL Masterclass — Part 3

Aggregate Functions, GROUP BY & HAVING — Data Analytics with SQL

Data se insights nikalne ka asli kaam yahin se shuru hota hai. COUNT, SUM, AVG, MIN, MAX seekho — phir GROUP BY se data ko groups mein todo aur HAVING se filtered analytics karo. Sab kuch real examples ke saath Data Insights par.

📑 Is Part 3 Mein Aap Kya Sikhenge:

SQL ka analytics engine — aggregate functions aur grouping ka complete guide:

  • Topic 1: COUNT() — Rows Count Karna
  • Topic 2: SUM() — Total Value Calculate Karna
  • Topic 3: AVG() — Average Nikalna
  • Topic 4: MIN() & MAX() — Smallest & Largest Values
  • Topic 5: GROUP BY — Data Ko Groups Mein Todna
  • Topic 6: HAVING — Groups Ko Filter Karna
  • Topic 7: WHERE vs HAVING — Complete Difference
  • Topic 8: Aggregate + GROUP BY + HAVING Combined Queries

📋 Note: Is Part 3 mein bhi wahi employees table use karenge jo Part 2 mein banai thi (15 rows — Rahul, Priya, Amit... wali). Agar table nahi bani toh Part 2 se data insert kar lo pehle.

1. COUNT() — Rows Count Karna

text

🔍 Definition: COUNT() is an aggregate function that returns the number of rows matching a specified condition. COUNT(*) counts all rows including NULLs. COUNT(column) counts only non-NULL values in that column. COUNT(DISTINCT column) counts only unique non-NULL values.

🎯 Samjho Simple Bhasha Mein: COUNT matlab "ginti karo." Jaise class mein teacher poochhe "kitne students present hain?" — toh hum ginti karenge saare desks ki (COUNT(*)) ya sirf jinke attendance column mein entry hai (COUNT(column)). Agar poochhe "kitne different cities se students aaye hain?" toh COUNT(DISTINCT city) lagayenge.

💡 3 Types of COUNT:
COUNT(*) → Total rows (NULL included) — fastest
COUNT(column_name) → Non-NULL rows in that column
COUNT(DISTINCT column) → Unique non-NULL values only

💻 Real-World Code Examples:

Example 1: Total kitne employees hain.

-- Total rows (including NULL salary rows)
SELECT COUNT(*) AS total_employees

FROM employees;

Example 2: Kitne employees ki salary filled hai (NULL nahi hai).

-- Non-NULL salary count
SELECT COUNT(salary) AS salary_filled

FROM employees;

Example 3: Kitne unique departments hain.

-- Unique departments count
SELECT COUNT(DISTINCT dept) AS total_departments

FROM employees;

Example 4: IT department mein kitne employees hain.

-- COUNT with WHERE filter
SELECT COUNT(*) AS it_count

FROM employees

WHERE dept = 'IT';

📊 Expected Output:

-- Example 1:
-- Example 2:
-- Example 3:
-- Example 4:

⚠️ Common Mistakes:

  • Mistake: COUNT(*) aur COUNT(column) ko same samajhna → COUNT(*) saari rows count karta hai, COUNT(column) NULL skip karta hai.
    Fix: Pehle samjho — kya count karna hai? Saari rows ya sirf filled values?
  • Mistake: COUNT ke saath non-aggregate columns SELECT karna bina GROUP BY ke → Error ya wrong result.
    Fix: SELECT dept, COUNT(*) likhoge toh GROUP BY dept zaroori hai.
  • Mistake: COUNT(DISTINCT) mein NULL ko count samajhna → DISTINCT bhi NULL ignore karta hai.

💬 Interview Questions:

Q1: What is the difference between COUNT(*), COUNT(column) and COUNT(DISTINCT column)?
Ans: COUNT(*) counts all rows regardless of NULL values — it is the total row count. COUNT(column) counts only rows where that specific column has a non-NULL value. COUNT(DISTINCT column) counts unique non-NULL values only. Example: 15 total rows, 14 with salary filled, 4 unique departments → COUNT(*) = 15, COUNT(salary) = 14, COUNT(DISTINCT dept) = 4.

Q2: Which is faster — COUNT(*) or COUNT(1)?
Ans: In MySQL, COUNT(*) and COUNT(1) are identical in performance — MySQL optimizer treats them the same way. Both count total rows. COUNT(1) passes literal 1 for every row which is never NULL, so it behaves exactly like COUNT(*). Use COUNT(*) for clarity as it is the standard convention.

Q3: Can COUNT() return 0 or does it always return at least 1?
Ans: COUNT() can return 0 when no rows match the condition. For example: SELECT COUNT(*) FROM employees WHERE dept = 'Marketing' returns 0 if no Marketing department exists. COUNT always returns exactly one row with the count value — even if that value is 0.

2. SUM() — Total Value Calculate Karna

text

🔍 Definition: SUM() calculates the total sum of all non-NULL numeric values in a specified column. It works only with numeric data types (INT, DECIMAL, FLOAT). NULL values are automatically ignored in the calculation.

🎯 Samjho Simple Bhasha Mein: SUM matlab "jod do sabko." Jaise accountant ko poochho "saare employees ki total salary kitni hai?" — toh SUM(salary) use karoge. Agar ek employee ki salary NULL hai toh SUM usse skip kar dega — error nahi dega, bas baaki sabki jod dega.

💡 Key Point: SUM sirf numeric columns par kaam karta hai. String column par SUM lagaoge toh MySQL 0 return karega (implicit conversion) — koi error nahi dega but result meaningless hoga. Hamesha verify karo ki column numeric hai.

💻 Real-World Code Examples:

Example 1: Saare employees ki total salary kitni hai.

-- Total salary bill
SELECT SUM(salary) AS total_salary_bill

FROM employees;

Example 2: Sirf IT department ki total salary.

-- IT department total salary cost
SELECT SUM(salary) AS it_salary_cost

FROM employees

WHERE dept = 'IT';

Example 3: Annual salary bill calculate karo (monthly * 12).

-- Annual salary bill
SELECT
SUM(salary) AS monthly_total,
SUM(salary) * 12 AS annual_total,
SUM(salary * 0.10) AS total_bonus

FROM employees;

📊 Expected Output:

-- Example 1:
-- Example 2:
-- Example 3:
monthly_totalannual_totaltotal_bonus
893000.0010716000.0089300.00
text

⚠️ Common Mistakes:

  • Mistake: String column par SUM use karna → SUM(name) — 0 return karega, warning nahi dega.
    Fix: Sirf numeric columns par SUM use karo — INT, DECIMAL, FLOAT.
  • Mistake: NULL values ko 0 assume karna → SUM NULL ko ignore karta hai, 0 treat nahi karta.
    Fix: Agar NULL ko 0 maanna ho toh SUM(IFNULL(salary, 0)) use karo.
  • Mistake: SUM ke result ko directly compare karna bina alias ke → Readability problem.
    Fix: Hamesha alias do: SUM(salary) AS total

💬 Interview Questions:

Q1: What does SUM() return if all values are NULL?
Ans: SUM() returns NULL (not 0) if all values in the column are NULL or if no rows match the condition. To handle this, use IFNULL or COALESCE: SELECT IFNULL(SUM(salary), 0) AS total — this will return 0 instead of NULL.

Q2: Can SUM() be used with expressions?
Ans: Yes, SUM can calculate expressions per row before summing. SUM(salary * 12) calculates annual salary for each row and then sums them all. SUM(price * quantity) is commonly used in e-commerce to calculate total order value. The expression is evaluated per row first, then aggregated.

Q3: What is the difference between SUM(salary) * 12 and SUM(salary * 12)?
Ans: Mathematically both produce the same result. SUM(salary) * 12 first calculates total monthly then multiplies by 12. SUM(salary * 12) multiplies each row by 12 first then sums. Performance wise SUM(salary) * 12 is slightly better because multiplication happens once instead of per-row.

3. AVG() — Average Nikalna

text

🔍 Definition: AVG() calculates the arithmetic mean (average) of all non-NULL numeric values in a column. Formula: AVG = SUM(column) / COUNT(column). NULL values are excluded from both numerator and denominator.

🎯 Samjho Simple Bhasha Mein: AVG matlab "average nikalo." Jaise class ka average marks nikaalna — sabke marks jodo aur total students se divide karo. Important baat: agar kisi ki marks NULL hai toh AVG usko skip karega — total mein bhi nahi jodega aur count mein bhi nahi ginne ga. Yeh bahut important hai — agar 15 employees hain aur 1 ki salary NULL hai toh AVG 14 se divide karega, 15 se nahi.

💡 NULL Handling in AVG — Critical:
15 employees, 1 salary NULL → AVG = SUM(14 salaries) / 14
Agar NULL ko 0 treat karna ho → AVG(IFNULL(salary, 0)) = SUM(14 salaries + 0) / 15
Result different hoga! Interview mein zaroor poochha jaata hai.

💻 Real-World Code Examples:

Example 1: Saare employees ki average salary.

-- Average salary (NULL automatically ignored)
SELECT AVG(salary) AS avg_salary

FROM employees;

Example 2: NULL ko 0 treat karke average nikalo (different result).

-- NULL = 0 treat karke average (lower result)
SELECT AVG(IFNULL(salary, 0)) AS avg_with_null_as_zero

FROM employees;

Example 3: Department wise average salary comparison.

-- Average salary aur overall average se comparison
SELECT
AVG(salary) AS avg_salary,
ROUND(AVG(salary), 2) AS avg_rounded,
AVG(age) AS avg_age

FROM employees;

📊 Expected Output:

-- Example 1:
-- Example 2:
-- Example 3:
avg_salaryavg_roundedavg_age
63785.71428663785.7129.2667
text

⚠️ Common Mistakes:

  • Mistake: AVG mein NULL ko 0 maanna → Default behavior mein AVG NULL skip karta hai. AVG(salary) aur AVG(IFNULL(salary,0)) ka result alag hoga.
    Fix: Decide karo — NULL ko exclude karna hai ya 0 treat karna hai — dono ka use case alag hai.
  • Mistake: AVG ka result directly use karna bina ROUND ke → Long decimal values display honge.
    Fix: ROUND(AVG(salary), 2) — 2 decimal places tak round karo.
  • Mistake: SUM/COUNT se manually average calculate karna → SUM(salary)/COUNT(*) galat dega agar NULL hai (COUNT(*) includes NULL rows).
    Fix: SUM(salary)/COUNT(salary) use karo ya simply AVG(salary).

💬 Interview Questions:

Q1: How does AVG() handle NULL values?
Ans: AVG() completely ignores NULL values — they are excluded from both the sum (numerator) and the count (denominator). So if 15 rows exist but 1 has NULL salary, AVG = SUM(14 salaries) / 14, not 15. This means NULL has no impact on the average. To treat NULL as 0, use AVG(IFNULL(salary, 0)).

Q2: Is SUM(salary)/COUNT(*) same as AVG(salary)?
Ans: No! COUNT(*) counts all rows including those with NULL salary, while AVG excludes NULLs from the denominator. SUM(salary)/COUNT(*) would give a lower average because the denominator is larger. The correct manual calculation equivalent is SUM(salary)/COUNT(salary) — which matches AVG(salary) exactly.

Q3: How to find employees earning above average salary?
Ans: Using a subquery: SELECT name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees). The subquery calculates the average first, then the outer query filters employees earning above that. This is a commonly asked query in SQL interviews.

4. MIN() & MAX() — Smallest & Largest Values

text

🔍 Definition: MIN() returns the smallest value and MAX() returns the largest value from a column. They work with numeric, string (alphabetical) and date columns. Both ignore NULL values.

🎯 Samjho Simple Bhasha Mein: MIN matlab "sabse chhota" aur MAX matlab "sabse bada." Salary mein MIN dega sabse kam salary wala, MAX dega sabse zyada wala. Dates mein MIN dega sabse purani date (pehle join hone wala), MAX dega sabse latest. Strings mein MIN dega alphabetically pehla (A...), MAX dega last (Z...).

💡 MIN & MAX Work With:
Numbers: MIN(salary) = lowest salary, MAX(salary) = highest salary
Dates: MIN(hire_date) = earliest join, MAX(hire_date) = latest join
Strings: MIN(name) = alphabetically first, MAX(name) = alphabetically last

💻 Real-World Code Examples:

Example 1: Sabse kam aur sabse zyada salary.

-- Min and Max salary
SELECT
MIN(salary) AS lowest_salary,
MAX(salary) AS highest_salary,
MAX(salary) - MIN(salary) AS salary_range

FROM employees;

Example 2: Sabse pehle aur sabse baad mein join karne wala employee.

-- Earliest and latest hire dates
SELECT
MIN(hire_date) AS first_hire,
MAX(hire_date) AS latest_hire

FROM employees;

Example 3: Sabse kam aur zyada age wale employees.

-- Youngest and oldest employee age
SELECT
MIN(age) AS youngest,
MAX(age) AS oldest

FROM employees;

Example 4: Sabse zyada salary wale employee ka naam nikalo (subquery se).

-- Employee with highest salary (subquery)
SELECT name, salary

FROM employees

WHERE salary = (SELECT MAX(salary) FROM employees);

📊 Expected Output:

-- Example 1:
lowest_salaryhighest_salarysalary_range
39000.0092000.0053000.00
-- Example 2:
first_hirelatest_hire
2018-08-122023-09-15
-- Example 3:
youngestoldest
2238
-- Example 4:
namesalary
Anjali Mehta92000.00
text

⚠️ Common Mistakes:

  • Mistake: SELECT name, MAX(salary) likhna bina GROUP BY ke → Error ya galat name return hoga (MySQL sirf 1 random name dega, guaranteed correct nahi).
    Fix: Subquery use karo: WHERE salary = (SELECT MAX(salary)...)
  • Mistake: MIN/MAX string comparison mein case sensitivity bhool jaana → MySQL default collation mein case-insensitive compare karta hai.
    Fix: Case-sensitive results ke liye BINARY keyword use karo.
  • Mistake: MIN/MAX ko COUNT se confuse karna → MIN/MAX values return karte hain, COUNT ginti karta hai.

💬 Interview Questions:

Q1: How to find the employee name with the highest salary?
Ans: Using subquery: SELECT name, salary FROM employees WHERE salary = (SELECT MAX(salary) FROM employees). You cannot directly do SELECT name, MAX(salary) because MAX is an aggregate function and name is not — MySQL would return an unpredictable name value. The subquery approach is the standard and reliable method.

Q2: Can MIN() and MAX() work with DATE columns?
Ans: Yes, MIN returns the earliest (oldest) date and MAX returns the latest (newest) date. This is commonly used to find the first hire date, latest order date, or date range of records. Example: SELECT MIN(hire_date), MAX(hire_date) FROM employees.

Q3: What is the difference between MAX(salary) and ORDER BY salary DESC LIMIT 1?
Ans: MAX(salary) returns only the salary value — you cannot select other columns without a subquery. ORDER BY salary DESC LIMIT 1 returns the entire row including name, dept etc. If you just need the value, MAX is better. If you need the full row details, ORDER BY + LIMIT is simpler. Performance wise both are similar — MAX may use index more efficiently.

5. GROUP BY — Data Ko Groups Mein Todna

text

🔍 Definition: GROUP BY divides the result set into groups based on one or more columns, then aggregate functions are applied to each group separately. Every non-aggregated column in SELECT must appear in the GROUP BY clause.

🎯 Samjho Simple Bhasha Mein: GROUP BY matlab "groups banao aur har group ka separately calculation karo." Socho ek school mein 100 students hain — hum class wise average marks nikalna chahte hain. Toh pehle students ko class ke hisaab se groups mein todenge (GROUP BY class), phir har group ka AVG(marks) calculate karenge. Bina GROUP BY ke ek hi overall number milega — GROUP BY se har group ka alag milega.

💡 Golden Rule: SELECT mein jo bhi column likha hai aur woh aggregate function (COUNT, SUM, AVG, MIN, MAX) ke andar nahi hai — woh column GROUP BY mein ZAROOR hona chahiye. Warna MySQL error dega (strict mode) ya random result (non-strict mode).

💻 Real-World Code Examples:

Example 1: Har department mein kitne employees hain.

-- Department wise employee count
SELECT
dept,
COUNT(*) AS emp_count

FROM employees

GROUP BY dept;

Example 2: Department wise complete salary analysis.

-- Department wise complete analysis
SELECT
dept,
COUNT(*) AS emp_count,
SUM(salary) AS total_salary,
ROUND(AVG(salary), 2) AS avg_salary,
MIN(salary) AS min_salary,
MAX(salary) AS max_salary

FROM employees

GROUP BY dept

ORDER BY avg_salary DESC;

Example 3: City wise employee count.

-- City wise employee distribution
SELECT
city,
COUNT(*) AS emp_count,
ROUND(AVG(salary), 2) AS avg_salary

FROM employees

GROUP BY city

ORDER BY emp_count DESC;

Example 4: Multi-column GROUP BY — Department aur City wise breakdown.

-- Department + City wise breakdown
SELECT
dept,
city,
COUNT(*) AS emp_count,
ROUND(AVG(salary), 2) AS avg_salary

FROM employees

GROUP BY dept, city

ORDER BY dept, city;

📊 Expected Output:

-- Example 1 (Department wise count):
deptemp_count
IT5
HR3
Sales4
Finance3
-- Example 2 (Department wise analysis):
deptemp_counttotal_salaryavg_salarymin_salarymax_salary
IT5393000.0078600.0055000.0092000.00
Finance3204000.0068000.0065000.0071000.00
HR3115000.0057500.0053000.0062000.00
Sales4183000.0045750.0039000.0051000.00
-- Example 3 (City wise):
cityemp_countavg_salary
Delhi557000.00
Mumbai457500.00
Bangalore390000.00
Chennai356333.33
text

⚠️ Common Mistakes:

  • Mistake: SELECT mein non-aggregate column likhna aur GROUP BY mein include na karna → SELECT name, dept, COUNT(*) bina GROUP BY name, dept → Error (strict mode) ya random name (non-strict).
    Fix: Har non-aggregate column GROUP BY mein daalo.
  • Mistake: GROUP BY ke baad WHERE use karna → WHERE hamesha GROUP BY se PEHLE aata hai.
    Fix: Sequence yaad rakho: WHERE → GROUP BY → HAVING → ORDER BY → LIMIT.
  • Mistake: GROUP BY aur DISTINCT confuse karna → GROUP BY aggregation allow karta hai, DISTINCT sirf unique values deta hai.
    Fix: Aggregation chahiye toh GROUP BY, sirf unique values chahiye toh DISTINCT.

💬 Interview Questions:

Q1: What is the rule for SELECT columns when using GROUP BY?
Ans: Every column in SELECT that is not inside an aggregate function (COUNT, SUM, AVG, MIN, MAX) must be listed in the GROUP BY clause. This is enforced in MySQL's strict mode (ONLY_FULL_GROUP_BY). Example: SELECT dept, COUNT(*) requires GROUP BY dept. Writing SELECT name, dept, COUNT(*) GROUP BY dept would fail because name is not grouped or aggregated.

Q2: Can we GROUP BY multiple columns? How does it work?
Ans: Yes, GROUP BY can have multiple columns — it creates groups based on unique combinations of those columns. GROUP BY dept, city creates separate groups for each department-city combination: (IT, Delhi), (IT, Mumbai), (HR, Delhi) etc. Each combination gets its own aggregate calculation.

Q3: Can we use column alias in GROUP BY?
Ans: In MySQL, yes — you can use column aliases in GROUP BY because MySQL evaluates SELECT aliases before GROUP BY as an extension. However in standard SQL and other databases (PostgreSQL, Oracle), this is not allowed. For portability, use original column names in GROUP BY. Example: GROUP BY dept instead of GROUP BY department_name (alias).

6. HAVING — Groups Ko Filter Karna

text

🔍 Definition: HAVING clause filters groups created by GROUP BY based on aggregate conditions. It works like WHERE but operates on grouped/aggregated data instead of individual rows. HAVING is the only way to filter using aggregate function results.

🎯 Samjho Simple Bhasha Mein: WHERE individual rows ko filter karta hai (GROUP BY se pehle). HAVING groups ko filter karta hai (GROUP BY ke baad). Socho HR manager ne kaha "mujhe sirf un departments ki list do jinmein 3 se zyada employees hain." Toh pehle GROUP BY dept karenge (departments ke groups banenge), phir HAVING COUNT(*) > 3 lagayenge (sirf bade departments dikhenge).

💡 KEY Rule: HAVING clause mein sirf aggregate functions (COUNT, SUM, AVG, MIN, MAX) ya GROUP BY mein listed columns use ho sakte hain. Agar non-aggregate column ko filter karna hai toh WHERE use karo, HAVING nahi.

💻 Real-World Code Examples:

Example 1: Sirf woh departments dikhao jinmein 3 se zyada employees hain.

-- Departments with more than 3 employees
SELECT
dept,
COUNT() AS emp_count

FROM employees

GROUP BY dept

HAVING COUNT() > 3;

Example 2: Departments jinki average salary 60000 se zyada hai.

-- Departments with avg salary above 60000
SELECT
dept,
ROUND(AVG(salary), 2) AS avg_salary,
COUNT(*) AS emp_count

FROM employees

GROUP BY dept

HAVING AVG(salary) > 60000

ORDER BY avg_salary DESC;

Example 3: WHERE + GROUP BY + HAVING sab combined — Delhi exclude karke, 2+ employees wale departments dikhao.

-- Combined: WHERE (row filter) + GROUP BY + HAVING (group filter)
SELECT
dept,
COUNT() AS emp_count,
ROUND(AVG(salary), 2) AS avg_salary

FROM employees

WHERE city != 'Delhi' -- Step 1: Delhi ke employees pehle hata do

GROUP BY dept -- Step 2: Baaki employees ko dept wise
group karo

HAVING COUNT() >= 2 -- Step 3: Sirf 2+ employees wale groups dikhao

ORDER BY avg_salary DESC;
-- Step 4: Average salary se sort karo

Example 4: HAVING with multiple conditions.

-- Departments where count > 2 AND total salary > 150000
SELECT
dept,
COUNT() AS emp_count,
SUM(salary) AS total_salary

FROM employees

GROUP BY dept

HAVING COUNT() > 2 AND SUM(salary) > 150000;

📊 Expected Output:

-- Example 1 (Departments with > 3 employees):

deptemp_count
IT5
Sales4
deptavg_salary
emp_countIT
78600.005
Finance68000.00
3dept
emp_counttotal_salary
IT5
393000.00Finance
3204000.00
Sales4

-- Example 2 (Avg salary > 60000): -- Example 4 (Count > 2 AND total > 150000): 183000.00

text

⚠️ Common Mistakes:

  • Mistake: HAVING ko WHERE ki jagah use karna → HAVING city = 'Delhi' — kaam karega but slow hoga kyunki pehle grouping hogi phir filter.
    Fix: Individual row filter → WHERE. Aggregate filter → HAVING.
  • Mistake: HAVING bina GROUP BY ke use karna → MySQL mein allowed hai (poora table ek group maana jaayega) but confusing hai.
    Fix: HAVING hamesha GROUP BY ke saath use karo.
  • Mistake: HAVING mein alias use karna → MySQL mein kaam karta hai, but standard SQL mein nahi. HAVING emp_count > 3 ki jagah HAVING COUNT(*) > 3 likho portability ke liye.

💬 Interview Questions:

Q1: Why can't we use WHERE with aggregate functions?
Ans: WHERE executes before GROUP BY — at that point rows haven't been grouped yet and aggregate values haven't been calculated. Since aggregate functions like COUNT, SUM, AVG need grouped data to produce results, they cannot be used in WHERE. HAVING executes after GROUP BY when aggregated values are available.

Q2: Can HAVING be used without GROUP BY?
Ans: Yes, MySQL allows HAVING without GROUP BY — in this case the entire table is treated as a single group. Example: SELECT COUNT(*) FROM employees HAVING COUNT(*) > 10. However this is uncommon and can be confusing. Best practice is to always pair HAVING with GROUP BY.

Q3: Can we use both WHERE and HAVING in the same query?
Ans: Yes, and this is a common pattern. WHERE filters individual rows before grouping, then GROUP BY creates groups, then HAVING filters groups. Example: WHERE salary IS NOT NULL (remove NULL rows first) → GROUP BY dept (create groups) → HAVING AVG(salary) > 60000 (keep groups with high average). WHERE reduces the data first making GROUP BY and HAVING faster.

7. WHERE vs HAVING — Complete Difference

text

🔍 Definition: WHERE and HAVING both filter data, but at different stages of query execution. WHERE filters individual rows before grouping. HAVING filters groups after aggregation. Understanding their difference is one of the most frequently asked SQL interview questions.

🎯 Samjho Simple Bhasha Mein: Socho ek party hai 100 logon ki. WHERE matlab bouncer — gate par hi decide karta hai kaun andar aayega (row-level filter). HAVING matlab VIP section ka guard — andar aake groups banne ke baad decide karta hai kaunsa group VIP section mein jaayega (group-level filter). Bouncer (WHERE) pehle kaam karta hai, VIP guard (HAVING) baad mein.

📊 WHERE vs HAVING — Complete Comparison:

Feature WHERE HAVING
Kab Execute Hota Hai GROUP BY se pehle GROUP BY ke baad
Kya Filter Karta Hai Individual rows Groups (aggregated data)
Aggregate Functions ❌ Cannot use (COUNT, SUM etc.) ✅ Can use
GROUP BY Required? ❌ No ✅ Usually yes
Performance ✅ Faster (filters early, less data to group) ❌ Slower (filters after grouping)
Use With SELECT, UPDATE, DELETE SELECT with GROUP BY
Example WHERE salary > 50000 HAVING AVG(salary) > 50000

💻 Side-by-Side Comparison Query:

-- WHERE: Pehle sirf 50000+ salary wale rows filter karo
-- GROUP BY: Phir departments mein group karo
-- HAVING: Phir sirf 2+ employees wale groups rakho
SELECT
dept,
COUNT() AS emp_count,
ROUND(AVG(salary), 2) AS avg_salary

FROM employees

WHERE salary > 50000 -- Row filter: sirf 50K+ salary

GROUP BY dept --
Group by department

HAVING COUNT() >= 2 --
Group filter: 2+ employees

ORDER BY avg_salary DESC;
-- Sort by avg salary

📊 Query Execution Flow:

Step 1:
FROM employees → 15 rows loaded
Step 2:
WHERE salary > 50000 → 10 rows remain (5 low salary removed)
Step 3:
GROUP BY dept → 4 groups: IT(4), HR(2), Finance(3), Sales(1)
Step 4:
HAVING COUNT(*) >= 2 → 3 groups remain (Sales removed — only 1 emp)
Step 5: SELECT dept, COUNT, AVG → Columns calculated
Step 6:
ORDER BY avg_salary DESC → Results sorted

💬 Interview Questions:

Q1: What is the exact difference between WHERE and HAVING?
Ans: WHERE filters individual rows before GROUP BY executes — it cannot use aggregate functions. HAVING filters groups after GROUP BY — it can use aggregate functions. WHERE is like a bouncer at the door (filters before entry), HAVING is like a VIP section guard (filters after groups are formed). Performance wise, WHERE is better because it reduces data early.

Q2: Can we replace WHERE with HAVING?
Ans: Technically yes — HAVING dept = 'IT' would work in MySQL even without GROUP BY. But it is bad practice for two reasons: (1) HAVING is slower because it filters after grouping, (2) WHERE is semantically correct for row-level filtering. Always use WHERE for non-aggregate filters and HAVING for aggregate filters.

Q3: Write the complete execution order of a SQL SELECT query.
Ans: FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT. This is the logical execution order — different from the writing order (SELECT ... FROM ... WHERE ...). Understanding this order explains why alias cannot be used in WHERE (SELECT runs after WHERE) and why HAVING can use aggregates (runs after GROUP BY).

8. Combined Queries — Real-World Analytics Scenarios

text

🔍 Definition: Real-world SQL analytics requires combining multiple concepts — aggregate functions, GROUP BY, HAVING, WHERE, ORDER BY and LIMIT — all in one query. This section demonstrates how these pieces fit together for practical business analytics.

🎯 Samjho Simple Bhasha Mein: Ab tak humne ek ek tool alag alag seekha — COUNT, SUM, AVG, GROUP BY, HAVING. Ab sab milaake real duniya ke questions solve karenge. Jaise manager poochhe "mujhe top 3 departments batao jinki average salary 50000 se zyada hai aur jinmein 2+ employees hain — Delhi ke employees exclude karke." Yahi real SQL hai!

💻 Scenario 1: HR Dashboard — Department Summary Report

-- Complete department wise analysis report
SELECT
dept AS 'Department',
COUNT(*) AS 'Total Staff',
SUM(salary) AS 'Salary Cost',
ROUND(AVG(salary), 2) AS 'Avg Salary',
MIN(salary) AS 'Min Salary',
MAX(salary) AS 'Max Salary',
MAX(salary) - MIN(salary) AS 'Salary Range',
ROUND(AVG(age), 1) AS 'Avg Age'

FROM employees

WHERE salary IS NOT NULL

GROUP BY dept

ORDER BY SUM(salary) DESC;

💻 Scenario 2: Budget Alert — High Cost Departments

-- Departments where total salary exceeds 180000
SELECT
dept,
SUM(salary) AS total_cost,
COUNT() AS headcount,
ROUND(SUM(salary) / COUNT(), 2) AS cost_per_employee

FROM employees

WHERE salary IS NOT NULL

GROUP BY dept

HAVING SUM(salary) > 180000

ORDER BY total_cost DESC;

💻 Scenario 3: Year Wise Hiring Trend Analysis

-- Year wise hiring count and average salary
SELECT
YEAR(hire_date) AS hire_year,
COUNT(*) AS hires,
ROUND(AVG(salary), 2) AS avg_salary_of_batch

FROM employees

GROUP BY YEAR(hire_date)

ORDER BY hire_year;

💻 Scenario 4: City Wise Top Paying Department

-- City + Department wise analysis (filtered)
SELECT
city,
dept,
COUNT() AS emp_count,
ROUND(AVG(salary), 2) AS avg_salary

FROM employees

WHERE salary IS NOT NULL AND age > 25

GROUP BY city, dept

HAVING COUNT() >= 1

ORDER BY city, avg_salary DESC;

💻 Scenario 5: Employees Above Average Salary (Subquery)

-- Find employees earning above company average
SELECT
name,
dept,
salary,
ROUND(salary - (SELECT AVG(salary) FROM employees), 2) AS above_avg_by

FROM employees

WHERE salary > (SELECT AVG(salary) FROM employees)

ORDER BY salary DESC;

📊 Expected Output (Scenario 1 — HR Dashboard):

DepartmentTotal StaffSalary CostAvg SalaryMin SalaryMax SalarySalary RangeAvg Age
IT5393000.0078600.0055000.0092000.0037000.0032.0
Finance3204000.0068000.0065000.0071000.006000.0032.3
Sales4183000.0045750.0039000.0051000.0012000.0024.5
HR2115000.0057500.0053000.0062000.009000.0030.5
text

📊 Expected Output (Scenario 3 — Hiring Trend):

hire_yearhiresavg_salary_of_batch
2018192000.00
2019279500.00
2020371000.00
2021364333.33
2022351333.33
2023342000.00
text

⚠️ Common Mistakes:

  • Mistake: Complex queries mein execution order bhool jaana → Unexpected results milte hain.
    Fix: Hamesha yaad rakho: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
  • Mistake: Subquery mein NULL handle na karna → AVG subquery mein NULL automatically skip hota hai but agar 0 count karna ho toh IFNULL use karo.
  • Mistake: YEAR(), MONTH() functions slow hote hain large tables par → Full table scan hota hai.
    Fix: Date columns par index ya pre-computed year column use karo production mein.

💬 Interview Questions:

Q1: How to find the department with the second highest average salary?
Ans: SELECT dept, ROUND(AVG(salary), 2) AS avg_sal FROM employees GROUP BY dept ORDER BY avg_sal DESC LIMIT 1 OFFSET 1. This groups by department, calculates average, sorts descending and skips the first (highest) to return the second highest.

Q2: How to find employees earning above their department's average?
Ans: Using correlated subquery: SELECT e1.name, e1.dept, e1.salary FROM employees e1 WHERE e1.salary > (SELECT AVG(e2.salary) FROM employees e2 WHERE e2.dept = e1.dept). The inner query calculates average per department dynamically for each row in the outer query.

Q3: Write a query to find duplicate records in a table.
Ans: SELECT name, COUNT(*) AS cnt FROM employees GROUP BY name HAVING COUNT(*) > 1. This groups by the column you want to check for duplicates and filters groups with more than 1 occurrence. For multi-column duplicates: GROUP BY name, email HAVING COUNT(*) > 1.

Q4: What is the complete execution order of a SELECT query?
Ans: 1) FROM (identify tables) → 2) WHERE (filter rows) → 3) GROUP BY (create groups) → 4) HAVING (filter groups) → 5) SELECT (choose columns and calculate) → 6) DISTINCT (remove duplicates) → 7) ORDER BY (sort results) → 8) LIMIT/OFFSET (restrict output). This is the most important concept for understanding why certain things work or fail in SQL.

Quick Cheat Sheet — Aggregate Functions

Function Purpose NULL Handling Example
COUNT(*) Total row count Includes NULL rows SELECT COUNT(*)
COUNT(col) Non-NULL count Ignores NULL COUNT(salary)
SUM(col) Total sum Ignores NULL SUM(salary)
AVG(col) Average value Ignores NULL (both SUM & COUNT) AVG(salary)
MIN(col) Smallest value Ignores NULL MIN(salary)
MAX(col) Largest value Ignores NULL MAX(salary)

🧠 SQL Execution Order — Yaad Kar Lo!

LIKHNE KA
ORDER: EXECUTE HONE KA
ORDER: ───────────────── ───────────────────── SELECT (1st likha)
FROM (1st execute)
FROM (2nd)
WHERE (2nd)
WHERE (3rd)
GROUP BY (3rd)
GROUP BY (4th)
HAVING (4th)
HAVING (5th) SELECT (5th)
ORDER BY (6th) DISTINCT (6th)
LIMIT (7th)
ORDER BY (7th)
LIMIT (8th)

Next: Data Insights MySQL Masterclass — Part 4

Part 4 mein hum cover karenge: JOINS — INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN, SELF JOIN, CROSS JOIN — multiple tables ko connect karne ka complete guide with real-world examples aur interview questions Data Insights par.

Happy Querying & Keep Learning SQL! 🚀

👤
Jatin Kumar
Data Analyst & Educator

Python, SQL, Power BI aur Excel mein practical tutorials likhta hoon — taaki data analytics seekhna aasan ho. Portfolio: jatinanalytics.co.in

Portfolio LinkedIn GitHub Kaggle All Articles
Share:

💬 Comments (0)

Spam/links allowed nahi hain — respectful comments welcome!

Loading comments...

Was this article helpful?
Previous ArticleSELECT Queries Deep Dive — WHERE, Operators, ORDER BY, LIMITNext Article SQL JOINS — Multiple Tables Ko Connect Karna

📚 More Articles Like This

MySQL Fundamentals

Read Article

Difference Between RANK(), DENSE_RANK(), and ROW_NUMBER() in SQL

Read Article