ЁЯПл The SchoolтА║ЁЯЧДя╕П DatabasesтА║ЁЯФН рдзрдбрд╛ 03 тАФ SQL reads: рджрдкреНрддрд░рджрд╛рд░рд╛рд▓рд╛ рд╡рд┐рдЪрд╛рд░рдгреЗ
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯФН рдзрдбрд╛ 03 тАФ SQL reads: рджрдкреНрддрд░рджрд╛рд░рд╛рд▓рд╛ рд╡рд┐рдЪрд╛рд░рдгреЗ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 18 рдкреИрдХреА рдзрдбрд╛ 03 ┬╖ рдорд╛рдЧреЗ: lesson-02-tables-rows-keys ┬╖ рдкреБрдвреЗ: lesson-04-transactions


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

рдзрдбреЗ 01тАУ02, рдЖрдгрд┐ рдкреНрд░рд╢реНрдирд╛рдВрдЪреА рднрд╛рд╖рд╛: SELECT, WHERE, ORDER BY, LIMIT, рдиреЛрдВрджрд╡рд╣реНрдпрд╛рдВрдордзреНрдпреЗ JOIN, рдЖрдгрд┐ рдореЛрдЬрдгреНрдпрд╛рд╕рд╛рдареА GROUP BY.

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

рддреБрдореНрд╣реА рд╕реНрд╡рддрдГ рдХрдкрд╛рдЯрд╛рдВрдордзреНрдпреЗ рдлрд┐рд░рдд рдирд╛рд╣реА. рддреБрдореНрд╣реА рджрдкреНрддрд░рджрд╛рд░рд╛рд▓рд╛ ЁЯзСтАНЁЯТ╝ рдкреНрд░рдорд╛рдгрд┐рдд рдкрджреНрдзрддреАрдиреЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓рд╛ рдкреНрд░рд╢реНрди рджреЗрддрд╛, рдЖрдгрд┐ рддреЛ рд╕рд░реНрд╡рд╛рдд рдЬрд▓рдж рдорд╛рд░реНрдЧ рдард░рд╡рддреЛ:

рджрдкреНрддрд░рджрд╛рд░рд╛рдЪрд╛ рдХрд╛рдорд╛рдЪрд╛ рдХреНрд░рдо рдиреЗрд╣рдореА рддреЛрдЪ рдЕрд╕рддреЛ: рдЖрдзреА рдЧрд╛рд│рдгреЗ (WHERE), рдордЧ рдЧрдЯ рдХрд░рдгреЗ (GROUP BY), рдордЧ рдХреНрд░рдо рд▓рд╛рд╡рдгреЗ (ORDER BY), рдордЧ рдкрд╛рдиреЗ (LIMIT/OFFSET) тАФ API рд╢рд╛рд│реЗрдЪрд╛ counter рд╡рд╛рдкрд░рддреЛ рддреЗрдЪ filter тЖТ sort тЖТ paginate, рдХрд╛рд░рдг counter рдпрд╛рдЪ рджрдкреНрддрд░рджрд╛рд░рд╛рд▓рд╛ рд╡рд┐рдЪрд╛рд░рддреЛ.

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

flowchart LR
    q["тЭУ SELECT s.name, c.name, g.grade<br/>FROM grades g JOIN students s тАж JOIN classes c тАж<br/>WHERE g.subject = 'maths' ORDER BY g.grade"]
    f["1 WHERE тАФ filter lines"]
    j["2 JOIN тАФ cross-reference registers by key"]
    o["3 ORDER BY / LIMIT тАФ sort, then the first few"]
    a["ЁЯУД ('Aishwarya','3A','A') ('Katrina','3A','A+') тАж"]
    q --> f --> j --> o --> a

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

ЁЯФЧ JOIN рдХреБрдЯреБрдВрдм тАФ рдЬреЛрдбреАрджрд╛рд░ рдирд╕реЗрд▓ рддреЗрд╡реНрд╣рд╛ рдХреЛрдгрддреНрдпрд╛ rows рдЯрд┐рдХрддрд╛рдд?

