Key constraints are part of Data Definition Language (DDL) and are used to enforce rules on data columns to maintain accuracy, uniqueness, and relationships in a database.
🧱 PRIMARY KEY
Ensures each row has a unique, non-null identifier.
📌 Example:
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(50)
);✔️ id must be unique and not null
✔️ Only one primary key per table
✔️ Can also be composite (multiple columns)
🔗 FOREIGN KEY
Creates a relationship between two tables.
A FOREIGN KEY in one table references a PRIMARY KEY in another.
📌 Example:
CREATE TABLE enrollments (
student_id INT,
FOREIGN KEY (student_id) REFERENCES students(id)
);✔️ Ensures that student_id exists in students
🔑 UNIQUE
Ensures all values in a column are different
Unlike PRIMARY KEY, allows nulls and multiple unique keys in a table
📌 Example:
CREATE TABLE users (
email VARCHAR(100) UNIQUE
);✔️ Two users can't have the same email
🚫 NOT NULL
Ensures a column must always contain a value
📌 Example:
CREATE TABLE employees (
name VARCHAR(50) NOT NULL
);✔️ Name cannot be empty
🧮 CHECK
Used to enforce custom conditions
📌 Example:
CREATE TABLE products (
price DECIMAL(10, 2) CHECK (price > 0)
);✔️ Prevents negative prices
🔄 DEFAULT
Automatically assigns a value if none is provided
📌 Example:
CREATE TABLE orders (
status VARCHAR(20) DEFAULT 'Pending'
);✔️ If status is not entered, it will be 'Pending'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SQL Constraints — Try It Yourself</title>
<style>
.infographic-constraintsrunner {
--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-constraintsrunner[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-constraintsrunner * { box-sizing: border-box; }
.ct-topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem; gap: 0.5rem; flex-wrap: wrap; }
.ct-progress { font-size: 0.78rem; color: var(--ink-soft); }
.ct-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);
}
.ct-showfull-btn:hover { border-color: var(--accent); color: var(--accent); }
.ct-showfull-btn.is-on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.ct-fullcode-panel { display: none; margin-bottom: 1.25rem; }
.ct-fullcode-panel.is-visible { display: block; }
.ct-fullcode-scroll { overflow-x: auto; }
.ct-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-constraintsrunner[data-theme="dark"] .ct-fullcode { background: #05070a; }
.ct-frame { background: var(--paper-alt); border: 1px solid var(--line); border-radius: 10px; padding: 1.25rem; }
.ct-kw { color: #9cc5ff; } .ct-str { color: #f8d980; } .ct-num { color: #4ade80; } .ct-cm { color: #7c8aa8; }
.ct-step { display: none; margin-bottom: 1rem; }
.ct-step.is-revealed { display: block; animation: ct-step-in 0.4s ease; }
@keyframes ct-step-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
.ct-step.is-done .ct-step-code-full { display: none; }
.ct-step.is-done .ct-step-done-pill { display: flex; }
.ct-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;
}
.ct-step-done-pill .chevron { margin-left: auto; font-size: 0.7rem; opacity: 0.7; }
.ct-step.is-expanded-done .ct-step-code-full { display: block; }
.ct-step.is-expanded-done .ct-step-done-pill { background: var(--accent-soft); color: var(--accent); }
.ct-step-label { font-family: var(--display-font); font-size: 1.02rem; margin: 0 0 0.5rem; }
.ct-step-desc { font-size: 0.82rem; color: var(--ink-soft); margin: 0 0 0.7rem; line-height: 1.45; }
.ct-code-scroll { overflow-x: auto; }
.ct-code {
background: var(--ink); color: #d4e2ff; border-radius: 8px; padding: 0.8rem 1rem;
font-family: var(--mono-font); font-size: 0.78rem; line-height: 1.6; white-space: pre;
display: inline-block;
min-width: 100%;
}
.infographic-constraintsrunner[data-theme="dark"] .ct-code { background: #05070a; }
.ct-mini-table-scroll { overflow-x: auto; margin-top: 0.85rem; }
table.ct-mini-table {
width: 100%; min-width: 200px; border-collapse: collapse; background: var(--paper);
border-radius: 7px; font-size: 0.82rem;
}
table.ct-mini-table th, table.ct-mini-table td {
border: 1px solid var(--line); padding: 0.4rem 0.6rem; text-align: left; white-space: nowrap;
}
table.ct-mini-table th { background: var(--accent); color: var(--on-accent); font-weight: 700; font-size: 0.7rem; }
table.ct-mini-table tr.is-new td { animation: ct-row-in 0.5s ease; }
@keyframes ct-row-in { from { background: var(--good-soft); } to { background: transparent; } }
.ct-mini-empty { font-size: 0.78rem; color: var(--ink-soft); font-style: italic; padding: 0.4rem 0; }
.ct-run-row { display: flex; justify-content: center; gap: 0.5rem; margin-top: 0.85rem; flex-wrap: wrap; }
.ct-run-btn {
font: inherit; font-size: 0.82rem; background: var(--accent); color: var(--on-accent);
border: none; border-radius: 7px; padding: 0.5rem 0.95rem; cursor: pointer;
}
.ct-run-btn:hover { opacity: 0.9; }
.ct-run-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.ct-run-btn.is-danger { background: var(--bad); }
.ct-run-btn.ct-next-btn { background: var(--good); }
.ct-mini-msg {
margin-top: 0.7rem; padding: 0.55rem 0.75rem; border-radius: 7px; font-size: 0.8rem; line-height: 1.5; display: none;
}
.ct-mini-msg.is-visible { display: block; }
.ct-mini-msg.st-good { background: var(--good-soft); color: var(--good); }
.ct-mini-msg.st-bad { background: var(--bad-soft); color: var(--bad); }
.ct-reset-row { display: flex; justify-content: center; margin-top: 1.25rem; }
.ct-reset-btn {
font: inherit; font-size: 0.82rem; background: var(--paper); border: 1px solid var(--line);
border-radius: 7px; padding: 0.5rem 1rem; cursor: pointer; color: var(--ink);
}
.ct-reset-btn:hover { border-color: var(--accent); }
@media (prefers-reduced-motion: reduce) {
.infographic-constraintsrunner *, .infographic-constraintsrunner *::before, .infographic-constraintsrunner *::after {
transition-duration: 0.001ms !important; animation-duration: 0.001ms !important;
}
}
</style>
</head>
<body>
<div class="infographic-constraintsrunner" id="constraintsrunner">
<div class="ct-topbar">
<span class="ct-progress" id="ctProgress"></span>
<button class="ct-showfull-btn" id="ctShowFullBtn" type="button">👁️ Show all constraint SQL</button>
</div>
<div class="ct-fullcode-panel" id="ctFullCodePanel">
<div class="ct-fullcode-scroll">
<div class="ct-fullcode" id="ctFullCodeContent"></div>
</div>
</div>
<div class="ct-frame">
<div id="ctSteps"></div>
<div class="ct-reset-row">
<button class="ct-reset-btn" id="ctReset" type="button">↺ Reset all</button>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('constraintsrunner');
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 () {
function kw(s) { return '<span class="ct-kw">' + s + '</span>'; }
function str(s) { return '<span class="ct-str">' + s + '</span>'; }
function num(s) { return '<span class="ct-num">' + s + '</span>'; }
function cm(s) { return '<span class="ct-cm">' + s + '</span>'; }
const steps = [
{
key: 'pk', title: '🧱 PRIMARY KEY', desc: 'Ensures each row has a unique, non-null identifier. Only one per table (though it can be composite).',
code: kw('CREATE TABLE') + ' students (\n id ' + kw('INT PRIMARY KEY') + ',\n name ' + kw('VARCHAR') + '(50)\n);',
cols: ['id', 'name'],
actions: [
{ label: "✔️ Insert (1, 'Alice')", kind: 'good',
run: (rows) => { rows.push([1, 'Alice']); return { ok: true, msg: 'Row inserted — id 1 is new and unique.' }; } },
{ label: "❌ Insert (1, 'Bob')", kind: 'bad',
run: (rows) => { return { ok: false, msg: "Blocked — Duplicate entry '1' for key PRIMARY. id must be unique." }; } }
]
},
{
key: 'fk', title: '🔗 FOREIGN KEY', desc: 'A FOREIGN KEY in one table must reference a value that actually exists as a PRIMARY KEY in another. (Assume students with id 1 and 2 already exist.)',
code: kw('CREATE TABLE') + ' enrollments (\n student_id ' + kw('INT') + ',\n ' + kw('FOREIGN KEY') + ' (student_id) ' + kw('REFERENCES') + ' students(id)\n);',
cols: ['student_id'],
actions: [
{ label: '✔️ Insert student_id = 1', kind: 'good',
run: (rows) => { rows.push([1]); return { ok: true, msg: 'Row inserted — student 1 exists in students.' }; } },
{ label: '❌ Insert student_id = 99', kind: 'bad',
run: (rows) => { return { ok: false, msg: 'Blocked — Key (student_id)=(99) is not present in table "students".' }; } }
]
},
{
key: 'unique', title: '🔑 UNIQUE', desc: 'Every value in the column must be different. Unlike PRIMARY KEY, it allows NULLs and a table can have several UNIQUE columns.',
code: kw('CREATE TABLE') + ' users (\n email ' + kw('VARCHAR') + '(100) ' + kw('UNIQUE') + '\n);',
cols: ['email'],
actions: [
{ label: "✔️ Insert 'anya@mail.com'", kind: 'good',
run: (rows) => { rows.push(['anya@mail.com']); return { ok: true, msg: 'Row inserted — first time this email is used.' }; } },
{ label: "❌ Insert 'anya@mail.com' again", kind: 'bad',
run: (rows) => { return { ok: false, msg: "Blocked — duplicate key value violates unique constraint on email." }; } }
]
},
{
key: 'notnull', title: '🚫 NOT NULL', desc: 'This column must always contain a value — it can never be left empty.',
code: kw('CREATE TABLE') + ' employees (\n name ' + kw('VARCHAR') + '(50) ' + kw('NOT NULL') + '\n);',
cols: ['name'],
actions: [
{ label: "✔️ Insert 'John'", kind: 'good',
run: (rows) => { rows.push(['John']); return { ok: true, msg: 'Row inserted — a value was provided.' }; } },
{ label: '❌ Insert NULL', kind: 'bad',
run: (rows) => { return { ok: false, msg: 'Blocked — null value in column "name" violates not-null constraint.' }; } }
]
},
{
key: 'check', title: '🧮 CHECK', desc: 'Enforces a custom condition on every row — here, price must stay positive.',
code: kw('CREATE TABLE') + ' products (\n price ' + kw('DECIMAL') + '(10,2) ' + kw('CHECK') + ' (price > 0)\n);',
cols: ['price'],
actions: [
{ label: '✔️ Insert price = 49.99', kind: 'good',
run: (rows) => { rows.push([49.99]); return { ok: true, msg: 'Row inserted — 49.99 satisfies price > 0.' }; } },
{ label: '❌ Insert price = -10', kind: 'bad',
run: (rows) => { return { ok: false, msg: 'Blocked — new row violates check constraint "price > 0".' }; } }
]
},
{
key: 'default', title: '🔄 DEFAULT', desc: "Automatically fills in a value when none is given — it doesn't block anything, unlike the other constraints here.",
code: kw('CREATE TABLE') + ' orders (\n status ' + kw('VARCHAR') + '(20) ' + kw('DEFAULT') + ' ' + str("'Pending'") + '\n);',
cols: ['status'],
actions: [
{ label: '➕ Insert without status', kind: 'good',
run: (rows) => { rows.push(['Pending (default)']); return { ok: true, msg: 'No status given, so DEFAULT filled in "Pending" automatically.' }; } },
{ label: "➕ Insert with status = 'Shipped'", kind: 'good',
run: (rows) => { rows.push(['Shipped']); return { ok: true, msg: 'An explicit value was given, so it overrides the default.' }; } }
]
}
];
const stepsContainer = document.getElementById('ctSteps');
const progress = document.getElementById('ctProgress');
const fullCodeContent = document.getElementById('ctFullCodeContent');
fullCodeContent.innerHTML = steps.map(s => cm('-- ' + s.title) + '\n' + s.code).join('\n\n');
const stepState = {};
steps.forEach((s, i) => {
stepState[s.key] = { rows: [] };
const stepEl = document.createElement('div');
stepEl.className = 'ct-step' + (i === 0 ? ' is-revealed' : '');
stepEl.id = 'step-' + s.key;
const actionButtons = s.actions.map((a, ai) =>
'<button class="ct-run-btn' + (a.kind === 'bad' ? ' is-danger' : '') + '" data-step="' + s.key + '" data-action="' + ai + '"' + (ai > 0 ? ' disabled' : '') + '>' + a.label + '</button>'
).join('');
stepEl.innerHTML =
'<div class="ct-step-done-pill"><span>✓ ' + s.title + ' explored</span><span class="chevron">\u25be view code</span></div>' +
'<div class="ct-step-code-full">' +
'<p class="ct-step-label">' + s.title + '</p>' +
'<p class="ct-step-desc">' + s.desc + '</p>' +
'<div class="ct-code-scroll"><div class="ct-code">' + s.code + '</div></div>' +
'<div class="ct-mini-table-scroll">' +
'<table class="ct-mini-table" id="table-' + s.key + '">' +
'<thead><tr>' + s.cols.map(c => '<th>' + c + '</th>').join('') + '</tr></thead>' +
'<tbody id="tbody-' + s.key + '"></tbody>' +
'</table>' +
'</div>' +
'<div class="ct-run-row">' + actionButtons + '</div>' +
'<p class="ct-mini-msg" id="msg-' + s.key + '"></p>' +
(i < steps.length - 1 ? '<div class="ct-run-row"><button class="ct-run-btn ct-next-btn" id="nextstep-' + s.key + '" type="button" style="display:none;">Next constraint →</button></div>' : '') +
'</div>';
stepsContainer.appendChild(stepEl);
});
function updateProgress(doneCount) {
progress.textContent = doneCount + ' / ' + steps.length + ' constraints explored';
}
updateProgress(0);
function renderMiniTable(key) {
const s = steps.find(st => st.key === key);
const tbody = document.getElementById('tbody-' + key);
const rows = stepState[key].rows;
if (rows.length === 0) {
tbody.innerHTML = '';
return;
}
tbody.innerHTML = rows.map(r => '<tr class="is-new">' + r.map(v => '<td>' + v + '</td>').join('') + '</tr>').join('');
}
let doneCount = 0;
steps.forEach((s, i) => {
const total = s.actions.length;
let completedActions = 0;
s.actions.forEach((a, ai) => {
const btn = document.querySelector('.ct-run-btn[data-step="' + s.key + '"][data-action="' + ai + '"]');
btn.addEventListener('click', () => {
btn.disabled = true;
const msgEl = document.getElementById('msg-' + s.key);
const result = a.run(stepState[s.key].rows);
renderMiniTable(s.key);
msgEl.textContent = (result.ok ? '✔️ ' : '❌ ') + result.msg;
msgEl.className = 'ct-mini-msg is-visible st-' + (result.ok ? 'good' : 'bad');
completedActions++;
const nextBtn = document.querySelector('.ct-run-btn[data-step="' + s.key + '"][data-action="' + (ai + 1) + '"]');
if (nextBtn) nextBtn.disabled = false;
if (completedActions === total) {
const nextStepBtn = document.getElementById('nextstep-' + s.key);
if (nextStepBtn) nextStepBtn.style.display = 'inline-block';
}
});
});
const nextStepBtn = document.getElementById('nextstep-' + s.key);
if (nextStepBtn) {
nextStepBtn.addEventListener('click', () => {
document.getElementById('step-' + s.key).classList.add('is-done');
doneCount++;
updateProgress(doneCount);
const next = steps[i + 1];
if (next) {
const nextEl = document.getElementById('step-' + next.key);
nextEl.classList.add('is-revealed');
nextEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
});
}
});
document.getElementById('ctShowFullBtn').addEventListener('click', function () {
const panel = document.getElementById('ctFullCodePanel');
const on = panel.classList.toggle('is-visible');
this.classList.toggle('is-on', on);
this.textContent = on ? '👁️ Hide all constraint SQL' : '👁️ Show all constraint SQL';
});
document.querySelectorAll('.ct-step-done-pill').forEach(pill => {
pill.addEventListener('click', () => {
const step = pill.closest('.ct-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('ctReset').addEventListener('click', () => {
doneCount = 0;
updateProgress(0);
steps.forEach((s, i) => {
stepState[s.key].rows = [];
renderMiniTable(s.key);
const el = document.getElementById('step-' + s.key);
el.classList.remove('is-done', 'is-expanded-done');
el.classList.toggle('is-revealed', i === 0);
const msgEl = document.getElementById('msg-' + s.key);
msgEl.classList.remove('is-visible');
s.actions.forEach((a, ai) => {
const btn = document.querySelector('.ct-run-btn[data-step="' + s.key + '"][data-action="' + ai + '"]');
btn.disabled = ai > 0;
});
const nextStepBtn = document.getElementById('nextstep-' + s.key);
if (nextStepBtn) nextStepBtn.style.display = 'none';
});
});
})();
</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 Table
Constraint | Purpose |
|---|---|
🔐 | Unique + Not Null, identifies each row |
🔗 | Links to primary key in another table |
🔑 | Ensures all values in a column are distinct |
🚫 | Prevents missing/empty values |
🧮 | Validates column values with condition |
🔄 | Auto-fills column with default value |