The HAVING clause is used to filter grouped results created by the GROUP BY clause.
It’s like WHERE, but works after aggregation (e.g., COUNT(), SUM()).
⚠️ Why Not Use WHERE?
🔹 WHERE filters rows before grouping
🔹 HAVING filters groups after aggregation
📌 Syntax
SELECT <column_name> AS <alias_name>
FROM <table_name>
GROUP BY <column_name>
HAVING <condition>;➕ Example with SUM()
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
department VARCHAR(50) NOT NULL,
salary INT NOT NULL
);
-- Insert sample data
INSERT INTO employees (name, department, salary) VALUES
('Alice', 'Engineering', 120000),
('Bob', 'Engineering', 150000),
('Carol', 'Engineering', 180000),
('David', 'Marketing', 90000),
('Eve', 'Marketing', 85000),
('Frank', 'Sales', 100000),
('Grace', 'Sales', 110000),
('Heidi', 'Sales', 95000),
('Ivan', 'HR', 60000),
('Judy', 'HR', 55000);
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department
HAVING SUM(salary) > 500000;✔️ Shows departments with total salary above ₹5 lakh
🚫 Invalid Example (Can’t Use Aggregates in WHERE)
❌ This won’t work:
SELECT grade, COUNT(*)
FROM students
WHERE COUNT(*) > 3
GROUP BY grade;🛑 COUNT() cannot be used inside WHERE
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SUM() & HAVING — Try It Yourself</title>
<style>
.infographic-sumhaving {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--good: #16a34a;
--good-soft: #dcfce7;
--bad: #dc2626;
--bad-soft: #fee2e2;
--on-accent: #ffffff;
--display-font: Georgia, 'Iowan Old Style', serif;
--body-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--mono-font: 'SF Mono', Consolas, monospace;
background: var(--paper);
color: var(--ink);
font-family: var(--body-font);
max-width: 780px;
margin: 0 auto;
box-sizing: border-box;
overflow-x: hidden;
}
.infographic-sumhaving[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--good: #4ade80;
--good-soft: #123420;
--bad: #f87171;
--bad-soft: #3a1414;
--on-accent: #101418;
}
.infographic-sumhaving * { box-sizing: border-box; }
.sm-topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem; gap: 0.5rem; flex-wrap: wrap; }
.sm-progress { font-size: 0.78rem; color: var(--ink-soft); }
.sm-showfull-btn {
font: inherit;
font-size: 0.78rem;
background: none;
border: 1px solid var(--line);
border-radius: 999px;
padding: 0.35rem 0.8rem;
cursor: pointer;
color: var(--ink-soft);
}
.sm-showfull-btn:hover { border-color: var(--accent); color: var(--accent); }
.sm-showfull-btn.is-on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.sm-fullcode-panel { display: none; margin-bottom: 1.25rem; }
.sm-fullcode-panel.is-visible { display: block; }
.sm-fullcode-scroll { overflow-x: auto; }
.sm-fullcode {
background: var(--ink);
color: #d4e2ff;
border-radius: 8px;
padding: 1rem 1.2rem;
font-family: var(--mono-font);
font-size: 0.72rem;
line-height: 1.7;
white-space: pre;
opacity: 0.85;
display: inline-block;
min-width: 100%;
}
.infographic-sumhaving[data-theme="dark"] .sm-fullcode { background: #05070a; }
.sm-frame {
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 10px;
padding: 1.25rem;
}
.sm-kw { color: #9cc5ff; }
.sm-str { color: #f8d980; }
.sm-num { color: #4ade80; }
.sm-cm { color: #7c8aa8; }
.sm-fn { color: #f472b6; }
.sm-step { display: none; margin-bottom: 1rem; }
.sm-step.is-revealed { display: block; animation: sm-step-in 0.4s ease; }
@keyframes sm-step-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
.sm-step.is-done .sm-step-code-full { display: none; }
.sm-step.is-done .sm-step-done-pill { display: flex; }
.sm-step-done-pill {
display: none;
align-items: center;
gap: 0.5rem;
background: var(--good-soft);
color: var(--good);
border-radius: 7px;
padding: 0.55rem 0.85rem;
font-family: var(--mono-font);
font-size: 0.78rem;
cursor: pointer;
}
.sm-step-done-pill.is-danger-done { background: var(--bad-soft); color: var(--bad); }
.sm-step-done-pill .chevron { margin-left: auto; font-size: 0.7rem; opacity: 0.7; }
.sm-step.is-expanded-done .sm-step-code-full { display: block; }
.sm-step.is-expanded-done .sm-step-done-pill { background: var(--accent-soft); color: var(--accent); }
.sm-step-label { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-soft); margin: 0 0 0.5rem; }
.sm-step-note { font-size: 0.82rem; color: var(--ink-soft); margin: 0.5rem 0 0; line-height: 1.45; }
.sm-code-scroll { overflow-x: auto; }
.sm-code {
background: var(--ink);
color: #d4e2ff;
border-radius: 8px;
padding: 0.85rem 1.05rem;
font-family: var(--mono-font);
font-size: 0.8rem;
line-height: 1.6;
white-space: pre;
display: inline-block;
min-width: 100%;
}
.infographic-sumhaving[data-theme="dark"] .sm-code { background: #05070a; }
.sm-run-row { display: flex; justify-content: center; margin-top: 0.85rem; }
.sm-run-btn {
font: inherit;
font-size: 0.85rem;
background: var(--accent);
color: var(--on-accent);
border: none;
border-radius: 7px;
padding: 0.55rem 1.1rem;
cursor: pointer;
}
.sm-run-btn:hover { opacity: 0.9; }
.sm-run-btn:disabled { opacity: 0.55; cursor: wait; }
.sm-run-btn.is-danger { background: var(--bad); }
.sm-run-btn.is-secondary { background: var(--paper); color: var(--ink); border: 1px solid var(--line); }
.sm-output-label { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-soft); margin: 1.1rem 0 0.6rem; }
.sm-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; min-width: 0; }
table.sm-table {
width: 100%;
min-width: 260px;
border-collapse: collapse;
background: var(--paper);
border-radius: 8px;
font-size: 0.88rem;
}
table.sm-table th, table.sm-table td {
border: 1px solid var(--line);
padding: 0.5rem 0.7rem;
text-align: left;
white-space: nowrap;
}
table.sm-table th { background: var(--accent); color: var(--on-accent); font-weight: 700; font-size: 0.72rem; }
table.sm-table th.agg-col { text-decoration: underline; }
table.sm-table tr.is-new td { animation: sm-row-in 0.5s ease; }
@keyframes sm-row-in { from { background: var(--good-soft); } to { background: transparent; } }
table.sm-table td.is-aggcol { color: var(--accent); font-weight: 700; }
.sm-empty-note { font-size: 0.82rem; color: var(--ink-soft); font-style: italic; padding: 0.6rem 0.2rem; }
.sm-error-box {
background: var(--bad-soft);
color: var(--bad);
border-radius: 8px;
padding: 0.9rem 1.05rem;
font-family: var(--mono-font);
font-size: 0.8rem;
line-height: 1.6;
white-space: pre-wrap;
}
.sm-status {
text-align: center;
font-size: 0.82rem;
margin-top: 0.85rem;
padding: 0.5rem 0.8rem;
border-radius: 7px;
display: none;
}
.sm-status.is-visible { display: block; animation: sm-step-in 0.3s ease; }
.sm-status.st-good { background: var(--good-soft); color: var(--good); }
.sm-status.st-bad { background: var(--bad-soft); color: var(--bad); }
.sm-reset-row { display: flex; justify-content: center; margin-top: 1.25rem; }
@media (prefers-reduced-motion: reduce) {
.infographic-sumhaving *, .infographic-sumhaving *::before, .infographic-sumhaving *::after {
transition-duration: 0.001ms !important;
animation-duration: 0.001ms !important;
}
}
</style>
</head>
<body>
<div class="infographic-sumhaving" id="sumhaving">
<div class="sm-topbar">
<span class="sm-progress" id="smProgress"></span>
<button class="sm-showfull-btn" id="smShowFullBtn" type="button">👁️ Show full SQL script</button>
</div>
<div class="sm-fullcode-panel" id="smFullCodePanel">
<div class="sm-fullcode-scroll">
<div class="sm-fullcode" id="smFullCodeContent"></div>
</div>
</div>
<div class="sm-frame">
<div id="smSteps"></div>
<p class="sm-status" id="smStatus"></p>
<p class="sm-output-label" id="smOutputLabel" style="display:none;">Query Result</p>
<div class="sm-table-scroll" id="smTableScroll" style="display:none;">
<table class="sm-table" id="smTable"></table>
</div>
<p class="sm-empty-note" id="smEmptyNote" style="display:none;">(no rows)</p>
<div class="sm-error-box" id="smErrorBox" style="display:none;"></div>
<div class="sm-reset-row">
<button class="sm-run-btn is-secondary" id="smReset" type="button">↺ Reset</button>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('sumhaving');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
(function () {
const employees = [
{ id: 1, name: 'Alice', department: 'Engineering', salary: 120000 },
{ id: 2, name: 'Bob', department: 'Engineering', salary: 150000 },
{ id: 3, name: 'Carol', department: 'Engineering', salary: 180000 },
{ id: 4, name: 'David', department: 'Marketing', salary: 90000 },
{ id: 5, name: 'Eve', department: 'Marketing', salary: 85000 },
{ id: 6, name: 'Frank', department: 'Sales', salary: 100000 },
{ id: 7, name: 'Grace', department: 'Sales', salary: 110000 },
{ id: 8, name: 'Heidi', department: 'Sales', salary: 95000 },
{ id: 9, name: 'Ivan', department: 'HR', salary: 60000 },
{ id: 10, name: 'Judy', department: 'HR', salary: 55000 }
];
function kw(s) { return '<span class="sm-kw">' + s + '</span>'; }
function str(s) { return '<span class="sm-str">' + s + '</span>'; }
function num(s) { return '<span class="sm-num">' + s + '</span>'; }
function cm(s) { return '<span class="sm-cm">' + s + '</span>'; }
function fn(s) { return '<span class="sm-fn">' + s + '</span>'; }
function groupBy(rows, keyFn) {
const map = new Map();
rows.forEach(r => {
const k = keyFn(r);
if (!map.has(k)) map.set(k, []);
map.get(k).push(r);
});
return map;
}
const setupCode =
cm('-- Create the employees table and add sample data') + '\n' +
kw('CREATE TABLE') + ' employees (\n' +
' id ' + kw('INT AUTO_INCREMENT PRIMARY KEY') + ',\n' +
' name ' + kw('VARCHAR') + '(50) ' + kw('NOT NULL') + ',\n' +
' department ' + kw('VARCHAR') + '(50) ' + kw('NOT NULL') + ',\n' +
' salary ' + kw('INT') + ' ' + kw('NOT NULL') + '\n' +
');\n\n' +
kw('INSERT INTO') + ' employees (name, department, salary) ' + kw('VALUES') + '\n' +
'(' + str("'Alice'") + ', ' + str("'Engineering'") + ', ' + num('120000') + '),\n' +
'(' + str("'Bob'") + ', ' + str("'Engineering'") + ', ' + num('150000') + '),\n' +
'(' + str("'Carol'") + ', ' + str("'Engineering'") + ', ' + num('180000') + '),\n' +
'(' + str("'David'") + ', ' + str("'Marketing'") + ', ' + num('90000') + '),\n' +
'(' + str("'Eve'") + ', ' + str("'Marketing'") + ', ' + num('85000') + '),\n' +
'(' + str("'Frank'") + ', ' + str("'Sales'") + ', ' + num('100000') + '),\n' +
'(' + str("'Grace'") + ', ' + str("'Sales'") + ', ' + num('110000') + '),\n' +
'(' + str("'Heidi'") + ', ' + str("'Sales'") + ', ' + num('95000') + '),\n' +
'(' + str("'Ivan'") + ', ' + str("'HR'") + ', ' + num('60000') + '),\n' +
'(' + str("'Judy'") + ', ' + str("'HR'") + ', ' + num('55000') + ');';
const groupCode = cm('-- Total salary per department') + '\n' + kw('SELECT') + ' department, ' + fn('SUM') + '(salary) ' + kw('AS') + ' total_salary\n' + kw('FROM') + ' employees\n' + kw('GROUP BY') + ' department;';
const havingCode = cm('-- Now keep only departments over \u20b95,00,000') + '\n' + kw('SELECT') + ' department, ' + fn('SUM') + '(salary) ' + kw('AS') + ' total_salary\n' + kw('FROM') + ' employees\n' + kw('GROUP BY') + ' department\n' + kw('HAVING') + ' ' + fn('SUM') + '(salary) > ' + num('500000') + ';';
const invalidCode = cm('-- \u274c Invalid: aggregates can\u2019t be used in WHERE') + '\n' + kw('SELECT') + ' grade, ' + fn('COUNT') + '(*)\n' + kw('FROM') + ' students\n' + kw('WHERE') + ' ' + fn('COUNT') + '(*) > ' + num('3') + '\n' + kw('GROUP BY') + ' grade;';
const steps = [
{ key: 'setup', title: 'Setup', btnLabel: '▶ Run Setup', doneLabel: '✓ Setup ran', code: setupCode, note: '', type: 'table' },
{ key: 'group', title: 'SUM() per department', btnLabel: '▶ Run SUM(salary) GROUP BY', doneLabel: '✓ SUM() ran', code: groupCode, note: 'Every department gets its own total, no filtering yet.', type: 'table' },
{ key: 'having', title: 'Add HAVING > ₹5,00,000', btnLabel: '▶ Run HAVING SUM > 500000', doneLabel: '✓ HAVING ran', code: havingCode, note: '', type: 'table' },
{ key: 'invalid', title: 'Invalid: aggregate in WHERE', btnLabel: '▶ Run the invalid query', doneLabel: '✓ Invalid query attempted', code: invalidCode, note: '', type: 'error', danger: true }
];
const stepsContainer = document.getElementById('smSteps');
const progress = document.getElementById('smProgress');
const outputLabel = document.getElementById('smOutputLabel');
const tableScroll = document.getElementById('smTableScroll');
const table = document.getElementById('smTable');
const emptyNote = document.getElementById('smEmptyNote');
const errorBox = document.getElementById('smErrorBox');
const status = document.getElementById('smStatus');
const fullCodeContent = document.getElementById('smFullCodeContent');
fullCodeContent.innerHTML = [setupCode, groupCode, havingCode, invalidCode].join('\n\n');
steps.forEach((s, i) => {
const stepEl = document.createElement('div');
stepEl.className = 'sm-step' + (i === 0 ? ' is-revealed' : '');
stepEl.id = 'step-' + s.key;
stepEl.innerHTML =
'<div class="sm-step-done-pill' + (s.danger ? ' is-danger-done' : '') + '"><span>' + s.doneLabel + '</span><span class="chevron">\u25be view code</span></div>' +
'<div class="sm-step-code-full">' +
'<p class="sm-step-label">' + s.title + '</p>' +
'<div class="sm-code-scroll"><div class="sm-code">' + s.code + '</div></div>' +
(s.note ? '<p class="sm-step-note">' + s.note + '</p>' : '') +
'<div class="sm-run-row"><button class="sm-run-btn' + (s.danger ? ' is-danger' : '') + '" id="btn-' + s.key + '" type="button">' + s.btnLabel + '</button></div>' +
'</div>';
stepsContainer.appendChild(stepEl);
});
function updateProgress(doneCount) {
progress.textContent = doneCount + ' / ' + steps.length + ' steps run';
}
updateProgress(0);
function renderResult(cols, rows, aggCols) {
errorBox.style.display = 'none';
if (rows.length === 0) {
table.innerHTML = '<thead><tr>' + cols.map((c, i) => '<th' + (aggCols.includes(i) ? ' class="agg-col"' : '') + '>' + c + '</th>').join('') + '</tr></thead>';
tableScroll.style.display = 'block';
emptyNote.style.display = 'block';
outputLabel.style.display = 'block';
return;
}
emptyNote.style.display = 'none';
let html = '<thead><tr>' + cols.map((c, i) => '<th' + (aggCols.includes(i) ? ' class="agg-col"' : '') + '>' + c + '</th>').join('') + '</tr></thead><tbody>';
rows.forEach(r => {
html += '<tr class="is-new">' + r.map((v, i) => '<td' + (aggCols.includes(i) ? ' class="is-aggcol"' : '') + '>' + v + '</td>').join('') + '</tr>';
});
html += '</tbody>';
table.innerHTML = html;
outputLabel.style.display = 'block';
tableScroll.style.display = 'block';
}
function renderError(text) {
outputLabel.style.display = 'none';
tableScroll.style.display = 'none';
emptyNote.style.display = 'none';
errorBox.style.display = 'block';
errorBox.textContent = text;
}
function showStatus(text, kind) {
status.textContent = text;
status.className = 'sm-status is-visible st-' + kind;
}
let doneCount = 0;
document.getElementById('btn-setup').addEventListener('click', () => {
const btn = document.getElementById('btn-setup');
btn.disabled = true; btn.textContent = 'Running...';
status.classList.remove('is-visible');
setTimeout(() => {
renderResult(['id', 'name', 'department', 'salary'], employees.map(e => [e.id, e.name, e.department, e.salary]), []);
showStatus('✔️ Table created, ' + employees.length + ' rows inserted.', 'good');
advance('setup');
}, 350);
});
document.getElementById('btn-group').addEventListener('click', () => {
const btn = document.getElementById('btn-group');
btn.disabled = true; btn.textContent = 'Running...';
status.classList.remove('is-visible');
setTimeout(() => {
const g = groupBy(employees, e => e.department);
const rows = [...g.keys()].sort().map(k => [k, g.get(k).reduce((a, e) => a + e.salary, 0)]);
renderResult(['department', 'total_salary'], rows, [1]);
showStatus('✔️ ' + rows.length + ' departments returned.', 'good');
advance('group');
}, 350);
});
document.getElementById('btn-having').addEventListener('click', () => {
const btn = document.getElementById('btn-having');
btn.disabled = true; btn.textContent = 'Running...';
status.classList.remove('is-visible');
setTimeout(() => {
const g = groupBy(employees, e => e.department);
const rows = [...g.keys()].sort().map(k => [k, g.get(k).reduce((a, e) => a + e.salary, 0)]).filter(r => r[1] > 500000);
renderResult(['department', 'total_salary'], rows, [1]);
if (rows.length === 0) {
showStatus('⚠️ 0 rows — with this exact data, no department\u2019s total exceeds \u20b95,00,000 (Engineering is highest at \u20b94,50,000). HAVING can legitimately filter a result down to nothing.', 'bad');
} else {
showStatus('✔️ ' + rows.length + ' department(s) exceeded \u20b95,00,000.', 'good');
}
advance('having');
}, 350);
});
document.getElementById('btn-invalid').addEventListener('click', () => {
const btn = document.getElementById('btn-invalid');
btn.disabled = true; btn.textContent = 'Running...';
status.classList.remove('is-visible');
setTimeout(() => {
renderError('ERROR: aggregate functions are not allowed in WHERE\nLINE 3: WHERE COUNT(*) > 3\n ^\nHINT: WHERE filters rows before grouping happens, so COUNT(*) doesn\u2019t exist yet at that point. Use HAVING to filter on an aggregate result instead.');
showStatus('❌ Query failed — this is expected. Aggregates belong in HAVING, not WHERE.', 'bad');
advance('invalid');
}, 350);
});
function advance(key) {
doneCount++;
updateProgress(doneCount);
document.getElementById('step-' + key).classList.add('is-done');
const idx = steps.findIndex(s => s.key === key);
const next = steps[idx + 1];
if (next) {
const nextEl = document.getElementById('step-' + next.key);
nextEl.classList.add('is-revealed');
nextEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
// if the next step produces an error (not a table), clear any leftover
// result from the previous step so it doesn't look like it already ran
if (next.type === 'error') {
outputLabel.style.display = 'none';
tableScroll.style.display = 'none';
emptyNote.style.display = 'none';
table.innerHTML = '';
status.classList.remove('is-visible');
}
}
}
document.getElementById('smShowFullBtn').addEventListener('click', function () {
const panel = document.getElementById('smFullCodePanel');
const on = panel.classList.toggle('is-visible');
this.classList.toggle('is-on', on);
this.textContent = on ? '👁️ Hide full SQL script' : '👁️ Show full SQL script';
});
document.querySelectorAll('.sm-step-done-pill').forEach(pill => {
pill.addEventListener('click', () => {
const step = pill.closest('.sm-step');
step.classList.toggle('is-expanded-done');
pill.querySelector('.chevron').textContent = step.classList.contains('is-expanded-done') ? '\u25b4 hide code' : '\u25be view code';
});
});
document.getElementById('smReset').addEventListener('click', () => {
doneCount = 0;
updateProgress(0);
status.classList.remove('is-visible');
outputLabel.style.display = 'none';
tableScroll.style.display = 'none';
emptyNote.style.display = 'none';
errorBox.style.display = 'none';
table.innerHTML = '';
steps.forEach((s, i) => {
const el = document.getElementById('step-' + s.key);
el.classList.remove('is-done', 'is-expanded-done');
el.classList.toggle('is-revealed', i === 0);
const btn = document.getElementById('btn-' + s.key);
btn.disabled = false;
btn.textContent = s.btnLabel;
});
});
})();
</script>
</body>
</html>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});📋 Summary
Clause | Works On | Used For |
|---|---|---|
| Individual rows | Filter before grouping |
| Groups | Filter after grouping |
| Aggregate functions | Must use in |