0% found this document useful (0 votes)
28 views15 pages

c4 2021

Uploaded by

Denis Andrei
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
28 views15 pages

c4 2021

Uploaded by

Denis Andrei
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 15

Curs 4

#exemplele din acest document sunt verificate pe platforma de mai jos:


MariaDB [lib4]> select version();
+-----------------+
| version() |
+-----------------+
| 10.4.14-MariaDB |
+-----------------+
1 row in set (0.000 sec)

#Declararea unei coloane virtuale (CARE SE CALCULEAZA PE BAZA ALTOR COLOANE) se


face folosind cuvintele cheie 'GENERATED ALWAYS AS'

CREATE TABLE IF NOT EXISTS firme (


id_firma int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
nume VARCHAR(20) NOT NULL,
cost float NOT NULL,
venit float not null,
profit float GENERATED ALWAYS AS (venit-cost)
);

mysql> desc firme;


+----------+------------------+------+-----+---------+-------------------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+-------------------+
| id_firma | int(10) unsigned | NO | PRI | NULL | auto_increment |
| nume | varchar(20) | NO | | NULL | |
| cost | float | NO | | NULL | |
| venit | float | NO | | NULL | |
| profit | float | YES | | NULL | VIRTUAL GENERATED |
+----------+------------------+------+-----+---------+-------------------+
5 rows in set (0.00 sec)

# la inserarea unei linii in acest tabel valoarea profitului e calculata automat,


nu trebuie specificata.

>> insert into firme(nume, cost,venit) values('HDesign SRL',100.00,200.00);


Query OK, 1 row affected (0.01 sec)

mysql> select * from firme;


+----------+-------------+------+-------+--------+
| id_firma | nume | cost | venit | profit |
+----------+-------------+------+-------+--------+
| 1 | HDesign SRL | 100 | 200 | 100 |
+----------+-------------+------+-------+--------+
1 row in set (0.00 sec)

#de fapt incercarea de inserare a unei valori intr-o coloana virtuala va genera o
eroare.

mysql> insert into firme(nume, cost,venit,profit) values('HDesign


SRL',100.00,200.00, 100.00);
ERROR 3105 (HY000): The value specified for generated column 'profit' in table
'firme' is not allowed.

# o coloana virtuala poate fi declarata ca VIRTUAL (default, valorile din ea sunt


intotdeauna calculate la inserare/updatare/citire) sau STORED (valorile din ea sunt
calculate si stocate la inserare/updatare dar la citire in cazul unei selectii de
linii ea nu mai este recalculata)

CREATE TABLE IF NOT EXISTS firme1 (


id_firma int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
nume VARCHAR(20) NOT NULL,
cost float NOT NULL,
venit float not null,
profit float GENERATED ALWAYS AS (venit-cost) STORED
);

# coloana virtuala trebuie declarata DUPA toate coloanele pe care ea le foloseste


in calcul. Comanda urmatoare va genera o eroare in mysql-8.0.18

CREATE TABLE firme1 (


id_firma int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
nume VARCHAR(20) NOT NULL,
cost float NOT NULL,
profit float GENERATED ALWAYS AS (venit-cost)
venit float not null);

# coloana virtuala nu poate folosi coloane de tip auto-increment

CREATE TABLE firme (


id_firma int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
nume VARCHAR(20) NOT NULL,
cost float NOT NULL,
profit float GENERATED ALWAYS AS (id_firma)
);

# coloanele virtuale se pastreaza in tabelele clonate

create table firme2 like firme;


mysql> desc firme1;
+----------+------------------+------+-----+---------+------------------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+------------------+
| id_firma | int(10) unsigned | NO | PRI | NULL | auto_increment |
| nume | varchar(20) | NO | | NULL | |
| cost | float | NO | | NULL | |
| venit | float | NO | | NULL | |
| profit | float | YES | | NULL | STORED GENERATED |
+----------+------------------+------+-----+---------+------------------+

===================================================================================
============

REGULI DE VALIDARE IN MYSQL

--> daca regula de validare e relativ simpla se poate impune folosind un CHECK
CONSTRAINT
--> daca regula de validare e mai complicata se foloseste un TRIGGER

