Uploaded by Микола Сисюк

Lab#4

advertisement
Лабораторна робота №4
з курсу “ОБДЗ”
на тему:
Написання збережуваних програмних конструкцій
Мета роботи: Навчитися розробляти та виконувати збережені процедури та функції, механізми
використання транзакцій, тригерів для забезпечення цілісності значень, обмеження вводу даних,
забезпечення кардинальності для таблиць, автоматичної корекції введених даних засобами SQL.
Хід роботи.
Збережені процедури (stored procedures) - це програми, які зберігаються в базі даних і можуть бути
викликані для виконання певних операцій з даними в цій базі. Більшість реляційних баз даних
підтримують стандартний SQL для розробки збережених процедур. Можна використовувати SQL
для створення, виклику та виконання збережених процедур. Вони забезпечують можливість
збереження логіки на рівні бази даних та забезпечують більшу ефективність та керованість операцій
над даними.
1. Створити користувацькі функції для реалізації автоматизованих операцій над даними:
a. обчислення статистичних даних;
b. вибірка даних за ключем між таблицями.
2. Створити збережувані процедури:
a. параметризована вставка нових значень у таблиці;
b. реалізація зв’язку М:М між таблицями;
c. динамічний SQL з використанням курсорів.
3. Розробка та застосування транзакцій:
a. застосувати механізми використання транзакцій.
b. розроблення SQL запитів в рамках однієї транзакції
4. Розробка та застосування тригерів:
a. забезпечення цілісності значень;
b. обмеження вводу даних;
c. забезпечення кардинальності для таблиць;
d. автоматична корекція введених даних.
5. Підготувати звіт до лабораторної роботи та завантажити у ВНС предмету
https://vns.lpnu.ua/course/view.php?id=7897.
6. Захистити лабораторну роботу, продемонструвавши викладачу знання у розробці та
застосуванні користувацьких процедур, збережуваних процедур, транзакцій та тригерів БД.
Посилання на ресурси проектування та розробки БД:
•
•
•
•
•
•
•
•
•
•
•
https://www.dbdesigner.net/
https://dev.mysql.com/downloads/installer/
https://cloud.ibm.com/catalog/services/db2
https://dev.mysql.com/downloads/workbench/
https://www.microsoft.com/en-us/sql-server/sql-server-downloads
https://www.postgresql.org/download/
https://learn.microsoft.com/en-us/sql/?view=sql-server-ver16
https://www.mssqltips.com/
https://sqlzoo.net/
http://sqlfiddle.com/
https://sqliteonline.com/
Рекомендовані джерела:
•
•
•
•
•
•
•
•
•
•
https://www.w3schools.com/sql/
https://docs.microsoft.com/learn/
https://www.postgresqltutorial.com/
https://www.mysqltutorial.org/
Writing Functions and Stored Procedures in SQL Server (tutorialspoint.com)
Difference between Functions and Stored Procedures in SQL Server (tutorialsteacher.com)
How to Efficiently Generate Test Data With SQL | HackerNoon
How to generate random SQL Server test data using T-SQL (sqlshack.com)
Transactions in SQL Server for beginners (sqlshack.com)
How to implement error handling in SQL Server (sqlshack.com)
Додаток 1
Теоретичні відомості.
Більшість СУБД підтримують використання збережених послідовностей команд для
виконання часто повторюваних, однотипних дій над даними. Такі збережені процедури
дозволяють спростити оброблення даних, а також підвищити безпеку при роботі з базою
даних, оскільки в цьому випадку прикладні програми не потребують прямого доступу до
таблиць, а отримують потрібну інформацію через процедури.
СУБД MySQL підтримує збережені процедури і збережені функції. Аналогічно до
вбудованих функцій (типу COUNT), збережену функцію викликають з деякого виразу і вона
повертає цьому виразу обчислене значення. Збережену процедуру викликають за допомогою
команди CALL. Процедура повертає значення через вихідні параметри, або генерує набір
даних, який передається у прикладну програму.
Користувацькі функції SQL - це функції, які можна створити користувачами на базі SQL
(Structured Query Language) для використання в запитах та збережених процедурах. Ці функції
дозволяють вам створювати власні обчислення, перетворення даних та інші дії, які можуть бути
використані в SQL-запитах. Ось деякі основні аспекти користувацьких функцій SQL:
Створення користувацьких функцій:
В різних СУБД можуть бути різні способи створення користувацьких функцій. Зазвичай це
використання спеціальних операторів, таких як CREATE FUNCTION, які дозволяють вам визначити
ім'я функції, аргументи, тип результату та тіло функції.
Типи користувацьких функцій:
Існують два основних типи користувацьких функцій: скалярні та табличні.
Скалярні функції: Повертають одне значення, таке як число, рядок чи дата.
Табличні функції: Повертають результат у вигляді таблиці, яку можна використовувати як
тимчасову таблицю у запитах.
Синтаксис та використання:
Користувацькі функції можуть містити параметри, що передаються у функцію, а також
операції та логіку для обчислення результату. Вони можуть використовуватися у SELECT-запитах,
WHERE-умовах, збережених процедурах та інших місцях.
Синтаксис команд для створення збережених процедур описано нижче.
CREATE
[DEFINER = { користувач | CURRENT_USER }] FUNCTION
назва_функції ([параметри_функції ...])
RETURNS тип
[характеристика ...] тіло_функції
CREATE
[DEFINER = { користувач | CURRENT_USER }]
PROCEDURE назва_процедури ([параметри_процедури ...])
[характеристика ...] тіло_процедури
Аргументи:
DEFINER
Задає автора процедури чи функції. За замовчуванням – це CURRENT_USER.
RETURNS
Вказує тип значення, яке повертає функція.
тіло_функції, тіло_процедури
Послідовність директив SQL. В тілі процедур і функцій можна оголошувати локальні
змінні, використовувати директиви BEGIN ... END, CASE, цикли тощо. В тілі процедур
також можна виконувати транзакії. Тіло функції обов’язково повинно містити команду
RETURNі повертати значення.
параметри_процедури:
[ IN | OUT | INOUT ] ім’я_параметру тип
Параметр, позначений як IN, передає значення у процедуру. OUT-параметр передає
значення у точку виклику процедури. Параметр, позначений як INOUT, задається при
виклику, може бути змінений всередині процедури і зчитаний після її завершення. Типом
параметру може бути будь-який із типів даних, що підтримується MySQL.
параметри_функції:
ім’я_параметру тип
У випадку функцій параметри використовують лише для передачі значень у функцію.
При створенні процедур і функцій можна вказувати їхні додаткові характеристики.
характеристика:
LANGUAGE SQL
| [NOT] DETERMINISTIC
| {CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA}
| SQL SECURITY {DEFINER | INVOKER}
| COMMENT 'короткий опис процедури'
DETERMINISTIC
Вказує на те, що процедура обробляє дані строго визначеним (детермінованим) чином.
Тобто, залежно від вхідних даних, процедура повертає один і той самий результат.
Недетерміновані процедури містять функції типу NOW() або RAND(), і результат їх
виконання не можна передбачити. За замовчуванням всі процедури і функції є
недетермінованими.
CONTAINS SQL | NO SQL
Вказує на те, що процедура містить (за замовчуванням), або не містить директиви SQL.
READS SQL DATA
Вказує на те, що процедура містить директиви, які тільки зчитують дані з таблиць.
MODIFIES SQL DATA
Вказує на те, що процедура містить директиви, які можуть змінювати дані в таблицях.
SQL SECURITY
Задає рівень прав доступу, під яким буде виконуватись процедура. DEFINER – з правами
автора процедури (задано за замовчуванням), INVOKER – з правами користувача, який
викликає процедуру. Щоб запускати збережені процедури і функції, користувач повинен
мати права EXECUTE.
При створенні процедур і функцій у командному рядку клієнта MySQL, потрібно
перевизначити стандартний символ завершення вводу директив ";", щоб мати можливість
ввести всі директиви процедури. Це робиться за допомогою команди DELIMITER. Наприклад,
DELIMITER |
означає, що завершення вводу процедури буде позначатись символом "|".
Приклад створення скалярної функції (для SQL Server):
CREATE FUNCTION dbo.GetTotalOrders(@customerId INT)
RETURNS INT
AS
BEGIN
DECLARE @totalOrders INT;
SELECT @totalOrders = COUNT(*) FROM Orders WHERE CustomerId = @customerId;
RETURN @totalOrders;
END;
Приклад використання скалярної функції:
SELECT CustomerId, CustomerName, dbo.GetTotalOrders(CustomerId) AS TotalOrders
FROM Customers;
Приклад створення табличної функції (для SQL Server):
CREATE FUNCTION dbo.GetCustomerOrders(@customerId INT)
RETURNS TABLE
AS
RETURN (
SELECT * FROM Orders WHERE CustomerId = @customerId);
Приклад використання табличної функції:
SELECT * FROM dbo.GetCustomerOrders(123);
Користувацькі функції SQL дозволяють розширювати можливості SQL запитів та збережених процедур,
надаючи зручний спосіб виконання власних операцій над даними.
Нижче наведено синтаксис додаткових директив MySQL, які дозволяють розробляти
нескладні програми на мові SQL.
DECLARE назва_змінної тип_змінної
[DEFAULT значення_за_замовчуванням]
Оголошення змінної заданого типу.
SET назва_змінної = вираз
Присвоєння змінній значення.
IF умова THEN директиви
[ELSEIF умова THEN директиви] ... [ELSE
директиви2]
END IF
Умовний оператор. Якщо виконується вказана умова, то виконуються відповідні їй
директиви, в протилежному випадку виконуються директиви2.
CASE вираз
WHEN значення1 THEN директиви1
[WHEN значення2 THEN директиви2] ... [ELSE
директиви3]
END CASE
Оператор умовного вибору. Якщо вираз приймає значення1, виконуються директиви1,
якщо приймає значення2 – виконуються директиви2, і т.д. Якщо вираз не прийме
жодного зі значень, виконуються директиви3.
[мітка:] LOOP
директиви END
LOOP
Оператор безумовного циклу. Вихід з циклу виконується командою LEAVE мітка.
REPEAT
директиви
UNTIL умова
END REPEAT
WHILE умова DO
директиви
END WHILE
Оператори REPEAT і WHILE дозволяють організувати умовні цикли, які завершуються
при виконанні деякої умови.
Транзакція – це сукупність директив SQL, які виконуються як єдине ціле з можливістю
відміни результатів їх виконання. Зміни в таблицях записуються у базу даних лише після
успішного виконання всіх директив транзакції. Інакше, всі зроблені зміни ігноруються. Це
дозволяє уникати помилок при маніпулюванні великими обсягами записів, зберігати цілісність
даних при помилках під час додавання, видалення, модифікації значень у різних таблицях і
полях тощо. СУБД MySQL також підтримує глобальні розподілені транзакції, які виконуються
на декількох базах даних, або на різних серверах баз даних (ХА-транзакції).
Для організації транзакцій в MySQL використовують такі директиви, як SET autocommit,
STARTTRANSACTION, COMMITі ROLLBACK.
START TRANSACTION
Вказує на початок транзакції. Директива вимикає автоматичне збереження змін для
всіх подальших запитів, поки не буде виконано команду COMMIT, або ROLLBACK.
COMMIT
Зберегти зміни, зроблені даною транзакцією.
ROLLBACK
Відмінити дану транзакцію і зроблені нею зміни у базі даних. Слід зауважити, що зміни
у схемі бази даних не можна відмінити, тобто результат видалення, зміни або створення
таблиці завжди зберігається.
SET autocommit=0
Вимикає автоматичне збереження змін для поточної сесії зв’язку з сервером БД. За
замовчуванням, зміни зберігаються автоматично, тобто результат виконання запиту,
який змінює таблицю, одразу записується на диск без можливості відміни операції.
AND CHAIN
Одразу після завершення даної транзакції розпочати виконання наступної.
RELEASE
Одразу після виконання даної транзакції завершити поточну сесію зв’язку з сервером.
Транзакції можна розбивати на окремі логічні частини, оголошуючи так звані точки
збереження. Це дозволяє відміняти результати виконання не всієї транзакції, а лише тих
запитів, які виконувались після оголошеної точки збереження (SAVEPOINT).
SAVEPOINT мітка
Оголошує точку збереження всередині транзакції та задає її назву.
ROLLBACK TO [SAVEPOINT] мітка
Відміняє результати виконання запитів, вказаних після даної точки збереження.
RELEASE SAVEPOINT мітка
Видаляє точку збереження.
Тригер – це спеціальний вид користувацької процедури, який виконується автоматично
при певних діях над таблицею, наприклад, при додаванні чи оновленні даних. Кожен тригер
асоційований з конкретною таблицею і подією. Найчастіше тригери використовуються для
перевірки коректності вводу нових даних та підтримки складних обмежень цілісності. Крім
цього їх використовують для автоматичного обчислення значень полів таблиць, організації
перевірок для захисту даних, збирання статистики доступу до таблиць баз даних чи реєстрації
інших подій.
Для створення тригерів використовують директиву CREATE TRIGGER.
Синтаксис:
CREATE
[DEFINER = { користувач | CURRENT_USER }]
TRIGGER ім’я_тригера час_виконання подія_виконання
ON назва_таблиці FOR EACH ROW тіло_тригера
Аргументи:
DEFINER
Задає автора процедури чи функції. За замовчуванням – це CURRENT_USER.
ім’я_тригера
Ім’я тригера повинно бути унікальним в межах однієї бази даних.
час_виконання
Час виконання тригера відносно події виконання. BEFORE – виконати тіло тригера до
виконання події, AFTER– виконати тіло тригера після події.
подія_виконання
Можлива подія – це внесення (INSERT), оновлення (UPDATE), або видалення (DELETE)
рядка з таблиці. Один тригер може бути пов’язаний лише з однією подією. Команда
AFTER INSERT, AFTER UPDATE, AFTER DELETE визначає виконання тіла тригера відповідно
після внесення, оновлення, або видалення даних з таблиці. Команда BEFORE INSERT,
BEFORE UPDATE, BEFORE DELETE визначає виконання тіла тригера відповідно до
внесення, оновлення, або видалення даних з таблиці.
ON назва_таблиці
Таблиця, або віртуальна таблиця (VIEW), для якої створюється даний тригер. При
видалені таблиці з бази даних, автоматично видаляються всі пов’язані з нею тригери.
FOR EACH ROW тіло_тригера
Задає набір SQL директив, які виконує тригер. Тригер викликається і виконується для
кожного зміненого рядка. Директиви можуть об’єднуватись командами BEGIN … END та
містити спеціальні команди OLD та NEW для доступу до попереднього та нового значення
поля у зміненому рядку відповідно. В тілі тригера дозволено викликати збережені
процедури, але заборонено використовувати транзакції, оскільки тіло тригера
автоматично виконується як одна транзакція.
NEW.назва_поля
Повертає нове значення поля для зміненого рядка. Працює лише при подіях INSERT та
UPDATE. У тригерах, які виконуються перед (BEFORE) подією можна змінити нове
значення поля командою SET NEW.назва_поля = значення.
OLD.назва_поля
Повертає старе значення поля для зміненого рядка. Можна використовувати лише при
подіях UPDATEта DELETE. Змінити старе значення поля не можливо.
Щоб видалити створений тригер з бази даних, потрібно виконати команду
DROP TRIGGER назва_тригера.
Додаток 2
Приклад використання операцій SQL
Напишемо функції, які будуть обгортками стандартних функцій шифрування, та
процедуру, яка буде обчислювати кількість написаних автором повідомлень у кожній категорії
за вказаний проміжок часу.
1. Функції шифрування/дешифрування із заданим ключем.
CREATE FUNCTION mycms_encode (pass CHAR(48))
RETURNS TINYBLOB
RETURN AES_ENCRYPT(pass, 'key-key');
CREATE FUNCTION mycms_decode (pass TINYBLOB)
RETURNS CHAR(48)
RETURN AES_DECRYPT(pass, 'key-key');
2. Процедура повинна рахувати кількість повідомлень автора написаних за певний
проміжок часу у кожній з існуючих категорій. Для цього потрібно відібрати всі повідомлення
та їх категорії за автором та часом написання. Потім згрупувати вибрані повідомлення за
категоріями та порахувати кількість повідомлень. У процедуру потрібно передати ім’я автора,
а також першу і другу дату.
Перед основними директивами додамо перевірку коректності задання початкової і
кінцевої дати (IF date1<=date2 THEN…). Результати обчислень будуть записуватись у таблицю
Stats, яку процедура завжди очищує (командою TRUNCATE mycms.stats) і заповнює з нуля.
DELIMITER //
CREATE PROCEDURE mycms_count (IN name CHAR(19), IN date1 DATE, IN
date2 DATE)
BEGIN
DECLARE error CHAR;
SET error = 'Некоректно задані дати';
IF (date1<=date2) THEN BEGIN
CREATE TABLE IF NOT EXISTS mycms.stats (category CHAR(20), amount INT
UNSIGNED);
TRUNCATE mycms.stats;
INSERT INTO mycms.stats SELECT cname AS category,
COUNT(message.messageID) AS amount
FROM ((author INNER JOIN message)
INNER JOIN message_category) INNER JOIN category
ON author.login=name
AND author.authorID=message.authorID
AND message.messageID=message_category.messageID AND
message_category.categoryID=category.categoryID WHERE
message.posted BETWEEN date1 AND date2
GROUP BY category;
END;
ELSE SELECT error; END IF;
END//
DELIMITER ;
3. Після створення функцій і процедури перевіримо їх роботу:
SELECT login, mycms_decode(password) FROM author LIMIT 4;
CALL mycms_count('user1', '2008-01-01', '2009-05-05');
SELECT * FROM stats;
Результат роботи процедури – таблиця stats:
CALL mycms_count('user1', '2010-01-01', '2009-05-05');
Результат виклику процедури:
4. Відміна транзакції.
Транзакція складається з чотирьох запитів на додавання нових користувачів у
групу "ugroup" та "sugroup3". При цьому, групи "sugroup3" з roleID=9 в базі даних не
існує, а отже, транзакція не виконується.
START TRANSACTION;
INSERT INTO mycms.author VALUE (NULL, NULL, 'user7', 'u7pass', '2008-04-16',
'user7@kml.com', NULL, 2);
INSERT INTO mycms.author VALUE (NULL, NULL, 'user8', 'u8pass', '2009-04-22',
'user8@kml.com', NULL, 2);
INSERT INTO mycms.author VALUE (NULL,NULL, 'superuser4',
'suser4_pass', '2009-05-01', 'suser4@gl.com', NULL, 9);
INSERT INTO mycms.author VALUE (NULL,NULL, 'suser5', 'suuser5_pass',
'2009-05-01', 'suser5@gl.com', NULL, 9);
COMMIT;
Відповідь сервера:
#1452 - Cannot add or update a child row: a foreign key constraint fails
(`mycms/author`, CONSTRAINT `author_role` FOREIGN KEY (`roleID`) REFERENCES `role`
(`roleID`) ON DELETE NO ACTION ON UPDATE NO ACTION)
5. Успішна транзакція.
Транзакція складається з запитів на додавання тих самих користувачів у групу
"ugroup" та "sugroup3" після створення у таблиці Role групи "sugroup3".
INSERT INTO role
VALUE (NULL,'SUGroup3', 'read, write, changeown, groupadmin');
START TRANSACTION;
INSERT INTO mycms.author VALUE (NULL, NULL, 'user7', 'u7pass', '2008-04-16',
'user7@kml.com', NULL, 2);
INSERT INTO mycms.author VALUE (NULL, NULL, 'user8', 'u8pass',
'2009-04-22', 'user8@ml.com', NULL, 2);
INSERT INTO mycms.author VALUE (NULL,NULL, 'superuser4',
'suser4_pass', '2009-05-01', 'suser4@gl.com', NULL, 9);
INSERT INTO mycms.author VALUE (NULL,NULL, 'suser5',
'suuser5_pass', '2009-05-01', 'suser5@gl.com', NULL, 9);
COMMIT;
Результат успішного додавання чотирьох користувачів у таблицю показано
нижче:
SELECT * FROM author LIMIT 9, 5;
6. Каскадне оновлення таблиці користувачів при видаленні ролі з таблиці Role. Діюче
обмеження зовнішнього ключа при видалені ролі встановлює для користувача
невизначену роль (значення NULL). Натомість, за допомогою тригера, користувачеві
потрібно присвоювати певну роль за замовчуванням (роль Guests з roleID=4).
CREATE
TRIGGER role_delete BEFORE DELETE
ON mycms.role FOR EACH ROW
UPDATE mycms.author SET roleID=4 WHERE roleID=OLD.roleID;
Перевіримо роботу тригера, видаливши роль з номером 9:
DELETE FROM mycms.role WHERE roleID=9;
SELECT * FROM mycms.author LIMIT 10, 5;
7.
Створити
тригер, який буде шифрувати
функцією
AES_ENCRYPTперед тим як внести його у таблицю Author.
пароль користувача
CREATE
TRIGGER author_password BEFORE INSERT
ON mycms.author FOR EACH ROW
SET NEW.password = AES_ENCRYPT(NEW.password, 'key-key');
Перевіримо виконання тригера:
INSERT INTO mycms.author VALUES
(NULL, NULL, 'superuser6', 'suser6_pass', '2009-05-09', 'suser6@g.com',
NULL, 6),
(NULL, NULL, 'superser7', 'suser7_pass', '2009-05-09', 'suser7@g.com',
NULL, 6);
SELECT * FROM mycms.author LIMIT 14, 2;
8. У таблицю Session за допомогою тригера потрібно записувати службову інформацію
при кожному вході користувача у систему. Тригер буде фіксувати дату входу і
записувати її у таблицю користувачів.
Перед створенням тригера, створимо нове поле lastseen у таблиці Author.
ALTER TABLE mycms.author
ADD COLUMN lastseen DATE DEFAULT NULL;
CREATE TRIGGER user_lastseen AFTER INSERT ON
mycms.session FOR EACH ROW
UPDATE mycms.author SET author.lastseen=DATE(NEW.start)
WHERE author.authorID=NEW.authorID;
Перевіримо роботу тригера:
INSERT INTO session VALUES
('2weQ34rt', '3', '2009-05-09 00:01:01', NULL),
('314eqrtE', '4', '2009-05-10 22:01:51', NULL);
SELECT authorID, login, email, lastseen FROM author LIMIT 3;
Висновок: на цій лабораторній роботі я навчився розробляти та використовувати
збережені процедури і функції, ознайомився із механізмом транзакцій та було
розглянуто тригери, їх призначення, створення та використання у СУБД.
Download