β
Yes, a foreign key column can have NULL values, unless it is explicitly restricted by a NOT NULL constraint.
π Why?
A foreign key means that the value must match a primary key in another table, or it can be left empty (NULL) if:
The relationship is optional
The data is not yet known
The design allows it
π§Ύ Example:
π§© Department Table (Parent)
DeptID (PK) | DeptName |
|---|---|
10 | Computer Science |
20 | Mathematics |
π©βπ Student Table (Child)
StudentID (PK) | Name | DeptID (FK) |
|---|---|---|
101 | Alice | 10 |
102 | Bob | 20 |
103 | Zara | NULL β |
β
Zaraβs DeptID is NULL β which is valid because:
It does not violate referential integrity.
It just means Zara is not currently assigned to any department.
π§ͺ SQL Example:
CREATE TABLE Department (
DeptID INT PRIMARY KEY,
DeptName VARCHAR(50)
);
CREATE TABLE Student (
StudentID INT PRIMARY KEY,
Name VARCHAR(50),
DeptID INT, -- Nullable foreign key
FOREIGN KEY (DeptID) REFERENCES Department(DeptID)
);<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nullable Foreign Keys β Try It Yourself</title>
<style>
.infographic-nullablefk {
--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: 980px;
margin: 0 auto;
box-sizing: border-box;
overflow-x: hidden;
}
.infographic-nullablefk[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-nullablefk * { box-sizing: border-box; }
.nf-head { text-align: center; margin-bottom: 0.25rem; }
.nf-head h2 {
font-family: var(--display-font);
font-size: 1.9rem;
margin: 0 0 0.5rem;
}
.nf-head p {
color: var(--ink-soft);
font-size: 1rem;
margin: 0 auto;
max-width: 620px;
line-height: 1.55;
}
.nf-head code { background: var(--paper-alt); border-radius: 4px; padding: 0.05rem 0.35rem; }
.nf-frames {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin-top: 2rem;
min-width: 0;
}
@media (max-width: 720px) { .nf-frames { grid-template-columns: 1fr; } }
.nf-frame {
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 10px;
padding: 1.1rem;
min-width: 0;
}
.nf-frame-title {
font-family: var(--display-font);
font-size: 1.02rem;
margin: 0 0 0.75rem;
}
.nf-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; min-width: 0; }
table.nf-table {
width: 100%;
min-width: 260px;
border-collapse: collapse;
background: var(--paper);
border-radius: 8px;
font-size: 0.85rem;
}
table.nf-table th, table.nf-table td {
border: 1px solid var(--line);
padding: 0.5rem 0.6rem;
text-align: left;
white-space: nowrap;
}
table.nf-table th { background: var(--accent); color: #fff; font-weight: 700; font-size: 0.75rem; }
table.nf-table td.nf-fk-valid { color: var(--good); font-weight: 600; }
table.nf-table td.nf-fk-null { color: var(--good); font-weight: 600; font-style: italic; }
table.nf-table td.nf-fk-invalid { color: var(--bad); font-weight: 600; }
table.nf-table tbody tr.is-clickable { cursor: pointer; }
table.nf-table tbody tr.is-linked td { background: var(--accent-soft); }
table.nf-table tbody tr.is-broken td { background: var(--bad-soft); }
table.nf-table tr.is-new td { animation: nf-row-in 0.5s ease; }
@keyframes nf-row-in { from { background: var(--good-soft); } to { background: transparent; } }
.nf-fix-row td { background: var(--paper) !important; padding: 0.6rem !important; }
.nf-fix-note { font-size: 0.76rem; color: var(--ink-soft); margin-bottom: 0.4rem; }
.nf-fix-btns { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.nf-fix-btn {
font: inherit;
font-size: 0.76rem;
background: var(--accent);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.35rem 0.7rem;
cursor: pointer;
}
.nf-fix-btn.nf-fix-null { background: var(--good); }
.nf-fix-btn:hover { opacity: 0.9; }
.nf-mini-form { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.85rem; }
.nf-mini-form input {
flex: 1 1 70px;
min-width: 0;
font: inherit;
font-size: 0.82rem;
padding: 0.4rem 0.5rem;
border: 1px solid var(--line);
border-radius: 6px;
background: var(--paper);
color: var(--ink);
}
.nf-mini-form input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.nf-add-btn {
font: inherit;
font-size: 0.82rem;
background: var(--accent);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.45rem 0.9rem;
cursor: pointer;
flex-shrink: 0;
}
.nf-add-btn:hover { opacity: 0.9; }
.nf-field-hint { font-size: 0.68rem; color: var(--ink-soft); width: 100%; margin-top: -0.3rem; }
.nf-msg {
margin-top: 1.25rem;
padding: 0.7rem 0.9rem;
border-radius: 8px;
font-size: 0.85rem;
line-height: 1.55;
display: none;
}
.nf-msg.is-visible { display: block; }
.nf-msg.msg-bad { background: var(--bad-soft); color: var(--bad); }
.nf-msg.msg-good { background: var(--good-soft); color: var(--good); }
@media (prefers-reduced-motion: reduce) {
.infographic-nullablefk *, .infographic-nullablefk *::before, .infographic-nullablefk *::after {
transition-duration: 0.001ms !important;
animation-duration: 0.001ms !important;
}
}
</style>
</head>
<body>
<div class="infographic-nullablefk" id="nullablefk">
<div class="nf-head">
<h2>Nullable Foreign Keys β Try It Yourself</h2>
</div>
<div class="nf-frames">
<div class="nf-frame">
<p class="nf-frame-title">π§© Department (Parent)</p>
<div class="nf-table-scroll">
<table class="nf-table" id="nfDeptTable"></table>
</div>
<div class="nf-mini-form">
<input id="nfDeptId" type="text" placeholder="DeptID">
<input id="nfDeptName" type="text" placeholder="DeptName">
<button class="nf-add-btn" id="nfAddDept" type="button">Add</button>
</div>
</div>
<div class="nf-frame">
<p class="nf-frame-title">π Student (Child)</p>
<div class="nf-table-scroll">
<table class="nf-table" id="nfStuTable"></table>
</div>
<div class="nf-mini-form">
<input id="nfStuId" type="text" placeholder="StudentID">
<input id="nfStuName" type="text" placeholder="Name">
<input id="nfStuDept" type="text" placeholder="DeptID (optional)">
<button class="nf-add-btn" id="nfAddStu" type="button">Add</button>
<span class="nf-field-hint">Leave DeptID blank for NULL β that's allowed here.</span>
</div>
</div>
</div>
<div class="nf-msg" id="nfMsg"></div>
</div>
<script>
(function () {
const wrapper = document.getElementById('nullablefk');
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 () {
let departments = [
{ id: '10', name: 'Computer Science' },
{ id: '20', name: 'Mathematics' }
];
let students = [
{ id: '101', name: 'Alice', deptId: '10' },
{ id: '102', name: 'Bob', deptId: '20' },
{ id: '103', name: 'Zara', deptId: '' }
];
let lastNewDept = -1;
let lastNewStudent = -1;
let selectedStudent = null;
let showFixFor = null;
const deptTable = document.getElementById('nfDeptTable');
const stuTable = document.getElementById('nfStuTable');
const msg = document.getElementById('nfMsg');
function showMsg(text, type) {
msg.textContent = text;
msg.className = 'nf-msg is-visible msg-' + type;
msg.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
function deptExists(id) { return departments.some(d => d.id === id); }
function renderDept() {
let html = '<thead><tr><th>DeptID (PK)</th><th>DeptName</th></tr></thead><tbody>';
departments.forEach((d, i) => {
const linked = selectedStudent && selectedStudent.deptId === d.id;
html += '<tr' + (linked ? ' class="is-linked"' : '') + (i === lastNewDept ? ' class="is-new"' : '') + '>' +
'<td>' + d.id + '</td><td>' + d.name + '</td></tr>';
});
html += '</tbody>';
deptTable.innerHTML = html;
}
function renderStudent() {
let html = '<thead><tr><th>StudentID (PK)</th><th>Name</th><th>DeptID (FK)</th></tr></thead><tbody>';
students.forEach((s, i) => {
const isNull = s.deptId === '';
const valid = isNull || deptExists(s.deptId);
const isSel = selectedStudent && selectedStudent.id === s.id;
const rowCls = [];
if (!valid) rowCls.push('is-broken');
if (i === lastNewStudent) rowCls.push('is-new');
let fkCell;
if (isNull) fkCell = '<td class="nf-fk-null">NULL β</td>';
else if (valid) fkCell = '<td class="nf-fk-valid">' + s.deptId + ' β</td>';
else fkCell = '<td class="nf-fk-invalid">' + s.deptId + ' β</td>';
html += '<tr class="is-clickable' + (rowCls.length ? ' ' + rowCls.join(' ') : '') + '" data-stu="' + s.id + '">' +
'<td>' + s.id + '</td><td>' + s.name + '</td>' + fkCell + '</tr>';
if (showFixFor === s.id) {
const validIds = departments.map(d => d.id);
html += '<tr class="nf-fix-row"><td colspan="3">' +
'<div class="nf-fix-note">' + (isNull ? 'Currently NULL β already valid. Or assign a department:' : 'DeptID ' + s.deptId + ' doesn\u2019t exist. Fix it:') + '</div>' +
'<div class="nf-fix-btns">' +
(isNull ? '' : '<button class="nf-fix-btn nf-fix-null" data-clear="' + s.id + '">Clear to NULL (valid)</button>') +
validIds.map(id => {
const d = departments.find(dep => dep.id === id);
return '<button class="nf-fix-btn" data-fix="' + s.id + '" data-newdept="' + id + '">Point to ' + id + ' (' + d.name + ')</button>';
}).join('') +
'</div></td></tr>';
}
});
html += '</tbody>';
stuTable.innerHTML = html;
stuTable.querySelectorAll('[data-stu]').forEach(row => {
row.addEventListener('click', (e) => {
if (e.target.closest('[data-fix]') || e.target.closest('[data-clear]')) return;
const id = row.dataset.stu;
const s = students.find(st => st.id === id);
const isNull = s.deptId === '';
if (isNull) {
showFixFor = (showFixFor === id) ? null : id;
selectedStudent = null;
renderDept();
renderStudent();
showMsg('β
"' + s.name + '"\u2019s DeptID is NULL β that\u2019s valid. It doesn\u2019t violate referential integrity; she\u2019s just not assigned to a department yet.', 'good');
return;
}
if (!deptExists(s.deptId)) {
showFixFor = (showFixFor === id) ? null : id;
renderDept();
renderStudent();
return;
}
selectedStudent = (selectedStudent && selectedStudent.id === id) ? null : s;
renderDept();
renderStudent();
if (selectedStudent) {
const dept = departments.find(d => d.id === selectedStudent.deptId);
showMsg('π Student "' + selectedStudent.name + '" (DeptID ' + selectedStudent.deptId + ') points to Department "' + dept.name + '".', 'good');
}
});
});
stuTable.querySelectorAll('[data-fix]').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const sid = btn.dataset.fix;
const newDept = btn.dataset.newdept;
const s = students.find(st => st.id === sid);
s.deptId = newDept;
showFixFor = null;
renderDept();
renderStudent();
showMsg('βοΈ "' + s.name + '" now points to DeptID ' + newDept + '.', 'good');
});
});
stuTable.querySelectorAll('[data-clear]').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const sid = btn.dataset.clear;
const s = students.find(st => st.id === sid);
s.deptId = '';
showFixFor = null;
renderDept();
renderStudent();
showMsg('βοΈ "' + s.name + '"\u2019s DeptID is now NULL β still valid, no department required.', 'good');
});
});
}
document.getElementById('nfAddDept').addEventListener('click', () => {
const id = document.getElementById('nfDeptId').value.trim();
const name = document.getElementById('nfDeptName').value.trim();
if (!id) { showMsg('β DeptID can\u2019t be empty.', 'bad'); return; }
if (deptExists(id)) { showMsg('β DeptID ' + id + ' already exists.', 'bad'); return; }
departments.push({ id, name: name || '' });
lastNewDept = departments.length - 1;
renderDept();
renderStudent();
showMsg('βοΈ Department ' + id + ' added.', 'good');
document.getElementById('nfDeptId').value = '';
document.getElementById('nfDeptName').value = '';
});
document.getElementById('nfAddStu').addEventListener('click', () => {
const id = document.getElementById('nfStuId').value.trim();
const name = document.getElementById('nfStuName').value.trim();
const deptId = document.getElementById('nfStuDept').value.trim();
if (!id) { showMsg('β StudentID can\u2019t be empty.', 'bad'); return; }
if (students.some(s => s.id === id)) { showMsg('β StudentID ' + id + ' already exists.', 'bad'); return; }
if (deptId !== '' && !deptExists(deptId)) {
showMsg('β DeptID ' + deptId + ' doesn\u2019t exist in Department β that violates referential integrity. Leave it blank instead if unassigned.', 'bad');
return;
}
students.push({ id, name, deptId });
lastNewStudent = students.length - 1;
renderStudent();
renderDept();
if (deptId === '') {
showMsg('βοΈ Student ' + id + ' added with a NULL DeptID β valid, just unassigned.', 'good');
} else {
showMsg('βοΈ Student ' + id + ' added, correctly referencing Department ' + deptId + '.', 'good');
}
document.getElementById('nfStuId').value = '';
document.getElementById('nfStuName').value = '';
document.getElementById('nfStuDept').value = '';
});
renderDept();
renderStudent();
showMsg('β
Zara\u2019s DeptID is NULL β click her row to see why that\u2019s valid.', 'good');
})();
</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!');
});π Important Points:
Rule | Explanation |
|---|---|
β NULL is allowed | A foreign key can be NULL if no NOT NULL constraint is applied |
β NULL β violation | A NULL in foreign key does not need to match anything in the parent table |
π Still maintains integrity | Because NULL means βunknownβ or βnot applicable,β and not βinvalidβ |
π Real-Life Analogy:
A student admission form may have a field for "Hostel Room Number":
If the student doesn't stay in the hostel β that field is left empty (NULL)
Itβs not wrong, just optional
β Summary:
Question | Answer |
|---|---|
Can a foreign key be NULL? | β Yes |
Is it a violation? | β No |
When is it not allowed? | If |