python3 db/demo.py joins рдПрдХ рдЫреЛрдЯрд╛ library_cards table рдмрдирд╡рддреЛ: рдРрд╢реНрд╡рд░реНрдпрд╛, рдХрддрд░рд┐рдирд╛ рдЖрдгрд┐ рдореАрд░рд╛ рдпрд╛рдВрдЪреНрдпрд╛рдХрдбреЗ cards рдЖрд╣реЗрдд, рджреАрдкрд┐рдХрд╛ рдЖрдгрд┐ Rohan рдХрдбреЗ рдирд╛рд╣реАрдд, рдЖрдгрд┐ card 104 рдПрдХрд╛ рдкрд╛рд╣реБрдгреНрдпрд╛рдЪреЗ рдЖрд╣реЗ (student 9, рд╢рд╛рд│реЗрддреАрд▓ рд╡рд┐рджреНрдпрд╛рд░реНрдереА рдирд╡реНрд╣реЗ). рдордЧ рддреЛ students s тАж library_cards l ON l.student_id = s.id рд╡рд░ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рдХрд╛рд░рдЪрд╛ join рдЪрд╛рд▓рд╡рддреЛ:

JOIN рдХрд╛рдп рдареЗрд╡рддреЛ rows рдЦрд░рд╛ рдирд┐рдХрд╛рд▓
INNER JOIN рджреЛрдиреНрд╣реА рдмрд╛рдЬреВрдВрдирд╛ рдЬреБрд│рдгрд╛рд▒реНрдпрд╛ рдЬреЛрдбреНрдпрд╛рдЪ 3 Aishwarya 101 ┬╖ Katrina 102 ┬╖ Meera 103
LEFT JOIN рдкреНрд░рддреНрдпреЗрдХ рдбрд╛рд╡реА row (рд╡рд┐рджреНрдпрд╛рд░реНрдереА); рдЬреЛрдбреАрджрд╛рд░ рдирд╛рд╣реА тЖТ NULL 5 тАж + Dipika NULL ┬╖ Rohan NULL
RIGHT JOIN рдкреНрд░рддреНрдпреЗрдХ рдЙрдЬрд╡реА row (cards); рдЬреЛрдбреАрджрд╛рд░ рдирд╛рд╣реА тЖТ NULL 4 тАж + NULL 104
FULL OUTER JOIN рджреЛрдиреНрд╣реА рдмрд╛рдЬреВрдВрдЪреЗ рд╕рдЧрд│реЗ 6 Dipika NULL ┬╖ Rohan NULL ┬╖ NULL 104 + 3 рдЬреЛрдбреНрдпрд╛
anti-join: LEFT JOIN тАж WHERE l.card IS NULL рдЬреЛрдбреАрджрд╛рд░ рдирд╕рд▓реЗрд▓реНрдпрд╛ рдбрд╛рд╡реНрдпрд╛ rows 2 Dipika ┬╖ Rohan
CROSS JOIN (ON рдирд╛рд╣реА) рдкреНрд░рддреНрдпреЗрдХ рд╕рдВрдпреЛрдЧ 2 ├Ч 2 = 4 3A maths ┬╖ 3A science ┬╖ 3B maths ┬╖ 3B science
self join: students a JOIN students b ON a.class_id = b.class_id AND a.id < b.id table рд╕реНрд╡рддрдГрд╢реАрдЪ 4 AishwaryaтАУKatrina ┬╖ AishwaryaтАУDipika ┬╖ KatrinaтАУDipika ┬╖ MeeraтАУRohan

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдПрдХрд╛ рдУрд│реАрдд рд▓рд┐рд╣рд┐рддрд╛ рдпреЗрдгрд╛рд░рд╛ рдкреНрд░рд╢реНрди, рддреБрдореНрд╣рд╛рд▓рд╛ рд▓рд┐рд╣рд╛рд╡рд╛, test рдХрд░рд╛рд╡рд╛ рдЖрдгрд┐ рд╕рд╛рдВрднрд╛рд│рд╛рд╡рд╛ рд▓рд╛рдЧрдгрд╛рд▒реНрдпрд╛ program рдЪреА рдЬрд╛рдЧрд╛ рдШреЗрддреЛ. рдкреНрд░рддреНрдпреЗрдХ dashboard, report рдЖрдгрд┐ API endpoint рдореНрд╣рдгрдЬреЗ рд╡реЗрд╢рд╛рдВрддрд░ рдХреЗрд▓реЗрд▓рд╛ SELECT тАФ рдЖрдгрд┐ рддреНрдпрд╛ рд╕рд░реНрд╡рд╛рдВрдорд╛рдЧреЗ рддреАрдЪ рдЪрд╛рд░ clauses, рддреНрдпрд╛рдЪ рдХреНрд░рдорд╛рдиреЗ рдЕрд╕рддрд╛рдд.

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

