β What is BCNF?
A table is in BCNF if:
It is in 3NF
Every determinant is a candidate key
π‘ What is a Determinant?
A determinant is an attribute (or set of attributes) on the left-hand side of a functional dependency.
For example:
If A β B, then A is the determinant.
β BCNF handles a specific issue that 3NF doesnβt always catch β When a non-candidate key becomes a determinant.
π§Ύ Example (3NF but Not in BCNF):
π« Course_Teacher Table:
Course | Teacher | Room |
|---|---|---|
DBMS | Roy | 101 |
OS | Roy | 101 |
AI | Khan | 102 |
Functional Dependencies:
Course β TeacherβTeacher β Roomβ Problem here!
Candidate Key = Course
But Teacher is not a candidate key, yet it determines Room.
β Violates BCNF (Teacher is a determinant but not a candidate key)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Boyce-Codd Normal Form (BCNF)</title>
<style>
.infographic-bcnf {
--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;
--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: 900px;
margin: 0 auto;
box-sizing: border-box;
overflow-x: hidden;
}
.infographic-bcnf[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;
}
.infographic-bcnf * { box-sizing: border-box; }
.bc-head { text-align: center; margin-bottom: 0.25rem; }
.bc-head h2 {
font-family: var(--display-font);
font-size: 1.95rem;
margin: 0 0 0.5rem;
}
.bc-head p {
color: var(--ink-soft);
font-size: 1rem;
margin: 0 auto;
max-width: 600px;
line-height: 1.55;
}
.bc-head code { background: var(--paper-alt); border-radius: 4px; padding: 0.05rem 0.35rem; }
.bc-frame {
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 10px;
padding: 1.25rem;
margin-top: 1.75rem;
}
.bc-frame-title { font-family: var(--display-font); font-size: 1.05rem; margin: 0 0 0.4rem; }
.bc-frame-desc { font-size: 0.87rem; color: var(--ink-soft); line-height: 1.5; margin: 0 0 1rem; }
.bc-key-note {
font-family: var(--mono-font);
font-size: 0.8rem;
background: var(--accent-soft);
color: var(--accent);
border-radius: 6px;
padding: 0.5rem 0.7rem;
margin-bottom: 1rem;
display: inline-block;
}
.bc-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; min-width: 0; margin-bottom: 1.25rem; }
table.bc-table {
width: 100%;
min-width: 340px;
border-collapse: collapse;
background: var(--paper);
border-radius: 8px;
font-size: 0.88rem;
}
table.bc-table th, table.bc-table td {
border: 1px solid var(--line);
padding: 0.5rem 0.7rem;
text-align: left;
white-space: nowrap;
}
table.bc-table th { background: var(--accent); color: #fff; font-weight: 700; font-size: 0.75rem; }
.bc-fd-card {
background: var(--paper);
border: 1px solid var(--line);
border-radius: 8px;
padding: 0.9rem 1rem;
margin-bottom: 0.9rem;
}
.bc-fd-text {
font-family: var(--mono-font);
font-size: 0.95rem;
margin-bottom: 0.6rem;
}
.bc-fd-question { font-size: 0.83rem; color: var(--ink-soft); margin-bottom: 0.6rem; }
.bc-fd-btns { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.bc-fd-btn {
font: inherit;
font-size: 0.82rem;
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 7px;
padding: 0.45rem 0.9rem;
cursor: pointer;
color: var(--ink);
}
.bc-fd-btn:hover { border-color: var(--accent); }
.bc-fd-btn.is-correct { background: var(--good-soft); color: var(--good); border-color: var(--good); }
.bc-fd-btn.is-wrong { background: var(--bad-soft); color: var(--bad); border-color: var(--bad); }
.bc-fd-btn:disabled { cursor: not-allowed; }
.bc-fd-feedback {
margin-top: 0.6rem;
font-size: 0.83rem;
line-height: 1.5;
padding: 0.55rem 0.75rem;
border-radius: 7px;
display: none;
}
.bc-fd-feedback.is-visible { display: block; }
.bc-fd-feedback.fb-good { background: var(--good-soft); color: var(--good); }
.bc-fd-feedback.fb-bad { background: var(--bad-soft); color: var(--bad); }
.bc-decompose-row { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-top: 1rem; }
.bc-decompose-btn {
font: inherit;
font-size: 0.88rem;
background: var(--accent);
color: #fff;
border: none;
border-radius: 7px;
padding: 0.6rem 1.2rem;
cursor: pointer;
}
.bc-decompose-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.bc-decompose-hint { font-size: 0.78rem; color: var(--ink-soft); }
.bc-result { display: none; margin-top: 1.5rem; }
.bc-result.is-visible { display: block; animation: bc-fade 0.3s ease; }
@keyframes bc-fade { from { opacity: 0; } to { opacity: 1; } }
.bc-arrow-down { text-align: center; font-size: 1.3rem; color: var(--ink-soft); margin-bottom: 1rem; }
.bc-tables-row { display: flex; gap: 1.25rem; flex-wrap: wrap; }
.bc-table-block { flex: 1 1 200px; min-width: 0; }
.bc-table-name { font-family: var(--display-font); font-size: 0.92rem; margin: 0 0 0.5rem; }
table.bc-result-table {
width: 100%;
min-width: 180px;
border-collapse: collapse;
background: var(--paper);
border-radius: 8px;
font-size: 0.85rem;
}
table.bc-result-table th, table.bc-result-table td {
border: 1px solid var(--line);
padding: 0.45rem 0.6rem;
text-align: left;
white-space: nowrap;
}
table.bc-result-table th { background: var(--good); color: #fff; font-weight: 700; font-size: 0.7rem; }
.bc-reset-btn {
font: inherit;
font-size: 0.82rem;
background: var(--paper);
border: 1px solid var(--line);
border-radius: 7px;
padding: 0.45rem 0.9rem;
cursor: pointer;
color: var(--ink);
margin-top: 1.25rem;
}
.bc-reset-btn:hover { border-color: var(--accent); }
@media (prefers-reduced-motion: reduce) {
.infographic-bcnf *, .infographic-bcnf *::before, .infographic-bcnf *::after {
transition-duration: 0.001ms !important;
animation-duration: 0.001ms !important;
}
}
</style>
</head>
<body>
<div class="infographic-bcnf" id="bcnf">
<div class="bc-head">
<p>A table is in BCNF when it's already in 3NF, and every determinant β the left side of any functional dependency β is a candidate key.</p>
</div>
<div class="bc-frame">
<p class="bc-frame-title">Test every determinant</p>
<p class="bc-frame-desc">This table has two functional dependencies. For each one, decide: is the left-hand side actually a candidate key?</p>
<div class="bc-key-note">Candidate Key = Course</div>
<div class="bc-table-scroll">
<table class="bc-table">
<thead><tr><th>Course</th><th>Teacher</th><th>Room</th></tr></thead>
<tbody>
<tr><td>DBMS</td><td>Roy</td><td>101</td></tr>
<tr><td>OS</td><td>Roy</td><td>101</td></tr>
<tr><td>AI</td><td>Khan</td><td>102</td></tr>
</tbody>
</table>
</div>
<div class="bc-fd-card" data-fd="1">
<div class="bc-fd-text">Course β Teacher</div>
<div class="bc-fd-question">Is <code>Course</code> a candidate key?</div>
<div class="bc-fd-btns">
<button class="bc-fd-btn" data-answer="yes">β
Yes, candidate key</button>
<button class="bc-fd-btn" data-answer="no">β No, not a candidate key</button>
</div>
<div class="bc-fd-feedback"></div>
</div>
<div class="bc-fd-card" data-fd="2">
<div class="bc-fd-text">Teacher β Room</div>
<div class="bc-fd-question">Is <code>Teacher</code> a candidate key?</div>
<div class="bc-fd-btns">
<button class="bc-fd-btn" data-answer="yes">β
Yes, candidate key</button>
<button class="bc-fd-btn" data-answer="no">β No, not a candidate key</button>
</div>
<div class="bc-fd-feedback"></div>
</div>
<div class="bc-decompose-row">
<button class="bc-decompose-btn" id="bcDecomposeBtn" disabled type="button">Decompose into BCNF</button>
<span class="bc-decompose-hint" id="bcHint">Answer both correctly to unlock this.</span>
</div>
<div class="bc-result" id="bcResult">
<div class="bc-arrow-down">β</div>
<div class="bc-tables-row">
<div class="bc-table-block">
<p class="bc-table-name">Course_Teacher</p>
<table class="bc-result-table">
<thead><tr><th>Course</th><th>Teacher</th></tr></thead>
<tbody>
<tr><td>DBMS</td><td>Roy</td></tr>
<tr><td>OS</td><td>Roy</td></tr>
<tr><td>AI</td><td>Khan</td></tr>
</tbody>
</table>
</div>
<div class="bc-table-block">
<p class="bc-table-name">Teacher_Room</p>
<table class="bc-result-table">
<thead><tr><th>Teacher</th><th>Room</th></tr></thead>
<tbody>
<tr><td>Roy</td><td>101</td></tr>
<tr><td>Khan</td><td>102</td></tr>
</tbody>
</table>
</div>
</div>
<div class="bc-fd-feedback fb-good is-visible" style="margin-top:1rem;">β
Now Course is the determinant in Course_Teacher, and Teacher is the determinant in Teacher_Room β both are candidate keys of their own table. This is BCNF.</div>
</div>
<button class="bc-reset-btn" id="bcReset" type="button">Reset exercise</button>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('bcnf');
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 correctAnswers = { '1': 'yes', '2': 'no' };
const explanations = {
'1': {
good: 'βοΈ Correct β Course alone can uniquely identify a row, so it\u2019s a candidate key. This dependency is fine.',
},
'2': {
good: 'βοΈ Correct β Teacher determines Room, but Teacher can\u2019t uniquely identify a Course row on its own. A non-candidate-key determinant β this violates BCNF.',
}
};
let solved = { '1': false, '2': false };
const decomposeBtn = document.getElementById('bcDecomposeBtn');
const hint = document.getElementById('bcHint');
const result = document.getElementById('bcResult');
function checkUnlock() {
const done = solved['1'] && solved['2'];
decomposeBtn.disabled = !done;
hint.textContent = done ? 'Both confirmed! Ready to decompose.' : 'Answer both correctly to unlock this.';
}
document.querySelectorAll('.bc-fd-card').forEach(card => {
const fdId = card.dataset.fd;
const feedback = card.querySelector('.bc-fd-feedback');
const btns = card.querySelectorAll('.bc-fd-btn');
btns.forEach(btn => {
btn.addEventListener('click', () => {
if (solved[fdId]) return;
const chosen = btn.dataset.answer;
const correct = chosen === correctAnswers[fdId];
if (correct) {
btn.classList.add('is-correct');
btns.forEach(b => b.disabled = true);
feedback.textContent = explanations[fdId].good;
feedback.className = 'bc-fd-feedback is-visible fb-good';
solved[fdId] = true;
checkUnlock();
} else {
btn.classList.add('is-wrong');
feedback.textContent = fdId === '1'
? 'β Think about it: does Course alone uniquely identify a row here? Try again.'
: 'β Could two different courses share the same Teacher? If so, Teacher alone can\u2019t be a candidate key. Try again.';
feedback.className = 'bc-fd-feedback is-visible fb-bad';
setTimeout(() => btn.classList.remove('is-wrong'), 900);
}
});
});
});
decomposeBtn.addEventListener('click', () => {
result.classList.add('is-visible');
result.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
});
document.getElementById('bcReset').addEventListener('click', () => {
solved = { '1': false, '2': false };
document.querySelectorAll('.bc-fd-card').forEach(card => {
card.querySelectorAll('.bc-fd-btn').forEach(b => {
b.disabled = false;
b.classList.remove('is-correct', 'is-wrong');
});
card.querySelector('.bc-fd-feedback').className = 'bc-fd-feedback';
});
checkUnlock();
result.classList.remove('is-visible');
});
})();
</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!');
});π§ Key Concepts Recap:
Concept | In BCNF? |
|---|---|
1NF + Atomic values | β Yes |
2NF + No partial dependencies | β Yes |
3NF + No transitive dependencies | β Yes |
Every determinant is a candidate key | β Yes |
π οΈ Why Use BCNF?
Fixes edge cases that 3NF misses
Ensures perfectly non-redundant design
Avoids data anomalies caused by hidden dependencies