Модуль 17

Functions and Basic Stored Procedures

Ось готовий урок, створений спеціально для тебе. Уяви, що ми зараз у великій аудиторії Сандерс-театру в Гарварді (або просто зідзвонилися в Zoom з кавою), і я намагаюся пояснити тобі, як перестати писати один і той самий код знову і знову.

Поїхали! 🚀


🎓 Урок: Functions and Basic Stored Procedures

(Або як навчити базу даних робити роботу за вас)


1. 🔥 Вступ: проблема та мотивація

Уявіть, що ви прийшли в кав'ярню. Ви хочете лате. Що ви кажете баристі? Ви кажете: "Одне лате, будь ласка".

Ви НЕ кажете:

"Візьми 18 грамів зерен, змели їх до фракції еспресо, спресуй темпером із зусиллям 20 кг, встав холдер у групу, пролий воду під тиском 9 бар протягом 25 секунд, потім візьми молоко 3.2% жирності, спінь його парою до температури 65 градусів і влий у чашку під кутом 45 градусів".

Чому? Тому що це довго, ви можете помилитися в інструкції, і бариста вас зненавидить. Ви використовуєте абстракцію — коротку команду "Лате", за якою ховається складний процес.

А тепер подивіться на свій код (або SQL-запити). Ви часто пишете щось таке: SELECT price * 0.20 FROM orders WHERE id = 123; (щоб порахувати ПДВ).

А потім знову: SELECT price * 0.20 FROM orders WHERE id = 124;

А що, якщо податок зміниться на 22%? Вам доведеться бігати по всьому коду і змінювати 0.20 на 0.22 у сотні місць. Це пекло. Це шлях до помилок.

Риторичне питання: Чи не було б круто мати кнопку "Порахуй податок" або "Зареєструй користувача" прямо в базі даних, щоб ми могли викликати її однією командою?

Спойлер: Так, це і є Функції та Збережені процедури. Без них серйозні проєкти перетворюються на смітник коду, який неможливо підтримувати.


2. 🧠 Теоретична база (без сухої академічності)

Давайте розкладемо це по поличках.

Що таке Функція (Function)? 🧮

Уявіть її як розумний калькулятор. * Вхід: Ви кидаєте туди дані (аргументи). * Всередині: Відбувається якась магія (обчислення). * Вихід: Вона обов'язково повертає вам одне значення (або таблицю).

Головне правило: Функції зазвичай використовуються всередині запиту (SELECT, WHERE). Вони, як правило, не змінюють дані в базі (хоча можуть), а просто обробляють їх.

Що таке Збережена процедура (Stored Procedure)? 📜

Це сценарій дій. Уявіть її як менеджера складу. Ви кажете: "Прийми товар". Менеджер йде, відкриває ворота, перевіряє накладну, оновлює залишки, закриває ворота. * Вхід: Параметри. * Всередині: Виконання серії команд (INSERT, UPDATE, DELETE, логіка IF/ELSE). * Вихід: Може нічого не повертати. Її мета — зробити дію, змінити стан системи.

Як це працює "під капотом"?

Коли ви надсилаєте звичайний SQL-запит, база даних щоразу робить три речі: 1. Парсинг: Перевіряє синтаксис (чи не забули ви крапку з комою). 2. Планування: Вирішує, як найшвидше знайти дані. 3. Виконання.

Функції та процедури зберігаються в базі вже в компільованому (підготовленому) вигляді. Ви не витрачаєте час на парсинг. Ви просто кажете: "Виконуй!". Це швидше.


3. 🧪 Приклади (від простого до реального)

(Приклади базуються на синтаксисі PostgreSQL/SQL, оскільки це золотий стандарт для навчання, але логіка універсальна).

Приклад 1: Проста функція (Порахуємо знижку)

Припустімо, у нас чорна п'ятниця. Ми хочемо знати фінальну ціну.

CREATE OR REPLACE FUNCTION get_discounted_price(price DECIMAL, discount DECIMAL)
RETURNS DECIMAL AS $$
BEGIN
    RETURN price - (price * discount);
END;
$$ LANGUAGE plpgsql;

Як ми це використовуємо? Замість формул ми пишемо:

SELECT product_name, get_discounted_price(price, 0.20)
FROM products;

Бачите? Чисто, красиво, зрозуміло.

Приклад 2: Збережена процедура (Реєстрація нового юзера)