CHECK CONSTRAINT

aceasta constrangere se poate impune pentru campuri si pt tabele. ATENTIE: CHECK


CONSTRAINT e valid numai in versiuni f. recente mysql (>8.0.xx). Similar, nu
functioneaza in mariadb versiuni mai vechi.

conditia de validare este verificata de cate ori se insereaza o linie nou sau se
updateaza o linie deja existenta.

fie tabelul

CREATE TABLE t1
(
v1 INT,
v2 INT,
v3 INT
);

Vrem ca v1<10, v2<20,v3>7

drop table if exists t1;


CREATE TABLE t1
(
v1 INT,
v2 INT,
v3 INT,
constraint c1 check (v1<10),
constraint c2 check (v2<20),
constraint c3 check (v3>7)
);

sau fara a specifica numele constrangerii.

drop table if exists t1;


CREATE TABLE t1
(
v1 INT ,
v2 INT ,
v3 INT ,
check (v1<10),
check (v2<20),
check (v3>7)
);
In plus, vrem ca v1+v2<v3 (regula care implica mai multe coloane din tabel)

drop table if exists t1;


CREATE TABLE t1
(
v1 INT ,
v2 INT ,
v3 INT ,
constraint c1 check (v1<10),
constraint c2 check (v2<20),
constraint c3 check (v3>7),
constraint c4 check (v1+v2<v3)
);

In general formatul pentru validare este

constraint [nume_constrangere] CHECK (EXPR)

trebuie ca expresia sa aiba valoarea de adevar T saul NULL pentru ca regula sa fie
validata. ATENTIE deci ca
valorile nule ale expresiei valideaza acest constraint, spre exemplu, constructia
urmatoare insereaza date in tabel.

drop table if exists t3;


CREATE TABLE t3
(
v1 INT,
v2 INT,
v3 INT,
constraint c3 CHECK (v3>7)
);

>>insert into t3(v1,v2) values(1,2);


Query OK, 1 row affected (0.01 sec)

>>select * from t1;


+------+------+------+
| v1 | v2 | v3 |
+------+------+------+
| 1 | 2 | NULL |
+------+------+------+
1 row in set (0.00 sec)

#fiecare constraint folosit in tabel poarta un nume care ulterior (vedem mai
tarziu) poate fi folosit pentru a modifica/sterge acel constraint fara a sterge
tabelul.

>>show create table t3;


+-------
+----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
------------------+
| Table | Create Table
|
+-------
+----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
------------------+
| t3 | CREATE TABLE `t3` (
`v1` int(11) DEFAULT NULL,
`v2` int(11) DEFAULT NULL,
`v3` int(11) DEFAULT NULL,
CONSTRAINT `c3` CHECK (`v3` > 7)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |

# Expresia logica EXPR din constructia CHECK(EXPR) se construieste folosind


operatori logici, aritmetici, de comparatie, functii deterministice (mecanismul e
similar cu cel din regulile de validare de la Access insa sintaxa e uneori diferita
si sunt unele limitari).

simboluri pentru SQL

_ underscore -->exact un caracter (? in access)


% -->orice secventa de caractere (* in access)

#Acceptam numai clienti cu numele din 3 caractere, prima fiind D (case


insensitive).

nume like 'D__'

drop table if exists clienti1;


CREATE TABLE clienti1 (
id_client int unsigned NOT NULL AUTO_INCREMENT,
nume VARCHAR(20) NOT NULL,
an_nastere int NOT NULL,
PRIMARY KEY( id_client),
CHECK(nume LIKE "D__")
) ;

insert into clienti1(nume, an_nastere) values('dan',1990);

aici nume din 3 caractere, primul D mare

nume like '___' and ascii(mid(nume,1,1))=68

drop table if exists clienti1;


CREATE TABLE clienti1 (
id_client int unsigned NOT NULL AUTO_INCREMENT,
nume VARCHAR(20) NOT NULL,
an_nastere int NOT NULL,
PRIMARY KEY( id_client),
CHECK(nume like '___' AND ascii(mid(nume,1,1))= 68 )
) ;

test
insert into clienti1(nume, an_nastere) values('Dan',1990);

