π Relational Keys β Notes
1οΈβ£ Super Key
A super key is any combination of attributes that can uniquely identify a row β it's allowed to have extra, unnecessary attributes.
β Unique β Can include extra columns π« Not necessarily minimal
Example Table: Student
ID | Name | |
|---|---|---|
101 | Alice | |
102 | Bob |
Valid Super Keys:
{ID}
{ID, Name}
{ID, Email}
{ID, Name, Email}
{Email}2οΈβ£ Candidate Key
A candidate key is a minimal super key β no extra, unnecessary attributes.
β Unique β Minimal β‘οΈ Every candidate key is a super key, but not every super key is a candidate key
From the table above, the Candidate Keys are:
{ID}
{Email}(Both uniquely identify a student, and neither has extra baggage.)
3οΈβ£ Primary Key
The one candidate key chosen to be a table's main identifier.
β Unique β Not NULL π Only one per table
Example:
CREATE TABLE Student (
ID INT PRIMARY KEY,
Name VARCHAR(50),
Email VARCHAR(100)
);β {ID} becomes the Primary Key.
4οΈβ£ Alternate Key
A candidate key that wasn't chosen as the primary key.
β Still unique β Still minimal π Just not the main one
Example:
{ID} β Primary Key
{Email} β Alternate KeyOften enforced with:
ALTER TABLE Student ADD CONSTRAINT uq_email UNIQUE (Email);5οΈβ£ Foreign Key
An attribute in one table that references the primary key of another table β this is what actually links tables together.
π Creates relationships π« Doesn't need to be unique in its own table π‘οΈ Enforces referential integrity
Example:
CREATE TABLE Enrolls (
Student_ID INT,
Course_ID INT,
FOREIGN KEY (Student_ID) REFERENCES Student(ID),
FOREIGN KEY (Course_ID) REFERENCES Course(ID)
);Student_ID | Course_ID |
|---|---|
S001 | C101 |
S001 | C102 |
S002 | C103 |
π S001 can repeat β a foreign key allows duplicates.
6οΈβ£ Composite Key
A key made of two or more attributes, where no single one is enough alone, but together they're unique.
π§© Multiple columns β Unique only in combination π Describes the shape of a key, not a new category
Example: In the Enrolls table, no single column is unique on its own (Student_ID repeats, Course_ID repeats) β but together:
{Student_ID, Course_ID}is unique per enrollment record. This composite pair is the Primary Key of Enrolls.
7οΈβ£ Compound Key
A composite key where each individual attribute is also a foreign key referencing another table.
π§© Multi-attribute (like composite) π Each part is a foreign key π€ Often used interchangeably with "composite key"
Example:
{Student_ID, Course_ID}in Enrolls is:
Composite β because it's made of 2+ attributes
Compound β because
Student_IDandCourse_IDare each foreign keys pointing back toStudentandCourse
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Relational Keys β Complete Guide</title>
<style>
.infographic-allkeys {
--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;
--gold: #b45309;
--gold-soft: #fef3c7;
--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: 860px;
margin: 0 auto;
box-sizing: border-box;
overflow-x: hidden;
}
.infographic-allkeys[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;
--gold: #fbbf24;
--gold-soft: #3a2f0e;
--on-accent: #101418;
}
.infographic-allkeys * { box-sizing: border-box; }
.ak-head { text-align: center; margin-bottom: 0.25rem; }
.ak-head h2 { font-family: var(--display-font); font-size: 1.85rem; margin: 0 0 0.5rem; }
.ak-head p { color: var(--ink-soft); font-size: 0.95rem; margin: 0 auto; max-width: 620px; line-height: 1.55; }
.ak-tabs { display: flex; gap: 0.45rem; margin-top: 1.5rem; flex-wrap: wrap; justify-content: center; }
.ak-tab-btn {
font: inherit; font-size: 0.8rem; background: var(--paper-alt); color: var(--ink-soft);
border: 1px solid var(--line); border-radius: 999px; padding: 0.45rem 0.85rem; cursor: pointer;
}
.ak-tab-btn.is-active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.ak-panel { display: none; background: var(--paper-alt); border: 1px solid var(--line); border-radius: 10px; padding: 1.25rem; margin-top: 1rem; }
.ak-panel.is-active { display: block; animation: ak-fade-in 0.35s ease; }
@keyframes ak-fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
.ak-panel-title { font-family: var(--display-font); font-size: 1.15rem; margin: 0 0 0.4rem; }
.ak-panel-desc { font-size: 0.85rem; color: var(--ink-soft); line-height: 1.5; margin: 0 0 0.9rem; }
.ak-chip-row { display: flex; gap: 0.5rem; flex-wrap: wrap; margin: 0 0 1rem; }
.ak-chip { font-size: 0.72rem; background: var(--paper); border: 1px solid var(--line); border-radius: 999px; padding: 0.25rem 0.6rem; color: var(--ink-soft); }
/* checkbox key tester (tab 1) */
.ak-checkbox-row { display: flex; gap: 1.2rem; justify-content: center; flex-wrap: wrap; margin: 1rem 0; }
.ak-checkbox-item { display: flex; align-items: center; gap: 0.4rem; font-size: 0.9rem; cursor: pointer; }
.ak-checkbox-item input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
.ak-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; min-width: 0; }
table.ak-table { width: 100%; min-width: 280px; border-collapse: collapse; background: var(--paper); border-radius: 8px; font-size: 0.87rem; }
table.ak-table th, table.ak-table td { border: 1px solid var(--line); padding: 0.5rem 0.7rem; text-align: left; white-space: nowrap; transition: background 0.3s, color 0.3s, opacity 0.3s; }
table.ak-table th { background: var(--accent); color: var(--on-accent); font-weight: 700; font-size: 0.75rem; }
table.ak-table th.is-selected, table.ak-table td.is-selected { background: var(--accent-soft); }
table.ak-table tr.is-new td { animation: ak-row-in 0.6s ease; }
@keyframes ak-row-in { from { background: var(--good-soft); } to { background: transparent; } }
.ak-verdict { margin-top: 1rem; padding: 0.7rem 0.9rem; border-radius: 8px; font-size: 0.85rem; line-height: 1.5; }
.ak-verdict.st-none { background: var(--bad-soft); color: var(--bad); }
.ak-verdict.st-super { background: var(--accent-soft); color: var(--accent); }
.ak-verdict.st-candidate { background: var(--good-soft); color: var(--good); }
.ak-badge-row { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 1.2rem; }
.ak-badge { flex: 1 1 130px; text-align: center; padding: 0.7rem 0.5rem; border-radius: 8px; font-size: 0.78rem; background: var(--paper); border: 1px solid var(--line); }
.ak-badge.is-primary { background: var(--gold-soft); border-color: var(--gold); color: var(--gold); }
.ak-badge.is-alternate { background: var(--good-soft); border-color: var(--good); color: var(--good); }
.ak-badge b { display: block; font-family: var(--mono-font); font-size: 0.9rem; margin-bottom: 0.15rem; }
/* relationship diagram */
.ak-diagram-scroll { overflow-x: auto; }
.ak-diagram-scroll svg { width: 100%; height: auto; display: block; min-width: 420px; }
.ak-box { fill: var(--paper); stroke: var(--accent); stroke-width: 2; }
.ak-box.is-assoc { fill: var(--accent-soft); }
.ak-title-text { font-size: 12px; font-weight: 700; font-family: var(--body-font); text-anchor: middle; fill: var(--ink); }
.ak-attr-text { font-size: 10px; font-family: var(--mono-font); text-anchor: middle; fill: var(--ink-soft); }
.ak-pk-text { fill: var(--gold); font-weight: 700; }
.ak-fk-text { fill: var(--accent); font-weight: 700; }
.ak-edge { stroke: var(--ink-soft); stroke-width: 1.5; fill: none; }
/* run buttons + msg (shared) */
.ak-run-row { display: flex; justify-content: center; gap: 0.5rem; margin-top: 1rem; flex-wrap: wrap; }
.ak-run-btn { font: inherit; font-size: 0.82rem; background: var(--accent); color: var(--on-accent); border: none; border-radius: 7px; padding: 0.55rem 1rem; cursor: pointer; }
.ak-run-btn:hover { opacity: 0.9; }
.ak-run-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.ak-run-btn.is-danger { background: var(--bad); }
.ak-run-btn.is-secondary { background: var(--paper); color: var(--ink); border: 1px solid var(--line); }
.ak-msg { margin-top: 0.85rem; padding: 0.6rem 0.85rem; border-radius: 8px; font-size: 0.83rem; line-height: 1.5; display: none; }
.ak-msg.is-visible { display: block; }
.ak-msg.st-good { background: var(--good-soft); color: var(--good); }
.ak-msg.st-bad { background: var(--bad-soft); color: var(--bad); }
@media (prefers-reduced-motion: reduce) {
.infographic-allkeys *, .infographic-allkeys *::before, .infographic-allkeys *::after {
transition-duration: 0.001ms !important; animation-duration: 0.001ms !important;
}
}
</style>
</head>
<body>
<div class="infographic-allkeys" id="allkeys">
<div class="ak-head">
<h2>π Relational Keys β Complete Guide</h2>
<p>Seven related ideas, one running example: Student, Course, and the Enrolls table that connects them.</p>
</div>
<div class="ak-tabs">
<button class="ak-tab-btn is-active" data-tab="basic" type="button">πππ
π Super / Candidate / Primary / Alternate</button>
<button class="ak-tab-btn" data-tab="fk" type="button">π Foreign Key</button>
<button class="ak-tab-btn" data-tab="composite" type="button">π§© Composite Key</button>
<button class="ak-tab-btn" data-tab="compound" type="button">π€ Compound Key</button>
</div>
<!-- TAB 1: basic key hierarchy -->
<div class="ak-panel is-active" id="panel-basic">
<p class="ak-panel-title">Super Key β Candidate Key β Primary / Alternate Key</p>
<p class="ak-panel-desc">Table: Student (ID, Name, Email). Tick any combination of attributes and see which kind of key it forms.</p>
<div class="ak-checkbox-row">
<label class="ak-checkbox-item"><input type="checkbox" id="ckID" checked> ID</label>
<label class="ak-checkbox-item"><input type="checkbox" id="ckName"> Name</label>
<label class="ak-checkbox-item"><input type="checkbox" id="ckEmail"> Email</label>
</div>
<div class="ak-table-scroll">
<table class="ak-table">
<thead><tr><th id="thID">ID</th><th id="thName">Name</th><th id="thEmail">Email</th></tr></thead>
<tbody>
<tr><td id="r1ID">101</td><td id="r1Name">Alice</td><td id="r1Email">alice@gmail.com</td></tr>
<tr><td id="r2ID">102</td><td id="r2Name">Bob</td><td id="r2Email">bob@gmail.com</td></tr>
</tbody>
</table>
</div>
<p class="ak-verdict" id="basicVerdict"></p>
<div class="ak-badge-row">
<div class="ak-badge is-primary"><b>{ ID }</b>Primary Key</div>
<div class="ak-badge is-alternate"><b>{ Email }</b>Alternate Key</div>
</div>
</div>
<!-- TAB 2: Foreign Key -->
<div class="ak-panel" id="panel-fk">
<p class="ak-panel-title">Foreign Key</p>
<p class="ak-panel-desc">Enrolls.Student_ID must reference an existing Student.ID. Unlike Primary Key, a foreign key value is allowed to repeat.</p>
<div class="ak-chip-row">
<span class="ak-chip">π Creates relationships</span>
<span class="ak-chip">π« Doesn't need to be unique</span>
</div>
<div class="ak-table-scroll">
<table class="ak-table" id="fkTable">
<thead><tr><th>Student_ID</th><th>Course_ID</th></tr></thead>
<tbody>
<tr data-row="1"><td>S001</td><td>C101</td></tr>
<tr data-row="2"><td>S001</td><td>C102</td></tr>
<tr data-row="3"><td>S002</td><td>C103</td></tr>
</tbody>
</table>
</div>
<p style="font-size:0.75rem; color:var(--ink-soft); margin-top:0.5rem;">Known Student IDs: S001, S002, S003</p>
<div class="ak-run-row">
<button class="ak-run-btn" id="fkValidBtn" type="button">βοΈ Insert (S003, C101)</button>
<button class="ak-run-btn is-danger" id="fkInvalidBtn" type="button">β Insert (S099, C101)</button>
<button class="ak-run-btn is-secondary" id="fkReset" type="button">βΊ Reset</button>
</div>
<p class="ak-msg" id="fkMsg"></p>
</div>
<!-- TAB 3: Composite Key -->
<div class="ak-panel" id="panel-composite">
<p class="ak-panel-title">Composite Key</p>
<p class="ak-panel-desc">No single column here is unique on its own β Student_ID repeats, Course_ID repeats β but the pair {Student_ID, Course_ID} together is.</p>
<div class="ak-chip-row">
<span class="ak-chip">π§© Multiple columns</span>
<span class="ak-chip">β
Unique only in combination</span>
</div>
<div class="ak-table-scroll">
<table class="ak-table" id="compTable">
<thead><tr><th>Student_ID</th><th>Course_ID</th></tr></thead>
<tbody>
<tr data-row="1"><td>S001</td><td>C101</td></tr>
<tr data-row="2"><td>S001</td><td>C102</td></tr>
<tr data-row="3"><td>S002</td><td>C103</td></tr>
</tbody>
</table>
</div>
<div class="ak-run-row">
<button class="ak-run-btn" id="compValidBtn" type="button">βοΈ Insert (S001, C103)</button>
<button class="ak-run-btn is-danger" id="compInvalidBtn" type="button">β Insert (S001, C101) again</button>
<button class="ak-run-btn is-secondary" id="compReset" type="button">βΊ Reset</button>
</div>
<p class="ak-msg" id="compMsg"></p>
</div>
<!-- TAB 4: Compound Key -->
<div class="ak-panel" id="panel-compound">
<p class="ak-panel-title">Compound Key</p>
<p class="ak-panel-desc">{Student_ID, Course_ID} is composite (multiple attributes) AND compound (each attribute is itself a foreign key). Both rules apply to every insert at once.</p>
<div class="ak-diagram-scroll">
<svg viewBox="0 0 460 190">
<rect class="ak-box" x="10" y="20" width="130" height="70" rx="8"></rect>
<text class="ak-title-text" x="75" y="40">Student</text>
<text class="ak-attr-text ak-pk-text" x="75" y="58">Student_ID (PK)</text>
<text class="ak-attr-text" x="75" y="74">Name</text>
<rect class="ak-box" x="320" y="20" width="130" height="70" rx="8"></rect>
<text class="ak-title-text" x="385" y="40">Course</text>
<text class="ak-attr-text ak-pk-text" x="385" y="58">Course_ID (PK)</text>
<text class="ak-attr-text" x="385" y="74">Title</text>
<rect class="ak-box is-assoc" x="150" y="110" width="160" height="65" rx="8"></rect>
<text class="ak-title-text" x="230" y="130">Enrolls</text>
<text class="ak-attr-text ak-fk-text" x="230" y="147">Student_ID (PK, FK)</text>
<text class="ak-attr-text ak-fk-text" x="230" y="162">Course_ID (PK, FK)</text>
<path class="ak-edge" d="M100,90 L200,110"></path>
<path class="ak-edge" d="M360,90 L260,110"></path>
</svg>
</div>
<div class="ak-table-scroll">
<table class="ak-table" id="cmpdTable">
<thead><tr><th>Student_ID</th><th>Course_ID</th></tr></thead>
<tbody>
<tr data-row="1"><td>S001</td><td>C101</td></tr>
<tr data-row="2"><td>S001</td><td>C102</td></tr>
<tr data-row="3"><td>S002</td><td>C103</td></tr>
</tbody>
</table>
</div>
<div class="ak-run-row">
<button class="ak-run-btn" id="cmpdValidBtn" type="button">βοΈ Insert (S003, C102)</button>
<button class="ak-run-btn is-danger" id="cmpdFkBtn" type="button">β Insert (S099, C101) β bad FK</button>
<button class="ak-run-btn is-danger" id="cmpdDupBtn" type="button">β Insert (S001, C101) β dup key</button>
<button class="ak-run-btn is-secondary" id="cmpdReset" type="button">βΊ Reset</button>
</div>
<p class="ak-msg" id="cmpdMsg"></p>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('allkeys');
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');
})();
/* tabs */
(function () {
document.querySelectorAll('.ak-tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.ak-tab-btn').forEach(b => b.classList.remove('is-active'));
document.querySelectorAll('.ak-panel').forEach(p => p.classList.remove('is-active'));
btn.classList.add('is-active');
document.getElementById('panel-' + btn.dataset.tab).classList.add('is-active');
});
});
})();
/* TAB 1: Super/Candidate/Primary/Alternate tester */
(function () {
const rows = [
{ ID: '101', Name: 'Alice', Email: 'alice@gmail.com' },
{ ID: '102', Name: 'Bob', Email: 'bob@gmail.com' }
];
const cols = ['ID', 'Name', 'Email'];
const checks = { ID: document.getElementById('ckID'), Name: document.getElementById('ckName'), Email: document.getElementById('ckEmail') };
const verdict = document.getElementById('basicVerdict');
function selectedCols() { return cols.filter(c => checks[c].checked); }
function isUnique(selected) {
const seen = new Set();
for (const r of rows) {
const key = selected.map(c => r[c]).join('||');
if (seen.has(key)) return false;
seen.add(key);
}
return true;
}
function isMinimal(selected) {
if (!isUnique(selected)) return false;
for (const c of selected) {
const without = selected.filter(x => x !== c);
if (without.length === 0) continue;
if (isUnique(without)) return false;
}
return true;
}
function render() {
const selected = selectedCols();
cols.forEach(c => {
const isSel = selected.includes(c);
document.getElementById('th' + c).classList.toggle('is-selected', isSel);
document.getElementById('r1' + c).classList.toggle('is-selected', isSel);
document.getElementById('r2' + c).classList.toggle('is-selected', isSel);
});
if (selected.length === 0) {
verdict.textContent = 'Select at least one attribute.';
verdict.className = 'ak-verdict st-none';
return;
}
const unique = isUnique(selected);
const label = '{ ' + selected.join(', ') + ' }';
if (!unique) {
verdict.textContent = 'β ' + label + ' is NOT a key β it doesn\u2019t uniquely identify every row.';
verdict.className = 'ak-verdict st-none';
return;
}
if (isMinimal(selected)) {
verdict.textContent = 'π ' + label + ' is a Candidate Key β unique, and every attribute is necessary.';
verdict.className = 'ak-verdict st-candidate';
} else {
verdict.textContent = 'π ' + label + ' is a Super Key, but not minimal β it has an extra attribute you could drop.';
verdict.className = 'ak-verdict st-super';
}
}
Object.values(checks).forEach(cb => cb.addEventListener('change', render));
render();
})();
/* shared helpers for tabs 2-4 */
function akShowMsg(id, text, kind) {
const el = document.getElementById(id);
el.textContent = text;
el.className = 'ak-msg is-visible st-' + kind;
}
function akAddRow(tableId, sid, cid) {
const tbody = document.querySelector('#' + tableId + ' tbody');
const tr = document.createElement('tr');
tr.className = 'is-new';
tr.dataset.sid = sid; tr.dataset.cid = cid;
tr.innerHTML = '<td>' + sid + '</td><td>' + cid + '</td>';
tbody.appendChild(tr);
}
/* TAB 2: Foreign Key */
(function () {
document.getElementById('fkValidBtn').addEventListener('click', function () {
this.disabled = true;
akAddRow('fkTable', 'S003', 'C101');
akShowMsg('fkMsg', 'βοΈ S003 exists in Student, so the foreign key check passes β row inserted.', 'good');
});
document.getElementById('fkInvalidBtn').addEventListener('click', function () {
this.disabled = true;
akShowMsg('fkMsg', 'β Blocked β S099 does not exist in Student. A foreign key value must reference something real.', 'bad');
});
document.getElementById('fkReset').addEventListener('click', () => {
document.querySelector('#fkTable tbody').innerHTML =
'<tr data-row="1"><td>S001</td><td>C101</td></tr><tr data-row="2"><td>S001</td><td>C102</td></tr><tr data-row="3"><td>S002</td><td>C103</td></tr>';
document.getElementById('fkValidBtn').disabled = false;
document.getElementById('fkInvalidBtn').disabled = false;
document.getElementById('fkMsg').classList.remove('is-visible');
});
})();
/* TAB 3: Composite Key */
(function () {
document.getElementById('compValidBtn').addEventListener('click', function () {
this.disabled = true;
akAddRow('compTable', 'S001', 'C103');
akShowMsg('compMsg', 'βοΈ S001 already appears in the table, but (S001, C103) as a pair is new β the composite key stays unique, so this succeeds.', 'good');
});
document.getElementById('compInvalidBtn').addEventListener('click', function () {
this.disabled = true;
akShowMsg('compMsg', 'β Blocked β (S001, C101) already exists. The composite key {Student_ID, Course_ID} must be unique as a pair.', 'bad');
});
document.getElementById('compReset').addEventListener('click', () => {
document.querySelector('#compTable tbody').innerHTML =
'<tr data-row="1"><td>S001</td><td>C101</td></tr><tr data-row="2"><td>S001</td><td>C102</td></tr><tr data-row="3"><td>S002</td><td>C103</td></tr>';
document.getElementById('compValidBtn').disabled = false;
document.getElementById('compInvalidBtn').disabled = false;
document.getElementById('compMsg').classList.remove('is-visible');
});
})();
/* TAB 4: Compound Key (composite + FK both enforced) */
(function () {
document.getElementById('cmpdValidBtn').addEventListener('click', function () {
this.disabled = true;
akAddRow('cmpdTable', 'S003', 'C102');
akShowMsg('cmpdMsg', 'βοΈ Both checks pass β S003 and C102 both exist (FK check), and (S003, C102) hasn\u2019t been used before (composite check).', 'good');
});
document.getElementById('cmpdFkBtn').addEventListener('click', function () {
this.disabled = true;
akShowMsg('cmpdMsg', 'β Blocked by the FK rule β S099 isn\u2019t a real Student_ID, so this fails before the composite uniqueness is even checked.', 'bad');
});
document.getElementById('cmpdDupBtn').addEventListener('click', function () {
this.disabled = true;
akShowMsg('cmpdMsg', 'β Blocked by the composite key rule β S001 and C101 both individually exist (FK is fine), but this exact pair is already in the table.', 'bad');
});
document.getElementById('cmpdReset').addEventListener('click', () => {
document.querySelector('#cmpdTable tbody').innerHTML =
'<tr data-row="1"><td>S001</td><td>C101</td></tr><tr data-row="2"><td>S001</td><td>C102</td></tr><tr data-row="3"><td>S002</td><td>C103</td></tr>';
document.getElementById('cmpdValidBtn').disabled = false;
document.getElementById('cmpdFkBtn').disabled = false;
document.getElementById('cmpdDupBtn').disabled = false;
document.getElementById('cmpdMsg').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!');
});π Quick Summary Table
Key | Unique? | Minimal? | Purpose |
|---|---|---|---|
π Super Key | β | β maybe not | Any unique identifier |
π Candidate Key | β | β | Minimal unique identifier |
π Primary Key | β | β | The chosen main identifier |
π Alternate Key | β | β | Unused candidate key |
π Foreign Key | β | β | Links to another table's PK |
π§© Composite Key | β (combined) | β | Multi-attribute unique key |
π€ Compound Key | β (combined) | β | Composite key made of FKs |