db/demo.py рдордзреАрд▓ reads(), join() рдЖрдгрд┐ joins() рдореНрд╣рдгрдЬреЗрдЪ рд╣рд╛ рдзрдбрд╛; рдЪрд╛рд▓рд╡рдгреНрдпрд╛рдЖрдзреА SQL strings рд╢рд╛рд│реЗрдЪреНрдпрд╛ рд╢рдмреНрджрд╛рдВрдд рдореЛрдареНрдпрд╛рдиреЗ рд╡рд╛рдЪрд╛.

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

python3 db/demo.py reads join joins
python3 - <<'EOF'
import sqlite3; c = sqlite3.connect("db/school.db")
print(c.execute("SELECT name FROM students WHERE name LIKE 'K%' OR roll_no IN ('3B-01')").fetchall())
print(c.execute("SELECT c.name, AVG(CASE g.grade WHEN 'A+' THEN 4.3 WHEN 'A' THEN 4 WHEN 'B+' THEN 3.3 WHEN 'B' THEN 3 ELSE 2 END) FROM grades g JOIN students s ON s.id=g.student_id JOIN classes c ON c.id=s.class_id GROUP BY c.name").fetchall())
print(c.execute("SELECT s.name, COUNT(g.id) FROM students s LEFT JOIN grades g ON g.student_id=s.id GROUP BY s.id HAVING COUNT(g.id) >= 2").fetchall())
EOF

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

reads рдЕрдХрд╛рд░рд╡рд┐рд▓реНрд╣реЗ рдХреНрд░рдорд╛рдиреЗ 3A рдЪреНрдпрд╛ рджреЛрди рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреА рдЫрд╛рдкрддреЛ; join рдЖрдзреА grade рдЖрдгрд┐ рдордЧ рдирд╛рд╡рд╛рдиреБрд╕рд╛рд░ рд▓рд╛рд╡рд▓реЗрд▓реЗ рдкрд╛рдЪ maths grades рдЫрд╛рдкрддреЛ, рдЖрдгрд┐ ('3A', 3) рд╡ ('3B', 2) рдЕрд╕рд▓реЗрд▓рд╛ рдПрдХ GROUP BY. joins INNER рд╕рд╛рдареА 3 rows, LEFT рд╕рд╛рдареА 5 (Dipika рдЖрдгрд┐ Rohan None рд╕рд╣), RIGHT рд╕рд╛рдареА 4 ((None, 104)), FULL OUTER рд╕рд╛рдареА 6, anti-join рд╕рд╛рдареА Dipika рдЖрдгрд┐ Rohan, 4 timetable рдЬреЛрдбреНрдпрд╛ рдЖрдгрд┐ 4 study-buddy рдЬреЛрдбреНрдпрд╛ рдЫрд╛рдкрддреЛ. рддреБрдордЪреНрдпрд╛ рддреАрди queries Katrina рдЖрдгрд┐ Meera, рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧрд╛рдЪреА рд╕рд░рд╛рд╕рд░реА, рдЖрдгрд┐ рджреЛрди рдХрд┐рдВрд╡рд╛ рдЬрд╛рд╕реНрдд grades рдЕрд╕рд▓реЗрд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рджреНрдпрд╛рд░реНрдереА рдЫрд╛рдкрддрд╛рдд.

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