aici nume din 3 caractere, prima D sau E mare

drop table if exists clienti1;


CREATE TABLE clienti1 (
id_client int unsigned NOT NULL AUTO_INCREMENT,
nume VARCHAR(20) NOT NULL,
an_nastere int NOT NULL,
PRIMARY KEY( id_client),
CHECK(nume like '___' AND (ascii(mid(nume,1,1))= 68 OR ascii(mid(nume,1,1))= 69 ) )

) ;

test
insert into clienti1(nume, an_nastere) values('Ean',1990);

#Acceptam numai clienti cu numele incepand cu D (case insensitive).

CHECK(nume LIKE "D%")

#Acceptam numai clienti cu numele incepand cu D (case sensitive, verificam faptul


ca primul caracter din string are codul ascii 68, adica este D).

CREATE TABLE clienti15 (


id_client int unsigned NOT NULL AUTO_INCREMENT,
nume VARCHAR(20) NOT NULL,
an_nastere int NOT NULL,
PRIMARY KEY( id_client),
CHECK(ascii(mid(nume,1,1))= 68)
) ;
>>insert into clienti15(nume, an_nastere) values('Dan',1990);
Query OK, 1 row affected (0.01 sec)
>>insert into clienti15(nume, an_nastere) values('dan',1990);
ERROR 4025 (23000): CONSTRAINT `CONSTRAINT_1` failed for `lib4`.`clienti1`

#Acceptam numai clienti cu numele continand litera D (case insensitive).


CHECK(nume LIKE "%D%")

# Acceptam anul nasterii numai dupa 1990.


CHECK(an_nastere >=1990)

# Acceptam data inregistrarii numai dupa 2012

drop table if exists clienti;


CREATE TABLE clienti (
id_client int unsigned NOT NULL AUTO_INCREMENT,
nume VARCHAR(20) NOT NULL,
an_nastere int NOT NULL,
data_inreg Date,
PRIMARY KEY( id_client)
) ;

data_inreg>='2012-1-1'

#se pot face constructii logice mai complexe, folosind operatori de flow control,
dar in acest caz e mai bine sa se creeze un trigger. In tabelul de mai jos impun ca
daca anul nasterii este >1947 atunci trebuie introdus obligatoriu si un nume.

CREATE TABLE clienti12 (


id_client int unsigned NOT NULL AUTO_INCREMENT,
nume VARCHAR(20),
an_nastere int NOT NULL,
CHECK(
CASE
WHEN an_nastere>1947
then
case
when (nume is not null)
then 1
else 0
end
else 1
end = 1
),
PRIMARY KEY( id_client)
) ;

Folosirea unui TRIGGER in MySQL pentru validarea inputului intr-un tabel.

Trigerii sunt functii definite dupa crearea unui tabel si care se executa atunci
cand se intampla un eveniment (inserare, updatare, stergere) in tabelul respectiv.
Triggerul poate fi setat sa se execute inainte (BEFORE) sau dupa(AFTER) evenimentul
respectiv.

In mysql-8.0 un trigger este asociat unei singure combinatii de eveniment


(INSERT/UPDATE/DELETE) si moment(BEFORE/AFTER). Asta inseamna ca pentru validarea
inputului vom scrie 2 trigeri (in esenta identici), unul va reactiona inainte de
orice inserare de date, iar celalalt va reactiona inainte de orice actualizare de
date in tabel.

Se pot defini mai multi triggeri care reactioneaza la acceasi combinatie


eveniment/moment. Ei reactioneaza in ordinea in care sunt definiti.

Ei pot fi folositi nu numai pentru validarea inputului dar si pentru alte actiuni
in baza.

Un trigger are structura


DELIMITER //
CREATE TRIGGER [NUME_TRIGGER] [BEFORE/AFTER] [INSERT/UPDATE/DELETE] ON
[NUME_TABEL]
FOR EACH ROW
BEGIN

INSTRUCTIUNI PENTRU VALIDAREA LINIEI CE URMEAZA A FI INSERATA/stearsa/updatata

DACA SE UPDATEAZA O LINIE, EA SE ACCESEAZA CU NUMELE 'OLD' IAR VALOAREA NOU-


