ЁЯПл The SchoolтА║ЁЯЧДя╕П DatabasesтА║ЁЯУЗ рдзрдбрд╛ 02 тАФ Tables, rows рдЖрдгрд┐ keys: рдУрд│ рдХреНрд░рдорд╛рдВрдХ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдиреЛрдВрджрд╡рд╣реНрдпрд╛
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯУЗ рдзрдбрд╛ 02 тАФ Tables, rows рдЖрдгрд┐ keys: рдУрд│ рдХреНрд░рдорд╛рдВрдХ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдиреЛрдВрджрд╡рд╣реНрдпрд╛

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 18 рдкреИрдХреА рдзрдбрд╛ 02 ┬╖ рдорд╛рдЧреЗ: lesson-01-why-databases ┬╖ рдкреБрдвреЗ: lesson-03-sql-reads


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбрд╛ 01, рдЖрдгрд┐ рд░реЗрдХреЙрд░реНрдб рд░реВрдордЪрд╛ рдЖрдХрд╛рд░: tables (рдиреЛрдВрджрд╡рд╣реНрдпрд╛), rows (рдУрд│реА), columns (рдЫрд╛рдкрд▓реЗрд▓реА рд╢реАрд░реНрд╖рдХреЗ), primary keys (рдУрд│ рдХреНрд░рдорд╛рдВрдХ) рдЖрдгрд┐ foreign keys ("рдиреЛрдВрджрд╡рд╣реА X, рдУрд│ 3 рдкрд╛рд╣рд╛").

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рд░реЗрдХреЙрд░реНрдб рд░реВрдо рдЙрдШрдбрд╛ рдЖрдгрд┐ рддреБрдореНрд╣рд╛рд▓рд╛ рддреАрди рдиреЛрдВрджрд╡рд╣реНрдпрд╛ ЁЯУЗ рджрд┐рд╕рддрд╛рдд:

рдкреНрд░рддреНрдпреЗрдХ рдиреЛрдВрджрд╡рд╣реАрддреАрд▓ рдУрд│ рдХреНрд░рдорд╛рдВрдХ рдореНрд╣рдгрдЬреЗ primary key: рддреЛ рдХрдзреАрдЪ рдкреБрдиреНрд╣рд╛ рд╡рд╛рдкрд░рд▓рд╛ рдЬрд╛рдд рдирд╛рд╣реА, рдХрдзреАрдЪ рдмрджрд▓рдд рдирд╛рд╣реА, рдЖрдгрд┐ рджреБрд╕рд░реА рдиреЛрдВрджрд╡рд╣реА рдлрдХреНрдд рддреНрдпрд╛рдХрдбреЗрдЪ рдмреЛрдЯ рджрд╛рдЦрд╡рддреЗ. рджреБрд╕рд▒реНрдпрд╛ рдиреЛрдВрджрд╡рд╣реАрдХрдбреЗ рдмреЛрдЯ рджрд╛рдЦрд╡рдгрд╛рд░реА рдиреЛрдВрдж рдореНрд╣рдгрдЬреЗ foreign key тАФ рдЖрдгрд┐ рджрдкреНрддрд░рджрд╛рд░ рдПрдХ рдирд┐рдпрдо рдкрд╛рд│рд╛рдпрд▓рд╛ рд▓рд╛рд╡рддреЛ: рдЕрд╕реНрддрд┐рддреНрд╡рд╛рдд рдирд╕рд▓реЗрд▓реНрдпрд╛ рдУрд│реАрдХрдбреЗ рдХреЛрдгрддреАрд╣реА рдУрд│ рдмреЛрдЯ рджрд╛рдЦрд╡реВ рд╢рдХрдд рдирд╛рд╣реА. "classes, рдУрд│ 99 рдкрд╛рд╣рд╛" рдЕрд╕реЗ рд▓рд┐рд╣рд╛рдпрдЪрд╛ рдкреНрд░рдпрддреНрди рдХрд░рд╛, рдЖрдгрд┐ рдкреЗрди рдирд╛рдХрд╛рд░рд▓рд╛ рдЬрд╛рддреЛ.

рдкреНрд░рддреНрдпреЗрдХ рдЫрд╛рдкрд▓реЗрд▓реНрдпрд╛ рд╢реАрд░реНрд╖рдХрд╛рд▓рд╛ рдПрдХ type (TEXT, INTEGER) рдЖрдгрд┐ рдирд┐рдпрдо (NOT NULL, UNIQUE, CHECK) рдЕрд╕рддрд╛рдд. рдХреЛрдгрддреНрдпрд╛рд╣реА program рд▓рд╛ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧрдгреНрдпрд╛рдЖрдзреАрдЪ рдиреЛрдВрджрд╡рд╣реА рд╕реНрд╡рддрдГрдЪ рдореВрд░реНрдЦрдкрдгрд╛ рдирд╛рдХрд╛рд░рддреЗ тАФ рджреБрд╣реЗрд░реА roll number, Z рд╣рд╛ grade.