рддреБрдореНрд╣реА рджрдкреНрддрд░рджрд╛рд░рд╛рд▓рд╛ рдЧрд╛рд│рд▓реЗрд▓реЗ, рдЬреЛрдбрд▓реЗрд▓реЗ, рдЧрдЯ рдХреЗрд▓реЗрд▓реЗ рдЖрдгрд┐ рдкрд╛рдиреЗ рдкрд╛рдбрд▓реЗрд▓реЗ рдкреНрд░рд╢реНрди рд╡рд┐рдЪрд╛рд░реВ рд╢рдХрддрд╛ тАФ рдЖрдгрд┐ JOIN рд▓рд╛ "рдпрд╛ рдиреЛрдВрджрд╡рд╣реНрдпрд╛ рддреНрдпрд╛рдВрдЪреНрдпрд╛ keys рдиреЗ рдЬреЛрдбрд╛" рдЕрд╕реЗ рд╡рд╛рдЪреВ рд╢рдХрддрд╛.

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

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рд╣рд│реВ pages рдмрд╣реБрддреЗрдХ рд╡реЗрд│рд╛ рдПрдХрд╛рдЪ query рдореБрд│реЗ рдЕрд╕рддрд╛рдд тАФ WHERE рдирд╕рд▓реЗрд▓реА, key рд╢рд┐рд╡рд╛рдп JOIN, рдХрд┐рдВрд╡рд╛ index рдирд╕рд▓реЗрд▓реНрдпрд╛ column рд╡рд░ ORDER BY тАФ рдЪрд╛рд░ clauses рдорд╛рд╣реАрдд рдЕрд╕рд▓реНрдпрд╛ рдХреА рд╣реЗ рд╕рдЧрд│реЗ SQL рдордзреНрдпреЗрдЪ рд╡рд╛рдЪрддрд╛ рдпреЗрддреЗ.

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

рд╡рд╛рдЪрдгреЗ рд╕реБрд░рдХреНрд╖рд┐рдд рдЖрд╣реЗ; рд▓рд┐рд╣рд┐рдгреНрдпрд╛рд╕рд╛рдареА рдЦреЛрдбрд░рдмрд░ рдЕрд╕рд▓реЗрд▓реА pencil рд▓рд╛рдЧрддреЗ. Writes рдЖрдгрд┐ transactions тАФ INSERT, UPDATE, DELETE, рдЖрдгрд┐ ACID.

git checkout lesson-04-transactions

ЁЯФН Lesson 03 тАФ SQL reads: asking the archivist

ЁЯУН You are here: Lesson 03 of 18 ┬╖ Previous: lesson-02-tables-rows-keys ┬╖ Next: lesson-04-transactions


ЁЯУж What's in this branch

Lessons 01тАУ02, plus the question language: SELECT, WHERE, ORDER BY, LIMIT, JOIN across registers, and GROUP BY for counting.

ЁЯзТ Explain like I'm 5

You do not walk the shelves yourself. You hand the archivist ЁЯзСтАНЁЯТ╝ a question written the standard way, and they decide the fastest route:

The archivist's order of work is always the same: filter (WHERE), then group (GROUP BY), then order (ORDER BY), then page (LIMIT/OFFSET) тАФ the same filter тЖТ sort тЖТ paginate the API school's counter uses, because the counter asks this archivist.

ЁЯЧ║я╕П Diagram

flowchart LR
    q["тЭУ SELECT s.name, c.name, g.grade<br/>FROM grades g JOIN students s тАж JOIN classes c тАж<br/>WHERE g.subject = 'maths' ORDER BY g.grade"]
    f["1 WHERE тАФ filter lines"]
    j["2 JOIN тАФ cross-reference registers by key"]
    o["3 ORDER BY / LIMIT тАФ sort, then the first few"]
    a["ЁЯУД ('Aishwarya','3A','A') ('Katrina','3A','A+') тАж"]
    q --> f --> j --> o --> a

