SQL Table Visualizer Online: Read the Declared Keys
HTML for this example
Select the code and use your copy shortcut. Changes in this preview are temporary.
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SQL Table Visualizer Online: Read the Declared Keys</title><style>*{box-sizing:border-box}body{margin:0;color:var(--ink,#233342);background:var(--bg,#eef2f3);font:17px/1.6 system-ui,sans-serif}main{max-width:980px;margin:auto;padding:clamp(16px,4vw,38px)}h1{font:700 clamp(30px,5vw,49px)/1.12 Georgia,serif;margin:10px 0 20px;max-width:800px}h2{font-size:24px;line-height:1.3;margin:0 0 15px}h3{font-size:19px;margin:12px 0}p{margin:0 0 16px}.eyebrow{font-weight:750;letter-spacing:.08em;text-transform:uppercase;font-size:16px}header{padding:10px 0 25px;border-bottom:3px solid var(--accent,#24586d);margin-bottom:24px}.lede{font-size:20px;max-width:760px}.panel{background:var(--paper,#fff);padding:clamp(18px,3vw,28px);border:1px solid var(--line,#c6d1d8);border-radius:12px;margin:20px 0}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px}.grid>*{min-width:0}.row{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin:16px 0}.stack>*{margin:12px 0}button{font:700 17px/1.4 system-ui,sans-serif;cursor:pointer;color:white;background:var(--accent,#24586d);border:2px solid var(--accent,#24586d);border-radius:7px;padding:11px 17px;max-width:100%;overflow-wrap:anywhere}button.secondary{background:transparent;color:var(--ink,#233342)}button[aria-pressed=true]{box-shadow:inset 0 0 0 3px #fff;outline:2px solid var(--ink,#233342)}button:disabled{opacity:.55;cursor:default}input,textarea,select{font:17px/1.5 system-ui,sans-serif;color:inherit;background:white;border:1px solid #778591;padding:10px 12px;border-radius:6px;max-width:100%;width:100%;min-width:0}input[type=checkbox]{width:22px;height:22px}textarea{min-height:110px;resize:vertical}label{display:block;font-weight:700;margin:12px 0 7px}button:focus-visible,input:focus-visible,textarea:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:3px solid #aa3d70;outline-offset:4px}.result{padding:18px;border-left:5px solid var(--accent,#24586d);background:var(--soft,#e5eff2);white-space:pre-wrap;overflow-wrap:anywhere;margin:20px 0}.result:empty{display:none}.hint{font-size:16px;color:var(--muted,#435968)}table{border-collapse:collapse;width:100%;table-layout:fixed;margin:16px 0}th,td{text-align:left;vertical-align:top;overflow-wrap:anywhere;padding:12px 8px;border-bottom:1px solid var(--line,#c6d1d8)}caption{text-align:left;font-weight:750;margin:8px 0}details{padding:18px;border:1px dashed #7b8b96;margin:20px 0}summary{cursor:pointer;font-weight:750}pre{font:16px/1.6 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;background:#162735;color:#f5f8fa;padding:18px;border-radius:8px}canvas,svg{display:block;max-width:100%;height:auto}footer{margin:28px 0;font-size:16px}.badge{font-weight:750;color:var(--accent,#24586d)}.metric{font:700 38px/1.3 Georgia,serif}.board{display:grid;gap:5px;max-width:440px;margin:20px auto}.board button{padding:7px;min-height:45px}.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}@media(max-width:560px){.grid{grid-template-columns:1fr}main{padding:16px}th,td{padding:10px 5px}.row button{flex-grow:1}.metric{font-size:32px}}
:root{--bg:#eaf0f5;--paper:#fff;--accent:#345e81;--soft:#e1edf5;--ink:#2a3c4b}.tables{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px}.tablebox{border:2px solid #7f9cb3;border-radius:8px;padding:18px;background:white}.tablebox h3{border-bottom:2px solid #7f9cb3;padding-bottom:10px}.pk{background:#e3edf5;padding:12px;font-weight:700;overflow-wrap:anywhere}@media(max-width:650px){.tables{grid-template-columns:1fr}}</style></head><body><main><header><p class="eyebrow">Schema reading lab · PostgreSQL 18 concepts</p><h1>SQL Table Visualizer Online: Read the Declared Keys</h1><p class="lede">One composite key, two declared references, and a place for unresolved declarations.</p><p class="hint">Five supplied schema revisions. No database connection, SQL execution, extraction or migration is performed.</p></header><section class="panel"><h2>Choose a declaration revision</h2><div class="row"><button id="s1" type="button">S1</button><button id="s2" type="button">S2</button><button id="s3" type="button">S3</button><button id="s4" type="button">S4</button><button id="s5" type="button">S5</button></div><p id="revision" class="result" role="status"></p><div class="tables"><article class="tablebox"><h3>members</h3><p>member_id · integer<br>display_name · text</p><p class="pk">PK (member_id)</p></article><article class="tablebox"><h3>enrollments</h3><p>member_id · integer<br>club_id · integer</p><p class="pk">One PK group:<br>(member_id, club_id)</p></article><article class="tablebox"><h3>clubs</h3><p>club_id · integer<br>title · text</p><p class="pk">PK (club_id)</p></article></div><h2>Directed declared references</h2><p id="f1" class="result"></p><p id="f2" class="result"></p><p id="issues" class="result" role="status"></p><details><summary>Source declarations</summary><pre id="source"></pre></details></section><section class="panel"><h2>Pair identity, not individual uniqueness</h2><p>Illustrative pairs (1,10), (1,11), (2,10) are distinct. A second (1,10) repeats the primary-key pair. These are not executed insertion tests.</p><p>A member or club can have zero enrollments. Matching field names alone do not declare a foreign key.</p></section><footer>Compare a repeated member with a repeated enrollment pair, then inspect S3’s missing target.</footer></main><script>
(()=>{'use strict';const $=id=>document.getElementById(id);
let revision=1;const fields={members:['member_id','display_name'],clubs:['club_id','title'],enrollments:['member_id','club_id']},keys={members:[['member_id']],clubs:[['club_id']],enrollments:[['member_id','club_id']]};function render(){const refs=[{id:'F1',name:revision===4?'enrollment_ref':'member_ref',from:['enrollments','member_id'],to:['members','member_id']}];if(revision!==2)refs.push({id:'F2',name:revision===4?'enrollment_ref':'club_ref',from:['enrollments','club_id'],to:['clubs',revision===3?'missing_id':'club_id']});const issues=[];for(const r of refs){const exists=fields[r.from[0]]?.includes(r.from[1])&&fields[r.to[0]]?.includes(r.to[1]),eligible=exists&&keys[r.to[0]].some(k=>k.length===1&&k[0]===r.to[1]);r.state=eligible?'Resolved':'Unresolved target';if(!eligible)issues.push(r.id+' target '+r.to.join('.')+' is not a declared eligible field/key.');if(refs.filter(x=>x.name===r.name).length>1)issues.push(r.id+' constraint label conflict: '+r.name+'.');}function line(id){const r=refs.find(x=>x.id===id);return r?r.id+' · '+r.from.join('.')+' → '+r.to.join('.')+' · '+r.state+' · label '+r.name:id+' · No declared foreign key; enrollments.club_id remains a field.';}$('f1').textContent=line('F1');$('f2').textContent=line('F2');if(revision===5)issues.push('Unsupported interpretation retained: CHECK (member_id > 0). Not classified as invalid PostgreSQL.');$('issues').textContent=issues.length?issues.join('\n'):'No unresolved declarations in this bounded revision. Actual database validation not performed.';$('revision').textContent='Source S'+revision+' · Composite PK remains (member_id, club_id).';$('source').textContent=JSON.stringify({revision:'S'+revision,fields,primaryKeys:keys,references:refs,rawAdditional:revision===5?'CHECK (member_id > 0)':null},null,2);}for(let i=1;i<=5;i++)$('s'+i).addEventListener('click',()=>{revision=i;render();});render();
})();
</script></body></html>