Целта на часа
Миналия час решихте какво правите. Днес в края на часа базата съществува, пълна е с данни и основните справки към нея връщат верни резултати. Без това следващият час няма върху какво да стъпи — AI функцията чете точно тази база.
| Готово | Как се проверява |
|---|---|
| Скрипт за създаване | изпълнява се два пъти подред без грешка на чиста база |
| Данни | минимумът от заданието е в таблиците; няма сираци по външните ключове |
| Основни справки | 3–5 заявки с JOIN и GROUP BY, всяка с ръчно сметнато очакване |
| Изглед | поне един, който ще е източник за AI функцията или за справка |
| Файлове | shema.sql, zaredi.py (или .sql), spravki.sql в споделената папка |
Създаване на таблиците
Схемата от заданието става скрипт за създаване — файл shema.sql, който прави таблиците от нулата. Той трябва да работи на чиста машина и да може да се пуска многократно, затова започва с DROP TABLE IF EXISTS в обратен ред на зависимостите.
PRAGMA foreign_keys = ON; -- само за SQLite
DROP TABLE IF EXISTS ai_zayavki;
DROP TABLE IF EXISTS zaemania;
DROP TABLE IF EXISTS knigi;
DROP TABLE IF EXISTS avtori;
CREATE TABLE avtori (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ime TEXT NOT NULL,
strana TEXT
);
CREATE TABLE knigi (
id INTEGER PRIMARY KEY AUTOINCREMENT,
zaglavie TEXT NOT NULL,
avtor_id INTEGER NOT NULL REFERENCES avtori(id) ON DELETE CASCADE,
godina INTEGER CHECK (godina BETWEEN 1800 AND 2026),
zhanr TEXT NOT NULL
);
CREATE TABLE zaemania (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kniga_id INTEGER NOT NULL REFERENCES knigi(id) ON DELETE CASCADE,
chitatel_kod TEXT NOT NULL, -- код, не име
ot_data TEXT NOT NULL,
do_data TEXT
);
CREATE TABLE ai_zayavki (
id INTEGER PRIMARY KEY AUTOINCREMENT,
vapros TEXT NOT NULL,
sql_text TEXT,
ok INTEGER NOT NULL DEFAULT 0,
sazdadeno TEXT NOT NULL DEFAULT (datetime('now'))
);NOT NULL,CHECKи външни ключове — базата да отхвърля грешните редове, преди тестовете да ги хванат.ON DELETE CASCADEтам, където дъщерните редове нямат смисъл без родителя (урок 1).- Лични данни не влизат: читателят е
chitatel_kod, не име и клас.
Данните: реални или синтетични
Реални данни имате, когато темата е ваша: каталогът на клубна библиотека, разписанието на училището, публичен набор от урок 9. Във всички останали случаи данните са синтетични — построени от вас така, че да приличат на истински. Това е нормално и се пише в документацията.
Правдоподобни синтетични данни
Стойности в реалистични граници, разпределени неравномерно както в живота (малко автори с много книги, много с по една), с връзки между таблиците, които се съгласуват (заемане на книга, която съществува, с дата на връщане след датата на вземане), и без нито един реален човек.
Два начина да ги построите
С Python и random — когато стойностите са числа, дати и избори от списък. С помощта на модела — когато трябват текстове: заглавия, описания, отзиви, обяви. Моделът връща JSON по схема (урок 20), а вие го проверявате и записвате.
from google import genai
from google.genai import types
from google.colab import userdata
import json
client = genai.Client(api_key=userdata.get("GEMINI_API_KEY"))
shema = {
"type": "array",
"items": {
"type": "object",
"properties": {
"zaglavie": {"type": "string"},
"avtor": {"type": "string"},
"godina": {"type": "integer"},
"zhanr": {"type": "string"},
},
"required": ["zaglavie", "avtor", "godina", "zhanr"],
},
}
otg = client.models.generate_content(
model="gemini-2.5-flash",
contents="Измисли 30 книги за училищна библиотека. Жанрове: роман, поезия, "
"фантастика, история. Години между 1950 и 2024. Автори — измислени "
"имена, не реални писатели. Заглавията да са различни.",
config=types.GenerateContentConfig(
temperature=1.0,
response_mime_type="application/json",
response_schema=shema,
),
)
knigi = json.loads(otg.text)
print(len(knigi), knigi[0])Защо се проверяват
Моделът измисля, а random не знае правилата ви. И двата произвеждат дубликати, стойности извън обхвата, книги без автор и връщане преди вземане. Проверката е няколко реда и се пуска преди INSERT:
ZHANROVE = {"роман", "поезия", "фантастика", "история"}
problemi = []
for k in knigi:
if not 1950 <= k["godina"] <= 2024:
problemi.append(("година извън обхвата", k["zaglavie"]))
if k["zhanr"] not in ZHANROVE:
problemi.append(("непознат жанр", k["zhanr"]))
zaglavia = [k["zaglavie"] for k in knigi]
if len(set(zaglavia)) != len(zaglavia):
problemi.append(("повтарящи се заглавия", len(zaglavia) - len(set(zaglavia))))
print("проблеми:", problemi or "няма")Основните справки
Три до пет заявки, които приложението или потребителят реално ще ползва. Поне една с JOIN, поне една с GROUP BY, и един изглед, който скрива съединението и е удобен източник за справка или за AI функцията.
-- 1. Кои книги са навън в момента (JOIN)
SELECT k.zaglavie, a.ime AS avtor, z.chitatel_kod, z.ot_data
FROM zaemania z
JOIN knigi k ON k.id = z.kniga_id
JOIN avtori a ON a.id = k.avtor_id
WHERE z.do_data IS NULL
ORDER BY z.ot_data;
-- 2. Колко книги има по жанр и коя е най-новата (GROUP BY)
SELECT zhanr, COUNT(*) AS broy, MAX(godina) AS nay_nova
FROM knigi
GROUP BY zhanr
ORDER BY broy DESC;
-- 3. Автори с повече от една книга (HAVING)
SELECT a.ime, COUNT(*) AS knigi
FROM avtori a JOIN knigi k ON k.avtor_id = a.id
GROUP BY a.id, a.ime
HAVING COUNT(*) > 1
ORDER BY knigi DESC, a.ime;
-- 4. Изглед — една „плоска“ таблица за справки и за модела
CREATE VIEW v_knigi AS
SELECT k.id, k.zaglavie, a.ime AS avtor, a.strana, k.godina, k.zhanr
FROM knigi k JOIN avtori a ON a.id = k.avtor_id;Тест на заявките с ръчно сметнати очаквания
Заявка, която „връща нещо“, не е проверена заявка. Проверена е тази, чийто резултат сте сметнали предварително на ръка върху малко данни и той съвпада. Методът е същият, с който в урок 5 хващахте грешките на AI асистента — и ще го ползвате пак в урок 30 за AI компонента.
- Изберете подмножество, което се обхваща с поглед: 5 книги, 3 автори, 4 заемания.
- За всяка справка сметнете на хартия какво трябва да излезе: брой редове и стойностите в 1–2 от тях.
- Пуснете заявката върху същите данни. Съвпада ли редът, броят, подредбата?
- Пробвайте граничен случай: жанр без книги, автор без книги, заемане без връщане.
- Запишете таблицата — тя влиза в документацията.
| Справка | Очаквано (на ръка) | Получено | ✓ |
|---|---|---|---|
| книги навън | 2 реда: „…“ и „…“ | 2 реда, същите | ✓ |
| по жанр | роман 3, история 1, поезия 1 | роман 3, история 1, поезия 1 | ✓ |
| автори с >1 книга | 1 ред (авторът с 3 книги) | 0 реда | ✗ — GROUP BY по грешна колона |
Какво трябва да запомниш
- Схемата живее в shema.sql — скрипт, който създава базата от нулата и може да се пуска многократно.
- Ограниченията NOT NULL, CHECK и външните ключове са първата проверка на данните.
- Синтетичните данни са нормални: правдоподобни граници, съгласувани връзки, без реални хора; винаги се проверяват преди INSERT.
- Основните справки: поне една с JOIN, една с GROUP BY и един изглед.
- Заявка е проверена само срещу ръчно сметнато очакване върху малки данни.
- В края на часа: база, данни, справки и трите файла в споделената папка.