ЁЯЧ║я╕П рдЖрдХреГрддреА

erDiagram
    classes ||--o{ students : "class_id тЖТ classes.id"
    students ||--o{ grades : "student_id тЖТ students.id"
    classes { int id PK "line number" string name "UNIQUE: 3A, 3B" }
    students { int id PK string name string roll_no "UNIQUE" int class_id FK }
    grades { int id PK int student_id FK string subject int term "CHECK 1-3" string grade "CHECK A+..C" }

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рддреАрд╕ rows рдордзреНрдпреЗ copy рдХреЗрд▓реЗрд▓реА рдорд╛рд╣рд┐рддреА рд╢реБрдХреНрд░рд╡рд╛рд░рдкрд░реНрдпрдВрдд рддреНрдпрд╛рдкреИрдХреА рдПрдХрд╛рдд рддрд░реА рдЪреБрдХреАрдЪреА рдЕрд╕реЗрд▓, рдЖрдгрд┐ рдХреБрдареЗрдЪ рдмреЛрдЯ рди рджрд╛рдЦрд╡рдгрд╛рд░реА row рдореНрд╣рдгрдЬреЗ report рдЪреА рд╡рд╛рдЯ рдкрд╛рд╣рдгрд╛рд░рд╛ crash. Keys рдЖрдгрд┐ constraints рдирд┐рдпрдорд╛рдВрдирд╛ рдкреНрд░рддреНрдпреЗрдХ program рдордзреВрди рдмрд╛рд╣реЗрд░ рдХрд╛рдвреВрди рд╕рд░реНрд╡ programs рд╡рд╛рдкрд░рддрд╛рдд рддреНрдпрд╛ рдПрдХрд╛рдЪ рдЬрд╛рдЧреА рдиреЗрддрд╛рдд. рдореНрд╣рдгреВрдирдЪ API рд╢рд╛рд│реЗрдЪрд╛ counter рд▓рд╣рд╛рди рд░рд╛рд╣реВ рд╢рдХрддреЛ: рдЦреЛрд▓реА рдЖрдзреАрдЪ рдореВрд░реНрдЦрдкрдгрд╛ рдирд╛рдХрд╛рд░рддреЗ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

db/schema.sql рд╡рд░рдкрд╛рд╕реВрди рдЦрд╛рд▓рдкрд░реНрдпрдВрдд рд╡рд╛рдЪрд╛: рдЪрд╛рд░ CREATE TABLE, рдкреНрд░рддреНрдпреЗрдХреА рдЖрдкрд▓реА primary key, foreign keys, constraints тАФ рдЖрдгрд┐ рджреЛрди indexes рдЬреЗ рдЖрдореНрд╣реА рдзрдбрд╛ 06 рдордзреНрдпреЗ рд╕рдордЬрд╛рд╡рддреЛ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 db/demo.py model      # watch the schema refuse a duplicate roll number and an impossible grade
python3 - <<'EOF'
import sqlite3; c = sqlite3.connect("db/school.db"); c.execute("PRAGMA foreign_keys = ON")
print(c.execute("PRAGMA table_info(students)").fetchall())          # the printed headings
try: c.execute("INSERT INTO students (name, roll_no, class_id) VALUES ('Ghost', '9Z-01', 99)")
except sqlite3.IntegrityError as e: print("refused:", e)              # a line may not point nowhere
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

table_info рдордзреНрдпреЗ id, name, roll_no, class_id рддреНрдпрд╛рдВрдЪреНрдпрд╛ types рдЖрдгрд┐ notnull flags рд╕рд╣ рджрд┐рд╕рддрд╛рдд; class_id = 99 рдЕрд╕рд▓реЗрд▓рд╛ insert refused: FOREIGN KEY constraint failed рдЫрд╛рдкрддреЛ; demo.py model рдЖрдгрдЦреА рджреЛрди рдирдХрд╛рд░ рджрд╛рдЦрд╡рддреЛ (UNIQUE рдЖрдгрд┐ CHECK).

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рдЦреЛрд▓реАрдЪрд╛ рдЖрдХрд╛рд░ рдХрдкрд╛рдЯрд╛рддрдЪ рд▓рд┐рд╣рд┐рд▓реЗрд▓рд╛ рдЖрд╣реЗ: keys рдиреЛрдВрджрд╡рд╣реНрдпрд╛ рдЬреЛрдбрддрд╛рдд, рдЖрдгрд┐ constraints рдХреЛрдгрддреНрдпрд╛рд╣реА program рд▓рд╛ рджрд┐рд╕рдгреНрдпрд╛рдЖрдзреАрдЪ рд╡рд╛рдИрдЯ рдУрд│реА рдирд╛рдХрд╛рд░рддрд╛рдд.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: "report рдЪреБрдХреАрдЪрд╛ рдЖрд╣реЗ" рдЕрд╢рд╛ рдмрд╣реБрддреЗрдХ tickets рдЪреЗ рдореВрд│ рджреЛрди рдард┐рдХрд╛рдгреА рд╕рд╛рдард╡рд▓реЗрд▓реА рдорд╛рд╣рд┐рддреА рдХрд┐рдВрд╡рд╛ delete рдЭрд╛рд▓реЗрд▓реНрдпрд╛ row рдХрдбреЗ рдмреЛрдЯ рджрд╛рдЦрд╡рдгрд╛рд░рд╛ reference рдЕрд╕рддреЗ. Constraints рд╣рд╛ рддреБрдореНрд╣реА рдХрдзреАрд╣реА рд▓рд┐рд╣рд╛рд▓ рдЕрд╕рд╛ рд╕рд░реНрд╡рд╛рдд рд╕реНрд╡рд╕реНрдд test suite рдЖрд╣реЗ.

тПня╕П рдкреБрдвреЗ

рдЖрддрд╛ рдиреЛрдВрджрд╡рд╣реНрдпрд╛рдВрдирд╛ рдЖрдХрд╛рд░ рдЖрд▓рд╛ рдЖрд╣реЗ, рддреНрдпрд╛рдВрдирд╛ рдкреНрд░рд╢реНрди рд╡рд┐рдЪрд╛рд░рд╛: SQL reads тАФ SELECT, WHERE, ORDER BY, LIMIT, рдЖрдгрд┐ рдиреЛрдВрджрд╡рд╣реНрдпрд╛рдВрдордзреНрдпреЗ JOIN.

git checkout lesson-03-sql-reads

ЁЯУЗ Lesson 02 тАФ Tables, rows & keys: registers with line numbers

ЁЯУН You are here: Lesson 02 of 18 ┬╖ Previous: lesson-01-why-databases ┬╖ Next: lesson-03-sql-reads


ЁЯУж What's in this branch

Lesson 01, plus the shape of the record room: tables (registers), rows (lines), columns (the printed headings), primary keys (line numbers) and foreign keys ("see register X, line 3").

ЁЯзТ Explain like I'm 5

Open the record room and you see three registers ЁЯУЗ:

The line number in each register is the primary key: it is never reused, never changes, and is the only thing another register points at. A pointer to another register is a foreign key тАФ and the archivist enforces a rule: a line may not point at a line that does not exist. Try to write "see classes, line 99" and the pen is refused.

Every printed heading has a type (TEXT, INTEGER) and rules (NOT NULL, UNIQUE, CHECK). The register itself refuses nonsense тАФ a duplicate roll number, a grade of Z тАФ before any program has to.

ЁЯЧ║я╕П Diagram

erDiagram
    classes ||--o{ students : "class_id тЖТ classes.id"
    students ||--o{ grades : "student_id тЖТ students.id"
    classes { int id PK "line number" string name "UNIQUE: 3A, 3B" }
    students { int id PK string name string roll_no "UNIQUE" int class_id FK }
    grades { int id PK int student_id FK string subject int term "CHECK 1-3" string grade "CHECK A+..C" }

тЭУ What

ЁЯдФ Why

Because a fact copied into thirty rows will be wrong in one of them by Friday, and a row that points nowhere is a crash waiting for a report. Keys and constraints move the rules out of every program and into the one place all programs share. That is why the API school's counter can stay small: the room already refuses nonsense.

ЁЯФз How (in this repo)

Read db/schema.sql top to bottom: four CREATE TABLEs, each with its primary key, its foreign keys, its constraints тАФ and two indexes we explain in lesson 06.

ЁЯзк Try it

python3 db/demo.py model      # watch the schema refuse a duplicate roll number and an impossible grade
python3 - <<'EOF'
import sqlite3; c = sqlite3.connect("db/school.db"); c.execute("PRAGMA foreign_keys = ON")
print(c.execute("PRAGMA table_info(students)").fetchall())          # the printed headings
try: c.execute("INSERT INTO students (name, roll_no, class_id) VALUES ('Ghost', '9Z-01', 99)")
except sqlite3.IntegrityError as e: print("refused:", e)              # a line may not point nowhere
EOF

тЬЕ Verify тАФ what you should see

table_info lists id, name, roll_no, class_id with their types and notnull flags; the insert with class_id = 99 prints refused: FOREIGN KEY constraint failed; demo.py model shows two more refusals (UNIQUE and CHECK).

ЁЯПБ What you just proved

The shape of the room is written into the shelf: keys connect registers, and constraints refuse bad lines before any program sees them.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: the majority of "the report is wrong" tickets trace back to a fact stored in two places or a reference that pointed at a deleted row. Constraints are the cheapest test suite you will ever write.

тПня╕П Next

Now that the registers have a shape, ask them questions: SQL reads тАФ SELECT, WHERE, ORDER BY, LIMIT, and JOIN across registers.

git checkout lesson-03-sql-reads
тЖР Previouswhy databasesNext тЖТsql reads

This page is the lesson's README from the lesson-02-tables-rows-keys branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.