Skip to content

Commit a485055

Browse files
author
ondrejbenes
authored
Merge pull request #3 from prusinsky/lesson_12
Lesson 12 - Databases
2 parents 49c6b8d + c9fbd68 commit a485055

File tree

4 files changed

+347
-0
lines changed

4 files changed

+347
-0
lines changed

lessons/beginners/database/index.md

+338
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
# Relační Databáze
2+
3+
Většina aplikací, zejména těch webových, potřebuje manipulovat s daty.
4+
Může se jednat třeba o správu objednávek, produktů na eshopu nebo bankovních transakcí.
5+
6+
Vzhledem k tomu, že databáze jsou velice komplexní téma, tak si v této lekci
7+
projedeme jen základy. Pokud vás ale databáze zaujmou, tak je dobré se podívat
8+
po dalších materiálech, například na [W3Schools](https://www.w3schools.com/sql/).
9+
10+
Nad daty provádíme čtyři druhy operací - vytváření, čtení, úpravu a mazání.
11+
Tyto operace se sdružují do zkratky **CRUD** (Create, Read, Update, Delete).
12+
13+
Existují různé možnosti, kam data ukládat. Mohli bychom například vše zapisovat
14+
do jednoho souboru. Brzy bychom ale zjistili, že se jedná o velice neefektivní
15+
způsob - operace nad daty by s rostoucím počtem záznamů začaly být pomalé.
16+
V těchto situacích nastupují na scénu databáze (resp. databázové systémy).
17+
18+
Jedná se o vysoce sofistikovaná řešení různých výrobců (zmínit můžeme třeba
19+
*Oracle* nebo *MySQL*). Výhodou je, že tyto systémy mají společný způsob,
20+
jakým se s nimi komunikuje (API) - prostřednictvím jazyka **SQL**. Nemusí nás tedy
21+
zajímat, jak databáze funguje uvnitř.
22+
23+
Základem relačních databází jsou **tabulky**. Můžeme si je představit jako jeden
24+
list sešitu MS Excel (celý sešit by pak odpovídal databázi). Jednotlivé řádky
25+
tabulky jsou **záznamy**. Ty se skládají z několika **atributů** (sloupců).
26+
Řádky a sloupce odpovídají Excelovým řádkům a sloupcům.
27+
28+
Můžeme mít například tabulku `OBJEDNAVKA`. Bude tvořena sloupci `ID_OBJEDNAVKY`,
29+
`ID_ZAKAZNIKA`, `DATUM`, `CENA`. Jednotlivé záznamy představují jednotlivé
30+
objednávky - víme tedy kdy si kdo udělal objednávku, a kolik za ni zaplatil.
31+
32+
## SQL
33+
SQL (Structured Querying Language - Strukturovaný dotazovací jazyk) nám umožňuje
34+
komunikovat s databází. Výrobci jednolivých databází obvykle jazyk v některých
35+
oblastech rozšiřují, základ je ale společný a standardzivaný. Kromě práci s daty
36+
(CRUD) se jazyk používá pro vytváření tabulek a dalších objektů, pro správu
37+
uživatelů databáze apod.
38+
39+
SQL příkazy mohou být do databáze posílány z webové aplikace (Python, Javascript, ...),
40+
z terminálu, nebo třeba z aplikace přímo určené pro správu databáze.
41+
Je zvykem jednotlivé příkazy jazyka psát `VELKÝMI PÍSMENY`. Příkazy oddělujeme
42+
středníkem `;`. Jednořádkové komentáře uvozujeme dvěma spojovníky `--`
43+
a víceřádkové komentáře píšeme mezi `/*` a `*/`.
44+
45+
Pojďme se podívat na některé z příkazů, které SQL podporuje.
46+
47+
### Tabulky
48+
Pro vytvoření tabulky použijeme příkaz `CREATE TABLE`, následovaný jménem tabulky
49+
a definicí jednotlivých sloupců. U sloupců definuje jméno, typ a případně další
50+
vlastnosti.
51+
52+
```sql
53+
/* Vytvoří tabulku ROBOT se sloupci ID, NAME a TYPE.
54+
ID je celé číslo, NAME a TYPE jsou řetězce.
55+
*/
56+
CREATE TABLE ROBOT (
57+
ID INT PRIMARY KEY,
58+
NAME TEXT,
59+
TYPE TEXT
60+
)
61+
```
62+
63+
Význam `PRIMARY_KEY` si vysvětlíme později. Pokud bychom chtěli tabulku smazat,
64+
tak použijeme `DROP TABLE JMENO_TABULKY`. Jen pozor - spolu s tabulkou se
65+
**smažou všechny záznamy** v ní obsažené.
66+
67+
### INSERT
68+
Jakmile máme vytvořenou tabulku, můžeme do ní vkládat záznamy. Děláme tak
69+
pomocí příkazu `INSERT`. Uvedeme jméno tabulky a pak hodnoty, představující
70+
záznam (nebo záznamy), které se mají vložit.
71+
72+
```sql
73+
-- Vloží do tabulky ROBOT nového Robota s ID 1, jménem Jim a typem AGGRESIVE
74+
INSERT INTO ROBOT (ID, NAME, TYPE) VALUES (1, "Jim", "AGGRESSIVE");
75+
76+
-- Vloží do tabulky ROBOT další dva Roboty
77+
INSERT INTO ROBOT (ID, NAME, TYPE) VALUES (2, "John", "DEFENSIVE"),(3, "Jack", "DEFENSIVE");
78+
```
79+
80+
### SELECT
81+
Když už máme i nějaké zaznámy, tak je dobré mít způsob, jak je číst. Slouží
82+
k tomu mocný příkaz `SELECT`, který v nejzákladnější podobě očekává seznam sloupců,
83+
který se má pro jednotlivé záznamy vypsat, a jméno tabulky, ze které se mají
84+
data číst. Seznam sloupců můžeme nahradit hvězdičkou `*`, pokud chceme vypsat
85+
hodnoty všech sloupců.
86+
87+
```sql
88+
-- vypíše hodnoty všech sloupců pro všechny roboty
89+
SELECT * FROM ROBOT;
90+
91+
-- vypíše jen jména všech robotů
92+
SELECT NAME FROM ROBOT;
93+
```
94+
95+
Příkaz `SELECT` podporuje třeba také filtrování dat (ukážeme si později),
96+
řazení (`ORDER BY`) a seskupování dat (průměr - `AVG`, suma - `SUM`, ...)
97+
98+
### UPDATE
99+
V případě, že chceme data upravit (například změnit jméno robota),
100+
tak použijeme příkaz `UPDATE`. I ten očekává jméno tabulky, jejíž záznamy se
101+
mají upravit. Kromě toho také zadáme nové hodnoty pro jednotlivé sloupce.
102+
Hodnoty sloupců, které neuvedeme, zůstanou nezměněné.
103+
104+
```sql
105+
-- nastaví všem robotům typ na AGGRESSIVE
106+
UPDATE ROBOT SET TYPE = "AGGRESSIVE";
107+
```
108+
109+
### DELETE
110+
A konečně poslední oprací v CRUD je mazání. V SQL se záznamy mažou pomocí příkazu
111+
`DELETE`. I zde, stejně jako u ostatních CRUD příkazů, uvádíme jméno tabulky.
112+
113+
```sql
114+
-- smaže všechny roboty
115+
DELETE FROM ROBOT;
116+
```
117+
118+
### WHERE
119+
Nejspíš vás napadalo, že pokud bychom vždy četli, upravovali nebo mazali všechna
120+
data v tabulce, tak by systém nebyl moc dobře použitelný. Naštěstí ale můžeme
121+
pomocí příkazu `WHERE` ovlivnit, nad jakými záznamy se bude operace provádět.
122+
123+
Za `WHERE` se píší podmínky (spojované pomocí `AND` nebo `OR`), které musí
124+
platit, aby se záznam přidal do množiny, nad kterou se bude operace
125+
provádět. V podmínkách je možné použít různé operátory:
126+
127+
- rovnost: `WHERE SLOUPEC = HODNOTA`
128+
- nerovnost: `WHERE SLOUPEC > HODNOTA` (`>`, `<`, `<>`, ...)
129+
- jedna z hodnot: `WHERE SLOUPEC IN (HODNOTA_1, HODNOTA_2)`
130+
- podřetězec: `WHERE SLOUPEC LIKE %HODNOTA%`
131+
132+
```sql
133+
-- vypíše všechny řádky o robotech, které mají typ AGGRESSIVE
134+
SELECT * FROM ROBOT WHERE TYPE = "AGGRESSIVE";
135+
136+
-- přejmenuje roboty, jejichž jména začínají na J, na Jimmy
137+
UPDATE ROBOT SET NAME = "Jimmy" WHERE NAME LIKE "J%";
138+
139+
-- smaže robota s ID větším než 1
140+
DELETE FROM ROBOT WHERE ID > 1;
141+
```
142+
143+
## Primární klíče
144+
Každá tabulka by měla mít soupec, který jednoznačně identifikuje jednotlivé řádky.
145+
Ve sloupci musí být unikátní hodnoty. Může se jednat o uměle vytvořené číslo
146+
(nejčastěji nazývané `ID`), nebo může jít o unikátní identifikátor z reálného
147+
světa. Pozor ale na to, že ne každý na první pohled unikátní identifikátor je
148+
*skutečně* unikátní - je například možné, aby dvě osoby měly stejné rodné číslo.
149+
Je tedy obecně lepší používat uměle vytvořené primární klíče.
150+
151+
Primární klíče se používají proto, že zrychlují operace nad daty (rychle se podle
152+
nich vyhledává) a také proto, že snižují riziko duplicitních dat v databázi.
153+
Při vytváření tabulky zadáme u sloupce, který má být primárním klíčem, vlastnost
154+
`PRIMARY KEY`.
155+
156+
## Cizí klíče
157+
Cizí klíče - `FOREIGN KEY` - se využívají pro zachycení vazby mezi tabulkami.
158+
V dceřiné tabulce máme sloupec představující cizí klíč, jehož hodnoty se
159+
odkazují na primární klíč jiné tabulky.
160+
161+
Například tabulka `OBJEDNAVKA` má cizí klíč `ID_ZAKAZNIKA`. Tento cizí klíč
162+
se odkazuje na primární klíč tabulky `ZAKAZNIK`. Tím zaručíme, že každá objednávka
163+
musí být "napojená" na existujícího zákazníka.
164+
165+
{{ figure(
166+
img=static('fk.png'),
167+
alt="Cizí klíče"
168+
) }}
169+
170+
[zdroj obrázku](https://cdn-images-1.medium.com/max/1600/1*yW_ha3z8Mp6fUn9m6qWwNw.png)
171+
172+
## Spojování tabulek
173+
Často potřebujeme v jednom dotazu číst data z více tabulek. Používáme k tomu
174+
příkaz `JOIN`, který je součástí příkazu `SELECT`. V základní verzi vezme
175+
`JOIN` záznamy z jedné tabulky a připojí k nim odpovídající záznamy z druhé
176+
tabulky. Na takto nově vzniklý řádek je možné udělat `JOIN` s další tabulkou.
177+
178+
To, co považujeme za "odpovídající záznam" zapíšeme jako součást `JOIN`
179+
(za klíčové slovo `ON`). Nejdříve ale musíme zadat jméno tabulky,
180+
kterou chceme připojit.
181+
182+
```sql
183+
-- vypíše jména a příjmení zákazníků, kteří udělali objednávku za více než 500 Kč
184+
185+
SELECT ZAKAZNIK.JMENO, ZAKAZNIK.PRIJMENI
186+
FROM ZAKAZNIK
187+
JOIN OBJEDNAVKA ON OBJEDNAVKA.ZAKAZNIK_ID = ZAKAZNIK.ID
188+
WHERE OBJEDNAVKA.CENA > 500;
189+
```
190+
191+
## Transakce
192+
Transakce jsou skupiny příkazů (`SELECT`, `INSERT`, ...), která se buď provede
193+
celá, nebo vůbec. To má velkou výhodu v tom, že pokud některý z příkazů vyvolá
194+
chybu, tak se databáze vrátí do původního stavu, my můžeme příkaz opravit
195+
a celou transakci spustit znovu.
196+
197+
O transakcích se ale bavíme především proto, že je důležité je **ukončit**,
198+
a to buď příkazem `COMMIT` nebo `ROLLBACK`. `COMMIT` uloží námi provedené
199+
změny trvale do databáze, takže je uvidíme i v budoucích transakcích.
200+
`ROLLBACK` vrátí databázi do původního stavu, což se může hodit v případě,
201+
že jsme provedli jiné změny, než jsme chtěli.
202+
203+
```sql
204+
-- vložení dat
205+
INSERT INTO ROBOT (ID, NAME, TYPE) VALUES(1, "Jim", "AGGRESSIVE");
206+
-- v této chvíli ještě nejsou data trvale uložena
207+
208+
-- uložení dat
209+
COMMIT;
210+
```
211+
212+
## SQLite
213+
Celé povídání o databázích by nemělo moc smysl, pokud bychom si neukázali,
214+
jak je využít z prostředí Pythonu. Jak jsme už zmínili, existují různé databázové
215+
systémy, my se ale zaměříme na jeden z nejjednodušších na "rozjetí", a to **SQLite**.
216+
217+
Tento systém ukládá celou databázi do jednoho binárního souboru.
218+
Můžeme si na něm jednoduše vyzkoušet jednotlivé SQL příkazy,
219+
a to prostřednictvím Python balíčku `sqlite3`. Ukažme si využití tohoto balíčku
220+
na příkladech.
221+
222+
```sql
223+
import sqlite3
224+
225+
# Připojíme se k databázi (v souboru)
226+
connection = sqlite3.connect('pyladies_example_1.db')
227+
228+
# Získáme instanci třídy `Cursor`, pomocí které bude do databáze posílat příkazy
229+
cursor = connection.cursor()
230+
231+
# Pokud tabulka už existuje, tak ji odstraníme,
232+
# abychom mohli skript spouštět opakovaně
233+
cursor.execute("""DROP TABLE IF EXISTS ROBOT""")
234+
235+
# Vytvoříme jednoduchou tabulku
236+
cursor.execute("""CREATE TABLE ROBOT (NAME TEXT, TYPE TEXT)""")
237+
238+
# Vložíme do tabulky data
239+
cursor.execute("""
240+
INSERT INTO ROBOT (NAME, TYPE)
241+
VALUES ("JIM", "DEFENSIVE"), ("JACK", "OFFENSIVE")
242+
""")
243+
244+
# Dotážeme se na všechny roboty, výsledky vypíšeme
245+
robots = cursor.execute("SELECT * FROM ROBOT")
246+
for robot in robots:
247+
print(robot)
248+
249+
# Uložíme změny a uzavřeme spojení
250+
connection.commit()
251+
connection.close()
252+
```
253+
254+
```sql
255+
# Složitejší příklad, který pracuje s primárními a cizími klíči
256+
# a se spojováním tabulek
257+
258+
import sqlite3
259+
260+
# Připojíme se k databázi (v souboru)
261+
connection = sqlite3.connect('pyladies_example_2.db')
262+
263+
# Získáme instanci třídy `Cursor`, pomocí které bude do databáze posílat příkazy
264+
cursor = connection.cursor()
265+
266+
# Pokud tabulky už existují, tak ji odstraníme,
267+
# abychom mohli skript spouštět opakovaně
268+
cursor.execute("""DROP TABLE IF EXISTS ROBOT""")
269+
cursor.execute("""DROP TABLE IF EXISTS BATTLE""")
270+
271+
# Vytvoříme tabulku s roboty a tabulky s výsledky bitev
272+
cursor.execute("""
273+
-- u jednotlivých roborů si ukládáme ID, jméno a typ
274+
CREATE TABLE ROBOT (
275+
ROBOT_ID INT PRIMARY KEY,
276+
NAME TEXT,
277+
TYPE TEXT)
278+
""")
279+
280+
cursor.execute("""
281+
-- bitva se skládá z ID bitvy, ID vítěze a poraženého (odpovídají ID v tabulce ROBOT)
282+
-- a z bodů pro vítěze a poraženého
283+
CREATE TABLE BATTLE (
284+
BATTLE_ID INT PRIMARY KEY,
285+
WINNER_ID INT,
286+
LOSER_ID INT,
287+
WINNER_POINTS INT,
288+
LOSER_POINTS INT,
289+
FOREIGN KEY(WINNER_ID) REFERENCES ROBOT(ROBOT_ID),
290+
FOREIGN KEY(LOSER_ID) REFERENCES ROBOT(ROBOT_ID)
291+
)
292+
""")
293+
294+
# Vložíme do tabulkek data
295+
cursor.execute("""
296+
INSERT INTO ROBOT (ROBOT_ID, NAME, TYPE) VALUES
297+
(1, "JIM", "DEFENSIVE"), (2, "JACK", "OFFENSIVE"), (3, "JIMMY", "OFFESIVE")
298+
""")
299+
300+
cursor.execute("""
301+
INSERT INTO BATTLE (BATTLE_ID, WINNER_ID, LOSER_ID, WINNER_POINTS, LOSER_POINTS) VALUES
302+
(1, 1, 2, 10, 8), -- robot 1 porazil robota 2 se skóre 10:8 (v bitvě 1)
303+
(2, 2, 1, 6, 9),
304+
(3, 2, 3, 10, 9),
305+
(4, 1, 3, 5, 4),
306+
(5, 3, 2, 2, 0),
307+
(6, 1, 2, 9, 6)
308+
""")
309+
310+
# Dotážeme se na výsledky bitev, které vyhrál robot se jménem "JIM"
311+
scores = cursor.execute("""
312+
SELECT BATTLE.WINNER_POINTS, BATTLE.LOSER_POINTS
313+
FROM BATTLE
314+
JOIN ROBOT ON ROBOT.ROBOT_ID = BATTLE.WINNER_ID
315+
WHERE ROBOT.NAME = "JIM"
316+
""")
317+
318+
for score in scores:
319+
print(score)
320+
321+
# Uložíme změny a uzavřeme spojení
322+
connection.commit()
323+
connection.close()
324+
```
325+
326+
## ORM
327+
V Pythonu jsme se naučili data a logiku sdružovat do **tříd**. V databázích
328+
se data sdružují do **tabulek**. O propojení těchto konceptů se stará ORM - Objektově
329+
Relační Mapování. Pomocí ORM Frameworku (v Pythonu např.
330+
[SQLAlchemy](https://en.wikipedia.org/wiki/SQLAlchemy)) vytváříme Python třídy,
331+
pro které existují odpovídající tabulky v databázi.
332+
333+
Například Python třída `Kocka` bude mít odpovídající tabulku `KOCKA`.
334+
Atributy třídy (`Vek`, `Barva`) budou v databázi existovat jako sloupce.
335+
Jednotlivé řádky tabulky bude možné načíst do aplikace jako instance třídy `Kocka`.
336+
Jednotlivé kočky samozřejmě bude možné upravovat, mazat, nebo vytvářet nové.
337+
338+
Ukázku ORM najdete na [Wikipedii](https://en.wikipedia.org/wiki/SQLAlchemy#Schema_definition)

lessons/beginners/database/info.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
title: Úvod do Relačních databází
2+
style: md
3+
attribution:
4+
- Pro PyLadies Brno napsal Ondřej Beneš, 2019.
5+
license: cc-by-sa-40
163 KB
Loading

runs/2019/brno-jaro-2019-ut/info.yml

+4
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ plan:
143143
- title: HTTP a API
144144
slug: github-api
145145
date: 2019-05-14
146+
materials:
147+
- lesson: projects/github-api
148+
- lesson: fast-track/http
149+
- lesson: beginners/database
146150

147151
- title: Závěrečné projekty - zadání
148152
slug: asteroids

0 commit comments

Comments
 (0)