Wstęp
Wyzwalacze, funkcje i procedury składowane to potężne narzędzia w MySQL, które pozwalają na automatyzację zadań, zwiększenie wydajności i utrzymanie integralności danych. W tej lekcji podsumujemy kluczowe koncepcje i zastosowania tych elementów.
1. Wyzwalacze (Triggers)
Oczywiście, rozbudujmy każdą sekcję o dodatkowe przykłady, które pomogą zrozumieć koncepcje, ale nie będą bezpośrednio kopiowalne do rozwiązania zadań.
1. Wyzwalacze (Triggers)
- Definicja:
- Wyzwalacze to automatyczne reakcje bazy danych na zmiany w danych. Wyobraź sobie, że chcesz śledzić, kto i kiedy modyfikował dane w tabeli. Wyzwalacz może automatycznie zapisywać te informacje w oddzielnej tabeli logów.
- Przykład: W systemie e-commerce, po każdej zmianie statusu zamówienia, wyzwalacz może wysyłać powiadomienie e-mail do klienta.
- Typy wyzwalaczy:
BEFORE
:- Użyj, gdy chcesz sprawdzić lub zmodyfikować dane przed ich zapisaniem. Na przykład, możesz chcieć upewnić się, że wprowadzana cena produktu nie jest ujemna.
- Przykład: Wyzwalacz, który automatycznie konwertuje wszystkie wprowadzane nazwy produktów na wielkie litery.
AFTER
:- Użyj, gdy chcesz wykonać operacje po zapisaniu danych. Na przykład, po dodaniu nowego klienta, możesz chcieć automatycznie utworzyć dla niego konto w systemie lojalnościowym.
- Przykład: Po usunięciu rekordu z tabeli zamówień, wyzwalacz aktualizuje tabele statystyk sprzedażowych.
- Zastosowania:
- Logowanie zmian: Rejestruj, kto, co i kiedy zmienił w bazie danych.
- Sprawdzanie poprawności: Upewnij się, że dane spełniają określone kryteria.
- Automatyczne aktualizowanie: Aktualizuj powiązane tabele, gdy dane w jednej tabeli się zmieniają.
- Przykład: Wyzwalacz który sprawdza czy wprowadzany numer telefonu ma poprawny format.
- Zmienne:
NEW
:- Użyj, aby uzyskać dostęp do nowych wartości wstawianych lub aktualizowanych rekordów.
- Przykład:
NEW.cena
w wyzwalaczuBEFORE UPDATE
pozwala sprawdzić, czy nowa cena produktu jest akceptowalna.
OLD
:- Użyj, aby uzyskać dostęp do starych wartości rekordów aktualizowanych lub usuwanych.
- Przykład:
OLD.status
w wyzwalaczuAFTER UPDATE
pozwala sprawdzić, jaki był poprzedni status zamówienia.
Zadania - Wyzwalacze
- Zadanie 1: Utwórz wyzwalacz
AFTER UPDATE
na tabeliprodukty
, który po każdej zmianie ceny produktu zapisze informację o starej i nowej cenie w tabelihistoria_cen
. - Zadanie 2: Stwórz wyzwalacz
BEFORE DELETE
na tabelizamowienia
, który przed usunięciem zamówienia sprawdzi, czy zamówienie ma status "Zrealizowane", a jeśli nie, uniemożliwi usunięcie. - Zadanie 3: Napisz wyzwalacz
AFTER INSERT
na tabeliklienci
, który automatycznie wygeneruje unikalny kod klienta i zapisze go w kolumniekod_klienta
.
2. Funkcje Składowane
- Definicja:
- Funkcje składowane to bloki kodu, które wykonują obliczenia i zwracają pojedynczą wartość.
- Przykład: Funkcja, która oblicza wiek klienta na podstawie jego daty urodzenia.
- Cechy:
- Muszą zwracać wartość:
RETURNS typ_danych
. - Mogą być używane w zapytaniach SQL:
SELECT funkcja(parametry);
. - Zazwyczaj nie modyfikują danych:
DETERMINISTIC
. - Przykład: Funkcja która oblicza podatek od produktu na podstawie jego ceny.
- Muszą zwracać wartość:
- Zastosowania:
- Obliczanie wartości: Obliczaj złożone wartości na podstawie danych w tabelach.
- Formatowanie danych: Konwertuj dane na określony format.
- Sprawdzanie poprawności: Sprawdzaj, czy dane spełniają określone kryteria.
- Przykład: Funkcja która zwraca tekstowy opis poziomu zapasów produktu (niski, średni, wysoki) na podstawie liczby sztuk w magazynie.
Zadania - Funkcje Składowane
- Zadanie 1: Utwórz funkcję
liczba_produktow_klienta
, która przyjmujeklient_id
i zwraca liczbę produktów zakupionych przez tego klienta. - Zadanie 2: Napisz funkcję
formatuj_date
, która przyjmuje datę i zwraca ją w formacie "DD-MM-RRRR". - Zadanie 3: Stwórz funkcję
czy_klient_vip
, która przyjmujeklient_id
i zwracaTRUE
, jeśli klient ma więcej niż 10 zamówień, w przeciwnym razieFALSE
.
3. Procedury Składowane
- Definicja:
- Procedury składowane to bloki kodu, które wykonują sekwencję instrukcji SQL.
- Przykład: Procedura, która dodaje nowego klienta, tworzy dla niego konto i wysyła powitalny e-mail.
- Cechy:
- Mogą przyjmować parametry:
IN
,OUT
,INOUT
. - Mogą modyfikować dane:
INSERT
,UPDATE
,DELETE
. - Nie muszą zwracać wartości:
CALL procedura(parametry);
. - Przykład: Procedura która generuje raport sprzedażowy dla danego okresu czasu.
- Mogą przyjmować parametry:
- Zastosowania:
- Automatyzacja: Wykonuj złożone operacje za pomocą jednego wywołania.
- Transakcje: Zapewnij spójność danych, grupując operacje w transakcje.
- Raportowanie: Generuj niestandardowe raporty.
- Przykład: Procedura która przenosi dane archiwalne do oddzielnej bazy danych.
- Elementy:
- Instrukcje warunkowe:
IF
,CASE
.- Przykład: Użyj
IF
, aby sprawdzić, czy klient ma wystarczająco środków na koncie, zanim wykonasz transakcję. - Przykład: Użyj
CASE
, aby przypisać klientowi status na podstawie liczby zakupów.
- Przykład: Użyj
- Pętle:
WHILE
,LOOP
.- Przykład: Użyj
WHILE
, aby przetworzyć wszystkie zamówienia w danym okresie. - Przykład: Użyj
LOOP
, aby wykonać operację określoną liczbę razy.
- Przykład: Użyj
- Instrukcje warunkowe:
Zadania - Procedury Składowane
- Zadanie 1: Utwórz procedurę
dodaj_zamowienie
, która przyjmujeklient_id
,kwota
istatus
, a następnie dodaje nowe zamówienie do tabelizamowienia
. - Zadanie 2: Napisz procedurę
aktualizuj_statusy_zamowien
, która aktualizuje statusy wszystkich zamówień o wartości powyżej 500 na "Wysłane". - Zadanie 3: Stwórz procedurę
generuj_raport_klienta
, która przyjmujeklient_id
i wyświetla listę wszystkich zamówień klienta wraz z ich kwotami i statusami.
Kod Testowej Bazy Danych
CREATE TABLE klienci (
id INT AUTO_INCREMENT PRIMARY KEY,
imie VARCHAR(50),
nazwisko VARCHAR(50),
kod_klienta VARCHAR(20)
);
CREATE TABLE produkty (
id INT AUTO_INCREMENT PRIMARY KEY,
nazwa VARCHAR(50),
cena DECIMAL(10, 2)
);
CREATE TABLE zamowienia (
id INT AUTO_INCREMENT PRIMARY KEY,
klient_id INT,
produkt_id int,
kwota DECIMAL(10, 2),
status VARCHAR(20),
data_zamowienia datetime
);
CREATE TABLE historia_cen (
id INT AUTO_INCREMENT PRIMARY KEY,
produkt_id INT,
stara_cena DECIMAL(10, 2),
nowa_cena DECIMAL(10, 2),
data_zmiany DATETIME
);
INSERT INTO klienci (imie, nazwisko) VALUES
('Jan', 'Kowalski'),
('Anna', 'Nowak'),
('Piotr', 'Wisniewski');
INSERT INTO produkty (nazwa, cena) VALUES
('Laptop', 1200.00),
('Mysz', 25.00),
('Klawiatura', 50.00);
INSERT INTO zamowienia (klient_id, produkt_id, kwota, status, data_zamowienia) VALUES
(1, 1, 1200.00, 'Nowe', '2023-01-01 10:00:00'),
(1, 2, 25.00, 'Zrealizowane', '2023-01-02 12:00:00'),
(2, 3, 50.00, 'Wyslane', '2023-01-03 14:00:00'),
(3, 1, 1200.00, 'Nowe', '2023-01-03 15:00:00'),
(3, 2, 50.00, 'Nowe', '2023-01-05 15:00:00'),
(3, 3, 25.00, 'Zrealizowane', '2023-01-06 15:00:00'),
(3, 1, 1200.00, 'Zrealizowane', '2023-01-07 15:00:00'),
(3, 2, 50.00, 'Wyslane', '2023-01-08 15:00:00'),
(3, 3, 25.00, 'Wyslane', '2023-01-09 15:00:00'),
(3, 1, 1200.00, 'Nowe', '2023-01-10 15:00:00'),
(3, 2, 50.00, 'Nowe', '2023-01-11 15:00:00');
1. Wyzwalacze (Triggers)
- Przykład 1: Logowanie zmian w tabeli
DELIMITER //
CREATE TRIGGER log_zmiany_ceny
AFTER UPDATE ON produkty
FOR EACH ROW
BEGIN
IF NEW.cena <> OLD.cena THEN
INSERT INTO historia_cen (produkt_id, stara_cena, nowa_cena, data_zmiany)
VALUES (OLD.id, OLD.cena, NEW.cena, NOW());
END IF;
END //
DELIMITER ;
Ten przykład pokazuje, jak utworzyć wyzwalacz, który rejestruje zmiany cen produktów w oddzielnej tabeli. U
- Przykład 2: Sprawdzanie poprawności danych
DELIMITER //
CREATE TRIGGER sprawdz_ilosc_magazyn
BEFORE INSERT ON zamowienia
FOR EACH ROW
BEGIN
DECLARE dostepna_ilosc INT;
SELECT ilosc INTO dostepna_ilosc FROM magazyn WHERE produkt_id = NEW.produkt_id;
IF dostepna_ilosc < NEW.ilosc THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Niewystarczająca ilość produktu w magazynie.';
END IF;
END //
DELIMITER ;
Ten przykład pokazuje, jak użyć wyzwalacza BEFORE INSERT
, aby sprawdzić, czy w magazynie jest wystarczająca ilość zamawianego produktu.
- Przykład 3: Automatyczne aktualizowanie tabeli powiązanej
DELIMITER //
CREATE TRIGGER aktualizuj_statystyki_klienta
AFTER INSERT ON zamowienia
FOR EACH ROW
BEGIN
UPDATE statystyki_klientow
SET liczba_zamowien = liczba_zamowien + 1
WHERE klient_id = NEW.klient_id;
END //
DELIMITER ;
Ten przykład pokazuje, jak zaktualizować statystyki klienta po dodaniu nowego zamówienia.
2. Funkcje Składowane
- Przykład 1: Obliczanie wartości
DELIMITER //
CREATE FUNCTION oblicz_rabat(kwota DECIMAL(10, 2)) RETURNS DECIMAL(10, 2)
DETERMINISTIC
BEGIN
DECLARE rabat DECIMAL(10, 2);
IF kwota > 1000 THEN
SET rabat = kwota * 0.1;
ELSE
SET rabat = 0;
END IF;
RETURN rabat;
END //
DELIMITER ;
Ten przykład pokazuje, jak utworzyć funkcję, która oblicza rabat na podstawie kwoty zamówienia.
- Przykład 2: Formatowanie danych
DELIMITER //
CREATE FUNCTION formatuj_numer_telefonu(numer VARCHAR(20)) RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
DECLARE sformatowany_numer VARCHAR(20);
SET sformatowany_numer = CONCAT('(+48) ', SUBSTRING(numer, 1, 3), '-', SUBSTRING(numer, 4, 3), '-', SUBSTRING(numer, 7, 3));
RETURN sformatowany_numer;
END //
DELIMITER ;
Ten przykład pokazuje, jak sformatować numer telefonu.
- Przykład 3: Sprawdzanie poprawności danych
DELIMITER //
CREATE FUNCTION czy_email_poprawny(email VARCHAR(255)) RETURNS BOOLEAN
DETERMINISTIC
BEGIN
DECLARE wynik BOOLEAN;
IF email REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$' THEN
SET wynik = TRUE;
ELSE
SET wynik = FALSE;
END IF;
RETURN wynik;
END //
DELIMITER ;
Ten przykład pokazuje, jak sprawdzić, czy adres e-mail jest poprawny.
3. Procedury Składowane
- Przykład 1: Automatyzacja operacji
DELIMITER //
CREATE PROCEDURE dodaj_klienta_i_zamowienie(IN imie VARCHAR(50), IN nazwisko VARCHAR(50), IN produkt_id INT, IN ilosc INT)
BEGIN
DECLARE klient_id INT;
INSERT INTO klienci (imie, nazwisko) VALUES (imie, nazwisko);
SET klient_id = LAST_INSERT_ID();
INSERT INTO zamowienia (klient_id, produkt_id, ilosc) VALUES (klient_id, produkt_id, ilosc);
END //
DELIMITER ;
Ten przykład pokazuje, jak utworzyć procedurę, która dodaje nowego klienta i tworzy dla niego zamówienie.
- Przykład 2: Transakcje
DELIMITER //
CREATE PROCEDURE przenies_srodki(IN z_konta INT, IN na_konto INT, IN kwota DECIMAL(10, 2))
BEGIN
DECLARE dostepne_srodki DECIMAL(10, 2);
START TRANSACTION;
SELECT srodki INTO dostepne_srodki FROM konta WHERE id = z_konta;
IF dostepne_srodki >= kwota THEN
UPDATE konta SET srodki = srodki - kwota WHERE id = z_konta;
UPDATE konta SET srodki = srodki + kwota WHERE id = na_konto;
COMMIT;
ELSE
ROLLBACK;
END IF;
END //
DELIMITER ;
Ten przykład pokazuje, jak użyć transakcji, aby zapewnić spójność danych podczas przenoszenia środków między kontami.
- Przykład 3: Generowanie raportów
DELIMITER //
CREATE PROCEDURE raport_sprzedazy_produktu(IN produkt_id INT)
BEGIN
SELECT k.imie, k.nazwisko, z.data_zamowienia, z.ilosc
FROM klienci k
JOIN zamowienia z ON k.id = z.klient_id
WHERE z.produkt_id = produkt_id;
END //
DELIMITER ;
Ten przykład pokazuje, jak utworzyć procedurę, która generuje raport sprzedaży dla danego produktu.