тЭУ What

ЁЯФЧ The JOIN family тАФ which rows survive when a partner is missing?

python3 db/demo.py joins builds a tiny library_cards table: Aishwarya, Katrina and Meera have cards, Dipika and Rohan have none, and card 104 belongs to a visitor (student 9, not a pupil). Then it runs every kind of join on students s тАж library_cards l ON l.student_id = s.id:

JOIN keeps rows real result
INNER JOIN only pairs that match on both sides 3 Aishwarya 101 ┬╖ Katrina 102 ┬╖ Meera 103
LEFT JOIN every left row (pupils); no partner тЖТ NULL 5 тАж + Dipika NULL ┬╖ Rohan NULL
RIGHT JOIN every right row (cards); no partner тЖТ NULL 4 тАж + NULL 104
FULL OUTER JOIN everything from both sides 6 Dipika NULL ┬╖ Rohan NULL ┬╖ NULL 104 + the 3 pairs
anti-join: LEFT JOIN тАж WHERE l.card IS NULL left rows with no partner 2 Dipika ┬╖ Rohan
CROSS JOIN (no ON) every combination 2 ├Ч 2 = 4 3A maths ┬╖ 3A science ┬╖ 3B maths ┬╖ 3B science
self join: students a JOIN students b ON a.class_id = b.class_id AND a.id < b.id a table against itself 4 AishwaryaтАУKatrina ┬╖ AishwaryaтАУDipika ┬╖ KatrinaтАУDipika ┬╖ MeeraтАУRohan

ЁЯдФ Why

Because a question you can write in one line replaces a program you would have to write, test and keep. Every dashboard, report and API endpoint is a SELECT in a costume тАФ and the same four clauses, in the same order, are behind all of them.

ЁЯФз How (in this repo)

reads(), join() and joins() in db/demo.py are this lesson; read the SQL strings aloud in school words before you run them.

ЁЯзк Try it

python3 db/demo.py reads join joins
python3 - <<'EOF'
import sqlite3; c = sqlite3.connect("db/school.db")
print(c.execute("SELECT name FROM students WHERE name LIKE 'K%' OR roll_no IN ('3B-01')").fetchall())
print(c.execute("SELECT c.name, AVG(CASE g.grade WHEN 'A+' THEN 4.3 WHEN 'A' THEN 4 WHEN 'B+' THEN 3.3 WHEN 'B' THEN 3 ELSE 2 END) FROM grades g JOIN students s ON s.id=g.student_id JOIN classes c ON c.id=s.class_id GROUP BY c.name").fetchall())
print(c.execute("SELECT s.name, COUNT(g.id) FROM students s LEFT JOIN grades g ON g.student_id=s.id GROUP BY s.id HAVING COUNT(g.id) >= 2").fetchall())
EOF

тЬЕ Verify тАФ what you should see

reads prints two 3A students in alphabetical order; join prints five maths grades sorted by grade then name, and a GROUP BY with ('3A', 3) and ('3B', 2). joins prints 3 rows for INNER, 5 for LEFT (Dipika and Rohan with None), 4 for RIGHT ((None, 104)), 6 for FULL OUTER, Dipika and Rohan for the anti-join, 4 timetable pairs and 4 study-buddy pairs. Your three queries print Katrina and Meera, an average per class, and every student with two or more grades.

ЁЯПБ What you just proved

You can ask the archivist filtered, joined, grouped and paged questions тАФ and read a JOIN as "cross-reference these registers by their keys".

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: slow pages are almost always one query with a missing WHERE, a JOIN without a key, or an ORDER BY on an unindexed column тАФ all readable in the SQL once you know the four clauses.

тПня╕П Next

Reading is safe; writing needs a pencil with an eraser. Writes and transactions тАФ INSERT, UPDATE, DELETE, and ACID.

git checkout lesson-04-transactions
тЖР Previoustables rows keysNext тЖТtransactions

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