Тут складніше. Нам треба додати юзера, але тільки якщо такого емейлу ще немає.

Чого ми очікуємо? Процедура має прийняти ім'я та емейл, перевірити базу і, якщо все ок, вставити рядок.

CREATE OR REPLACE PROCEDURE register_user(u_name TEXT, u_email TEXT)
LANGUAGE plpgsql
AS $$
BEGIN
    -- Перевірка: чи існує вже такий емейл?
    IF EXISTS (SELECT 1 FROM users WHERE email = u_email) THEN
        RAISE NOTICE 'Користувач з таким емейлом вже існує!';
        RETURN;
    END IF;

    -- Якщо ні — додаємо
    INSERT INTO users (username, email, created_at)
    VALUES (u_name, u_email, NOW());

    RAISE NOTICE 'Користувача % успішно створено!', u_name;
END;
$$;

Як викликати?

CALL register_user('David Malan', 'malan@harvard.edu');

Чому це круто? Тому що логіка перевірки "чи існує юзер" лежить у базі. Бекенд-розробнику (Python, JS, C#) не треба про це думати. Він просто викликає процедуру.


4. 🛠 Практична частина

Прийшов час забруднити руки кодом. Ось твій "Gym Set" на сьогодні:

Завдання 1: Hello World Напиши просту функцію say_hello(name), яка приймає ім'я і повертає рядок "Привіт, [ім'я]!". Перевір її через SELECT.

Завдання 2: Логіка віку Є таблиця students з полем birth_date. Напиши функцію is_adult(birth_date), яка повертає TRUE, якщо студенту є 18 років, і FALSE, якщо немає.

Завдання 3: Виправ помилку (Debug) Уяви, що є процедура update_balance(user_id, amount). Новачок написав її так:

UPDATE wallets SET balance = amount; 

Що станеться, якщо запустити цю процедуру? (Підказка: це катастрофа). Виправ код так, щоб баланс оновлювався тільки в конкретного користувача і додавався до існуючого, а не перезаписував його.

Завдання 4: Міні-кейс (Банківський переказ) Напиши процедуру transfer_money(sender_id, receiver_id, amount). Вона повинна: 1. Відняти гроші у відправника. 2. Додати гроші отримувачу. Бонус: Додай перевірку, чи вистачає у відправника грошей перед транзакцією.

Питання "А що, якщо...": Що, якщо ми змінимо логіку функції get_discounted_price (Приклад 1) прямо в базі даних? Чи потрібно нам перезапускати або перекомпілювати наш веб-сайт (бекенд)?


5. 💡 Мислення як у розробника

Ось що відрізняє новачка від профі в цій темі.

  1. Де тримати логіку? (The Eternal War)

    • Новачок: Пхає всю бізнес-логіку (надсилання емейлів, складні розрахунки) в базу даних.
    • Профі: Знає, що база даних має займатися даними. Складну логіку краще тримати в коді програми (Python/Java). У базі — тільки те, що стосується цілісності даних.
    • Порада: Функції для валідації даних — супер. Функції для відправки JSON на зовнішній API — погана ідея.
  2. Безпека (SQL Injection) Використання процедур і параметрів (як u_email у прикладі вище) автоматично захищає вас від хакерів, які намагаються вставити шкідливий код. Це ваш щит.

  3. Іменування Називайте функції дієсловами: calculate_tax, get_user_status. Якщо ваша функція називається f1 або do_stuff, через місяць ви самі не згадаєте, що вона робить.


6. 🧩 Підсумок

Отже, що ми сьогодні зробили? Ми взяли складні, повторювані шматки SQL-коду і запакували їх у зручні "чорні скриньки".

  • Функції — щоб отримувати відповіді (обчислення).
  • Процедури — щоб виконувати дії (бізнес-процеси).

Тепер ви не просто пишете запити, ви будуєте API всередині бази даних. Ви робите життя простішим для себе і безпечнішим для проєкту.

👀 Тизер наступного уроку: Уяви, що ти хочеш, щоб база даних сама реагувала на події. Наприклад: "Якщо хтось змінив зарплату співробітника, автоматично запиши це в секретний журнал аудиту". Без твого втручання. Звучить як магія? Це називається Тригери (Triggers). І про це ми поговоримо наступного разу.

А поки що — вперед до практики! Це саме те місце, де народжується розуміння. 💻