ЁЯПЖ рдзрдбрд╛ 14 тАФ Advanced SQL: рдПрдХрд╣реА row рди рдЧрдорд╛рд╡рддрд╛ rankings
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 18 рдкреИрдХреА рдзрдбрд╛ 14 ┬╖ рдорд╛рдЧреАрд▓: lesson-13-security ┬╖ рдкреБрдвреАрд▓: lesson-15-inside-the-engine
рднрд╛рдЧ 3 тАФ рдЕрдзрд┐рдХ рдЦреЛрд▓рд╛рдд. рдзрдбреЗ 01тАУ12 рдиреА рд░реЗрдХреЙрд░реНрдб рд░реВрдо рдмрд╛рдВрдзрд▓реА рдЖрдгрд┐ рдЪрд╛рд▓рд╡рд▓реА. рдзрдбреЗ 13тАУ18 рд╣реЗ interviews рдЖрдгрд┐ incidents рдордзреНрдпреЗ рд╡рд┐рдЪрд╛рд░рд▓реЗ рдЬрд╛рдгрд╛рд░реЗ рд╡рд┐рд╖рдп рдЖрд╣реЗрдд тАФ рдкреНрд░рддреНрдпреЗрдХ рддреНрдпрд╛рдЪ
school.dbрд╡рд░ рдкреНрд░рддреНрдпрдХреНрд╖ рдЪрд╛рд▓рддреЛ.
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ14, рдЖрдгрд┐ db/demo.py рдордзрд▓реЗ window(): рд╢реНрд░реЗрдгреАрдВрдЪреЗ рдЧреБрдгрд╛рдВрдордзреНрдпреЗ
рд░реВрдкрд╛рдВрддрд░ рдХрд░рдгрд╛рд░рд╛ рдПрдХ CTE, рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧрд╛рдд RANK() рдЖрдгрд┐ running total, "рд╢рд╛рд│реЗрдЪреНрдпрд╛ рд╕рд░рд╛рд╕рд░реАрдкреЗрдХреНрд╖рд╛
рдЬрд╛рд╕реНрдд" рд╕рд╛рдареА рдПрдХ subquery, рдЖрдгрд┐ report_card рдирд╛рд╡рд╛рдЪрд╛ рдПрдХ view.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
GROUP BY рдореНрд╣рдгрдЬреЗ рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧрд╛рдХрдбреЗ рдПрдХрдЪ рдЖрдХрдбрд╛ рдорд╛рдЧрдгреЗ тАФ рд╡рд░реНрдЧрд╛рдЪреА рд╕рд░рд╛рд╕рд░реА тАФ рдЖрдгрд┐
рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧрд╛рд╕рд╛рдареА рдПрдХ рдЪрд┐рдареНрдареА рдкрд░рдд рдорд┐рд│рдгреЗ. рдкрдг рдореБрдЦреНрдпрд╛рдзреНрдпрд╛рдкрд┐рдХреЗрд▓рд╛ рдпрд╛рджреАрдд рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреА
рд╣рд╡реА рдЖрд╣реЗ, рдЖрдгрд┐ рдирд╛рд╡рд╛рд╕рдореЛрд░ рддрд┐рдЪреНрдпрд╛ рд╡рд░реНрдЧрд╛рддрд▓рд╛ рддрд┐рдЪрд╛ рдХреНрд░рдорд╛рдВрдХ рд▓рд┐рд╣рд┐рд▓реЗрд▓рд╛ рд╣рд╡рд╛. рд╣реАрдЪ window
function: рддреА рд╕рдВрдмрдВрдзрд┐рдд rows рдЪреА рдПрдХ "рдЦрд┐рдбрдХреА" (рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреАрдЪрд╛ рд╡рд░реНрдЧ) рдкрд╛рд╣рддреЗ рдЖрдгрд┐ рдПрдХрд╣реА row рди рджрд╛рдмрддрд╛
рдкреНрд░рддреНрдпреЗрдХ row рд╡рд░ рдПрдХ рдЬрд╛рд╕реНрддреАрдЪрд╛ рдЖрдХрдбрд╛ рд▓рд┐рд╣рд┐рддреЗ.
CTE (WITH тАж) рдореНрд╣рдгрдЬреЗ рддреБрдореНрд╣реА рдЖрдзреА рдмрдирд╡рддрд╛ рдЖрдгрд┐ рдордЧ рд╡рд╛рдкрд░рддрд╛ рдЕрд╕рд╛ рдЫреЛрдЯрд╛ table, рдЬрд╕реЗ рдкрд╛рдирд╛рдЪреНрдпрд╛
рдХрдбреЗрд▓рд╛ рдХрдЪреНрдЪреЗ рдХрд╛рдо рдХрд░рдгреЗ. Subquery рдореНрд╣рдгрдЬреЗ рдкреНрд░рд╢реНрдирд╛рдЪреНрдпрд╛ рдЖрдд рдкреНрд░рд╢реНрди. View рдореНрд╣рдгрдЬреЗ
рддреБрдореНрд╣реА рдЬрддрди рдХрд░реВрди рдирд╛рд╡ рджрд┐рд▓реЗрд▓рд╛ рдкреНрд░рд╢реНрди.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
g["grades<br/>A+=10 ┬╖ A=9 ┬╖ B+=8 ┬╖ B=7 ┬╖ C=6"]
cte["WITH points AS (тАж)<br/>Katrina 20 ┬╖ Meera 18 ┬╖ Aishwarya 17 ┬╖ Dipika 15 ┬╖ Rohan 13"]
rank["RANK() OVER (PARTITION BY class ORDER BY pts DESC)<br/>3A: Katrina 1 ┬╖ Aishwarya 2 ┬╖ Dipika 3 тАФ 3B: Meera 1 ┬╖ Rohan 2"]
sub["WHERE pts > (SELECT AVG(pts)) = 16.6<br/>Katrina ┬╖ Meera ┬╖ Aishwarya"]
g --> cte --> rank
cte --> sub
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/database/lesson-diagrams.html#l14
тЭУ рдХрд╛рдп
- Subquery тАФ
WHERE pts > (SELECT AVG(pts) FROM points); рддрд╕реЗрдЪIN (SELECT тАж)рдЖрдгрд┐EXISTS (SELECT тАж). - CTE тАФ
WITH name AS (SELECT тАж) SELECT тАж FROM name: рд╡рд╛рдЪрд╛рдпрд▓рд╛ рд╕реЛрдкреНрдпрд╛ рдкрд╛рдпрд▒реНрдпрд╛, рдПрдХрд╛ query рдордзреНрдпреЗ рдкреБрдиреНрд╣рд╛ рд╡рд╛рдкрд░рддрд╛ рдпреЗрддрд╛рдд;WITH RECURSIVEрдЭрд╛рдбрд╛рдВрдордзреВрди рдлрд┐рд░рддреЛ (рд╡рд░реНрдЧ тЖТ рддреНрдпрд╛рдЪреНрдпрд╛ рддреБрдХрдбреНрдпрд╛ тЖТ рддреНрдпрд╛рдВрдЪреНрдпрд╛ рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреА). - Window functions тАФ
fn() OVER (PARTITION BY тАж ORDER BY тАж):ROW_NUMBER,RANK,DENSE_RANK, runningSUM, рд╢реЗрд╡рдЯрдЪреНрдпрд╛ N rows рд╡рд░AVG,LAG/LEAD(рдорд╛рдЧрдЪреА / рдкреБрдврдЪреА row),NTILE(quartiles). рдкреНрд░рддреНрдпреЗрдХ row рд░рд╛рд╣рддреЗ. - View тАФ
CREATE VIEW report_card AS SELECT тАж: table рд╕рд╛рд░рдЦрд╛ рд╡рд╛рдкрд░рд▓рд╛ рдЬрд╛рдгрд╛рд░рд╛ рдЬрддрди рдХреЗрд▓реЗрд▓рд╛ рдкреНрд░рд╢реНрди. Materialized view (Postgres) рдЙрддреНрддрд░ рд╕рд╛рдард╡рддреЛ рдЖрдгрд┐ рддреЛ refresh рдХрд░рд╛рд╡рд╛ рд▓рд╛рдЧрддреЛ. HAVINGGROUP BYрдирдВрддрд░ groups рдЧрд╛рд│рддреЛ (рдзрдбрд╛ 03),WHEREрдЖрдзреА rows рдЧрд╛рд│рддреЛ.
ЁЯзСтАНЁЯН│ рдЦреЛрд▓реАрдд рд░рд╛рд╣рдгрд╛рд░рд╛ code тАФ functions рдЖрдгрд┐ stored procedures
View рдкреНрд░рд╢реНрди рд╕рд╛рдард╡рддреЛ. Function SQL рдЪреНрдпрд╛ рдЖрдд рд╡рд╛рдкрд░рддрд╛ рдпреЗрдИрд▓ рдЕрд╢реА value рдкрд░рдд рджреЗрддреЛ
(SELECT points(grade) тАж). Stored procedure рдПрдХ рдХрд╛рдо рдХрд░рддреЗ тАФ рдЕрдиреЗрдХ statements, рдирд┐рдпрдо
рдЖрдгрд┐ writes тАФ рдЖрдгрд┐ рддреА рддреБрдореНрд╣реА CALL рдиреЗ рдЪрд╛рд▓рд╡рддрд╛. Trigger (рдзрдбрд╛ 18) row рдмрджрд▓рд▓реА рдХреА рдЖрдкреЛрдЖрдк
рдЪрд╛рд▓рддреЛ.
db/postgres/procedures.sql рд╣реА рдЦрд░реА PostgreSQL рдЖрд╡реГрддреНрддреА рдЖрд╣реЗ (PostgreSQL рдЪреНрдпрд╛ рд╕реНрд╡рддрдГрдЪреНрдпрд╛ parser рдиреЗ рддрдкрд╛рд╕рд▓реЗрд▓реА; рдХреЛрдгрддреНрдпрд╛рд╣реА Postgres 11+ рдордзреНрдпреЗ рдЪрд╛рд▓рд╡рд╛):
CREATE PROCEDURE transfer_pupil(p_student int, p_class int)
LANGUAGE plpgsql AS $$
BEGIN
IF (SELECT count(*) FROM students WHERE class_id = p_class) >= 3 THEN
RAISE EXCEPTION 'class % is full (3 pupils)', p_class;
END IF;
UPDATE students SET class_id = p_class WHERE id = p_student;
INSERT INTO audit_log (what) VALUES ('pupil ' || p_student || ' moved to class ' || p_class);
END
$$;
CALL transfer_pupil(4, 1); -- ERROR: class 1 is full (3 pupils)
CALL transfer_pupil(1, 2); -- CALL (and one audit_log row)
GRANT EXECUTE ON PROCEDURE transfer_pupil(int, int) TO school_api; -- the API may CALL it, not touch the tables
SQLite рдордзреНрдпреЗ stored procedures рдирд╛рд╣реАрдд тАФ рддреЗ рддреБрдордЪреНрдпрд╛ app рдЪреНрдпрд╛ рдЖрдд рдЪрд╛рд▓рддреЗ, рдореНрд╣рдгреВрди app рд╣реАрдЪ
procedure рдЖрд╣реЗ. python3 db/demo.py procs SQLite рдордзрд▓реЗ рд╕рд░реНрд╡рд╛рдд рдЬрд╡рд│рдЪреЗ рддреБрдХрдбреЗ рдЪрд╛рд▓рд╡рддреЗ:
conn.create_function("points", тАж) рдиреЗ рдиреЛрдВрджрд╡рд▓реЗрд▓реЗ рдЖрдгрд┐ SQL рдЪреНрдпрд╛ рдЖрдд рд╡рд╛рдкрд░рд▓реЗрд▓реЗ Python function, рдЖрдгрд┐
RAISE(ABORT, 'class is full (3 pupils)') рдЕрд╕рд▓реЗрд▓рд╛ BEFORE UPDATE trigger, рдЬреЛ рдХреЛрдгрддреНрдпрд╛рд╣реА app рд╕рд╛рдареА рдореАрд░рд╛рдЪреЗ рднрд░рд▓реЗрд▓реНрдпрд╛ 3A рдордзреНрдпреЗ рдЬрд╛рдгреЗ рдирд╛рдХрд╛рд░рддреЛ.
- тЬЕ рдкреНрд░рддреНрдпреЗрдХ app рдЖрдгрд┐ script рд╕рд╛рдареА рдПрдХрдЪ рдирд┐рдпрдо, data рд╢реЗрдЬрд╛рд░реАрдЪ рддрдкрд╛рд╕рд▓реЗрд▓рд╛, рдПрдХрд╛рдЪ round trip рдордзреНрдпреЗ
- тЬЕ tables рди рджреЗрддрд╛
GRANT EXECUTE(рдзрдбрд╛ 13) - тЭМ app рдЪреНрдпрд╛ code review, tests рдЖрдгрд┐ debugger рдкрд╛рд╕реВрди рд▓рдкрд▓реЗрд▓реЗ logic; рддреЗ migrations рд╕рд╣ deploy рдХрд░рд╛ (рдзрдбрд╛ 08)
- тЭМ vendor-specific (PL/pgSQL, T-SQL, MySQL рдЪреА рдмреЛрд▓реА) тАФ procedures рд▓рд╣рд╛рди рдЖрдгрд┐ рдХрдореА рдареЗрд╡рд╛
ЁЯдФ рдХрд╛
Reports, leaderboards, "рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧрд╛рддрд▓реЗ рдкрд╣рд┐рд▓реЗ 3", рдорд╣рд┐рдиреНрдпрд╛-рдорд╣рд┐рдиреНрдпрд╛рдЪрд╛ рдмрджрд▓ тАФ рд╣реЗ app рдордзрд▓реНрдпрд╛ loop code рдЪреНрдпрд╛ рдкрд╛рдирд╛рдВрдРрд╡рдЬреА рдЦреЛрд▓реАрддрд▓реА рдПрдХ query рдЕрд╕рддреЗ. SQL interview рдордзрд▓реЗ рд╣реЗрдЪ рд╕рд░реНрд╡рд╛рдд рд╕рд╛рдорд╛рдиреНрдп рдкреНрд░рд╢реНрдирд╣реА рдЖрд╣реЗрдд.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
db/demo.py рдордзрд▓реЗ window() тАФ рдЧреБрдг CTE рдЪреНрдпрд╛ рдЖрдд CASE рдиреЗ рдореЛрдЬрд▓реЗ рдЬрд╛рддрд╛рдд,
рдордЧ рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧрд╛рдд rank рдХреЗрд▓реЗ рдЬрд╛рддрд╛рдд, рд╕рд░рд╛рд╕рд░реАрд╢реА рддреБрд▓рдирд╛ рдХрд░реВрди рдЧрд╛рд│рд▓реЗ рдЬрд╛рддрд╛рдд, рдЖрдгрд┐ view рдореНрд╣рдгреВрди рдЬрддрди рд╣реЛрддрд╛рдд.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 db/demo.py window procs
python3 - <<'EOF'
import sqlite3
c = sqlite3.connect("db/school.db")
P = "CASE grade WHEN 'A+' THEN 10 WHEN 'A' THEN 9 WHEN 'B+' THEN 8 WHEN 'B' THEN 7 ELSE 6 END"
# every pupil's maths grade next to the one before it in the list (LAG) тАФ change maths to science
for row in c.execute(f"""SELECT s.name, g.grade, LAG(g.grade) OVER (ORDER BY {P} DESC) AS previous
FROM grades g JOIN students s ON s.id = g.student_id WHERE g.subject = 'maths'"""):
print(row)
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
('3A', 'Katrina', 20, 1, 20), ('3A', 'Aishwarya', 17, 2, 37), ('3A', 'Dipika', 15, 3, 52), рдордЧ
3B рдкреБрдиреНрд╣рд╛ rank 1 рдкрд╛рд╕реВрди рдореАрд░рд╛рд╕рд╣ рд╕реБрд░реВ; рд╕рд░рд╛рд╕рд░реА 16.6 рдЖрд╣реЗ рдЖрдгрд┐ рдлрдХреНрдд рдХрддрд░рд┐рдирд╛, рдореАрд░рд╛ рдЖрдгрд┐
рдРрд╢реНрд╡рд░реНрдпрд╛ рддрд┐рдЪреНрдпрд╛рдкреЗрдХреНрд╖рд╛ рд╡рд░ рдЖрд╣реЗрдд; view рддреЗрдЪ totals рдкрд░рдд рджреЗрддреЛ. рддреБрдордЪреНрдпрд╛ snippet рдордзреНрдпреЗ рдкрд╣рд┐рд▓рд╛ previous
None рдЖрд╣реЗ. procs points() рдиреЗ рдХрддрд░рд┐рдирд╛, рдореАрд░рд╛, рдРрд╢реНрд╡рд░реНрдпрд╛ рдпрд╛рдВрдирд╛ rank рдХрд░рддреЗ, Meera тЖТ 3A
class is full (3 pupils) рдореНрд╣рдгреВрди рдирд╛рдХрд╛рд░рддреЗ рдЖрдгрд┐ рдРрд╢реНрд╡рд░реНрдпрд╛рд▓рд╛ 3B рдордзреНрдпреЗ рд╣рд▓рд╡рддреЗ.
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рддреБрдореНрд╣реА groups рдЪреНрдпрд╛ рдЖрдд рдПрдХрд╣реА row рди рдЧрдорд╛рд╡рддрд╛ rows рдирд╛ rank рдХрд░реВ рд╢рдХрддрд╛, рдмреЗрд░реАрдЬ рдХрд░реВ рд╢рдХрддрд╛ рдЖрдгрд┐ рддреБрд▓рдирд╛ рдХрд░реВ рд╢рдХрддрд╛, рдЖрдгрд┐ рдПрдЦрд╛рджрд╛ рдкреНрд░рд╢реНрди рдЬрддрди рдХрд░реВ рд╢рдХрддрд╛ рдореНрд╣рдгрдЬреЗ рдкреНрд░рддреНрдпреЗрдХ app рддреЛ рдПрдХрд╛рдЪ рдкреНрд░рдХрд╛рд░реЗ рд╡рд┐рдЪрд╛рд░рддреЗ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- рдкреНрд░рддреНрдпреЗрдХ row рд╣рд╡реА рдЕрд╕рддрд╛рдирд╛
GROUP BYрд╡рд╛рдкрд░рдгреЗ тАФ window рд╡рд╛рдкрд░рд╛ PARTITION BYрд╡рд┐рд╕рд░рдгреЗ, рддреНрдпрд╛рдореБрд│реЗ rank рд╕рдВрдкреВрд░реНрдг рд╢рд╛рд│реЗрдд рдЪрд╛рд▓рддреЛ- рдмрд░реЛрдмрд░реАрдЪреНрдпрд╛ рд╡реЗрд│реА
RANKрд╡рд┐рд░реБрджреНрдзROW_NUMBER: RANK рджреЗрддреЛ 1, 1, 3; ROW_NUMBER рджреЗрддреЛ 1, 2, 3 - рдореЛрдареНрдпрд╛ table рд╡рд░ рдкреНрд░рддреНрдпреЗрдХ row рд╕рд╛рдареА рдПрдХрджрд╛ рдЪрд╛рд▓рдгрд╛рд░реА correlated subquery тАФ
EXPLAINрддрдкрд╛рд╕рд╛ - view рд▓рд╛ рд╡реЗрдЧ рд╡рд╛рдврд╡рдгрд╛рд░рд╛ рд╕рдордЬрдгреЗ: рд╕рд╛рдзрд╛ view рдкреНрд░рд╢реНрди рд╕рд╛рдард╡рддреЛ, рдЙрддреНрддрд░ рдирд╛рд╣реА
- рдПрдЦрд╛рджрд╛ business рдирд┐рдпрдо рдлрдХреНрдд рдПрдХрд╛рдЪ app рдордзреНрдпреЗ, рддрд░ scripts рдереЗрдЯ tables рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рддрд╛рдд
- рд╕рдЧрд│реЗ business logic procedures рдордзреНрдпреЗ рдЯрд╛рдХрдгреЗ тАФ test, review рдЖрдгрд┐ deploy рдХрд░рд╛рдпрд▓рд╛ рдХрдареАрдг
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: analytics pages, rankings рдЖрдгрд┐ billing reports CTEs рдЖрдгрд┐ windows рдиреЗ рд▓рд┐рд╣рд┐рд▓реЗ рдЬрд╛рддрд╛рдд; reviewers app-side loops рдРрд╡рдЬреА рд╣реЗрдЪ рдЕрдкреЗрдХреНрд╖рд┐рдд рдзрд░рддрд╛рдд.
тПня╕П рдкреБрдвреЗ
git checkout lesson-15-inside-the-engine тАФ commit рдЭрд╛рд▓реЗрд▓реА row рд╡реАрдЬ рдЧреЗрд▓реНрдпрд╛рд╡рд░рд╣реА рдХрд╛ рдЯрд┐рдХрддреЗ.