INTRODUSA CU 'NEW'

DACA SE STERGE O LINIE, EA SE ACCESEAZA CU NUMELE 'OLD'

END;//
DELIMITER ;

Se pot defini mai multi triggeri care reactioneaza la acceasi combinatie


eveniment/moment. Ei reactioneaza in ordinea in care sunt definiti.

daca am definit 2 triggeri t1 si t2 reactionand la aceeasi combinatie event/moment,


se poate folosi clauza PRECEDES pentru a forta t1 sa reactioneze dupa t2 scriind in
codul de definire al lui t1

FOR EACH ROW PRECEDES t2

EXEMPLE TRIGGERI:

Fie un tabel
drop table if exists produse;
CREATE TABLE produse (
id_produs int unsigned NOT NULL AUTO_INCREMENT,
nume VARCHAR(20),
pret double NOT NULL,
data_inreg Date,
PRIMARY KEY( id_produs)
) ;

#trigger pt introducerea unui pret strict pozitiv;

DELIMITER //
CREATE TRIGGER valideaza_pret_la_inserare BEFORE INSERT ON produse
FOR EACH ROW
BEGIN

IF (NEW.pret<=0)

THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'pretul trebuie sa fie strict pozitiv';
END IF;

END;
//
DELIMITER ;

Acum definim un trigger care sa permita numai preturi strict pozitive la updatare
in tabelul produse.

DELIMITER //
CREATE TRIGGER valideaza_pret_la_updatare BEFORE UPDATE ON produse
FOR EACH ROW
BEGIN

IF (NEW.pret<=0)

THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'pretul trebuie sa fie strict pozitiv';
END IF;

END;
//
DELIMITER ;

mysql> insert into produse(pret) values(-10.1);


ERROR 1644 (45000): pretul trebuie sa fie strict pozitiv
mysql> insert into produse(pret) values(10.1);
Query OK, 1 row affected (0.01 sec)

#putem scrie o procedura stocata care sa execute corpul de instructiuni:

DELIMITER //
create procedure valideaza(in pret double)
begin
IF (pret<=0)

THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'pretul trebuie sa fie strict pozitiv';

END IF;
end
//
DELIMITER ;

si apoi rescriem cei 2 triggeri

DELIMITER //
CREATE OR REPLACE TRIGGER valideaza_pret_la_inserare BEFORE INSERT ON produse
FOR EACH ROW
BEGIN
call valideaza(NEW.pret);
END;
//
DELIMITER ;

DELIMITER //
CREATE OR REPLACE TRIGGER valideaza_pret_la_inserare BEFORE UPDATE ON produse
FOR EACH ROW
BEGIN
call valideaza(NEW.pret);

END;
//
DELIMITER ;

#scriem un trigger pentru a forta introducerea pretului cu exact 2 zecimale si


strict pozitiv. El se va executa inaintea inserarii unui nou produs.

drop trigger if exists valideaza_pret_la_inserare;


DELIMITER //
CREATE TRIGGER valideaza_pret_la_inserare BEFORE INSERT ON produse
FOR EACH ROW
BEGIN
declare d double;
set d=NEW.pret-truncate(NEW.pret,2);

IF (NEW.pret<=0 || d > 0.0)

THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'pretul trebuie sa fie strict pozitiv si cu
exact 2 zecimale';
END IF;

END;
//
DELIMITER ;

#scriem un trigger pentru a accepta numai nume formate din exact 4 caractere

drop trigger valideaza_nume;


DELIMITER //
CREATE TRIGGER valideaza_nume BEFORE INSERT ON produse
FOR EACH ROW
BEGIN

IF (NEW.nume not like "____")


THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'numele trebuie sa fie din 4 caractere';
END IF;

END;
//
DELIMITER ;

#scriem un trigger pentru a accepta numai nume care incep cu D(mic sau mare)

drop trigger valideaza_nume;


DELIMITER //
CREATE TRIGGER valideaza_nume BEFORE INSERT ON produse
FOR EACH ROW
BEGIN

IF (NEW.nume not like "D%")

THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'numele trebuie sa inceapa cu D mare sau
mic';
END IF;

END;
//
DELIMITER ;

insert into produse(nume, pret) values('dan',15.1);


insert into produse(nume, pret) values('ion',15.1);

#acceptam numai nume care incep cu D mare


ascii(mid(nume,1,1))= 68

drop trigger valideaza_nume;


DELIMITER //
CREATE TRIGGER valideaza_nume BEFORE INSERT ON produse
FOR EACH ROW
BEGIN

IF (ascii(mid(NEW.nume,1,1)) <> 68 )

THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'numele trebuie sa inceapa cu D mare';

END IF;

END;
//
DELIMITER ;
insert into produse(nume, pret) values('dan',15.1);
insert into produse(nume, pret) values('Dan',15.1);

#acceptam numai nume care contin numai litere. Verificam ca acestea au codul ascii
fie intre 97-
122 fie intre 65 si 90

#exemplu bucla for in trigger


#parcurgem un string si il validam numai daca toate caracterele sunt litere:

ascii(mid(NEW.nume,k,1)) <65 || ascii(mid(NEW.nume,k,1)) >122 ||


(ascii(mid(NEW.nume,k,1)) >90 && ascii(mid(NEW.nume,k,1)) <97 )

drop trigger valideaza_nume;


DELIMITER //
CREATE TRIGGER valideaza_nume BEFORE INSERT ON produse
FOR EACH ROW
BEGIN

DECLARE k INT;

set k=0;

iteratie:LOOP
set k=k+1;
if(k> length(NEW.nume)) then
LEAVE iteratie;
end if;

if( ascii(mid(NEW.nume,k,1)) <65 || ascii(mid(NEW.nume,k,1)) >122 ||


(ascii(mid(NEW.nume,k,1)) >90 && ascii(mid(NEW.nume,k,1)) <97 ))

THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'numele trebuie sa contina numai litere';

END IF;

END LOOP;

END;
//
DELIMITER ;
#Scriem un trigger care va insera data curenta daca o data anume nu a fost inserata
pentru produs

drop table if exists produse;


CREATE TABLE produse (
id_produs int unsigned NOT NULL AUTO_INCREMENT,
nume VARCHAR(20),
pret double NOT NULL,
data_inreg Date,
PRIMARY KEY( id_produs)
) ;

DELIMITER //
CREATE TRIGGER adauga_data1 BEFORE INSERT ON produse
FOR EACH ROW
BEGIN

IF (NEW.data_inreg is null)

THEN
set NEW.data_inreg=curdate();
END IF;

END;
//
DELIMITER ;

#num permitem inserarea de mai mult de 5 linii in tabelul produse


DELIMITER //
CREATE TRIGGER nr_max BEFORE INSERT ON produse
FOR EACH ROW
BEGIN
declare k int;

select count(*) into k from produse;

IF (k >=5 )

THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'am depasit nr maxim de linii din tabel';
END IF;

END;
//
DELIMITER ;
#afisam instructiunea de creare a primului trigger
show create trigger valideaza_pretul_la_inserare;

#stergem primul trigger


drop trigger valideaza_pretul_la_inserare;

#daca dorim sa inlocuim un trigger cu altul cu acelasi nume putem folosi


CREATE OR REPLACE in loc de CREATE

#afisam toti triggerii asociati bazei lib4


show triggers from lib4;

#afisam toti triggerii asociati bazei in care lucram in acest moment si care contin
in ei secventa valideaza

select * from information_schema.triggers where trigger_name like '%valideaza%';

Cateva reguli importante privind triggerii in mysql:

1) la stergerea unui tabel se sterg automat toti triggerii asociati.


2) trigerii nu reactioneaza atunci cand se modifica/sterg linii din cauza prezentei
cheilor externe in tabel si a actiunilor tip 'on update cascade', 'on delete
cascade' pe ele.
3)trigerii asociati momentului AFTER se executa dupa ce toti trigerrii asociati
momentului BEFORE sunt executati.
4) in instalarea de default a mysql, daca un trigger iese din executie cu o eroare
( precum SIGNAL SQLSTATE '45000') atunci orice instructiune efectuata inainte de
iesirea din executie este anulata.

You might also like