Diferència entre revisions de la pàgina «Procediments i funcions a MySQL»
m (→Exercici 2) |
m (→Exercici 3) |
||
| Línia 199: | Línia 199: | ||
</pre> | </pre> | ||
| − | ==Exercici 3== | + | ==Exercici 3: cursor amb paràmetres== |
| − | + | Ens fixem en el següent exemple, com a punt de partida: | |
| − | |||
*https://stackoverflow.com/questions/40907109/mysql-how-to-use-parameters-in-cursor | *https://stackoverflow.com/questions/40907109/mysql-how-to-use-parameters-in-cursor | ||
| + | |||
| + | Sobre la base de dades ''classicmodels'' i la taula ''orders'', crear el procediment '''llistarComandes(vstatus)''' per saber el número de comanda, dia de la comanda i número del client, filtrant per un valor vàlid del camp ''status''. Crearem un cursor per a les comandes, filtrant pel valor de ''status''. | ||
| + | |||
| + | Solució: | ||
| + | <pre> | ||
| + | DROP PROCEDURE IF EXISTS llistarComandes; | ||
| + | |||
| + | DELIMITER $$ | ||
| + | CREATE PROCEDURE llistarComandes(IN vstatus varchar(15)) | ||
| + | BEGIN | ||
| + | DECLARE vorderNumber INT; | ||
| + | DECLARE vorderDate DATE; | ||
| + | DECLARE vcustomerNumber INT; | ||
| + | DECLARE done INT DEFAULT FALSE; | ||
| + | |||
| + | DECLARE curLlistaComandes CURSOR FOR | ||
| + | SELECT orderNumber, orderDate, customerNumber FROM orders WHERE status = vstatus; | ||
| + | |||
| + | DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; | ||
| + | |||
| + | OPEN curLlistaComandes; | ||
| + | read_loop: LOOP | ||
| + | FETCH curLlistaComandes INTO vorderNumber, vorderDate, vcustomerNumber; | ||
| + | IF done THEN LEAVE read_loop; END IF; | ||
| + | select vorderNumber, vorderDate, vcustomerNumber; | ||
| + | END LOOP; | ||
| + | CLOSE curLlistaComandes; | ||
| + | END$$ | ||
| + | |||
| + | DELIMITER ; | ||
| + | |||
| + | CALL llistarComandes('Shipped'); | ||
| + | </pre> | ||
| + | Les diferents opcions que tenim per al camp ''status'' són: | ||
| + | <pre> | ||
| + | mysql> select distinct status from orders; | ||
| + | |||
| + | Shipped | ||
| + | Resolved | ||
| + | Cancelled | ||
| + | On Hold | ||
| + | Disputed | ||
| + | In Process | ||
| + | </pre> | ||
| + | <pre> | ||
| + | mysql> CALL llistarComandes('Resolved'); | ||
| + | 10164 2003-10-21 452 | ||
| + | 10327 2004-11-10 145 | ||
| + | 10367 2005-01-12 205 | ||
| + | 10386 2005-03-01 141 | ||
| + | |||
| + | mysql> CALL llistarComandes('Cancelled'); | ||
| + | 10167 2003-10-23 448 | ||
| + | 10179 2003-11-11 496 | ||
| + | 10248 2004-05-07 131 | ||
| + | 10253 2004-06-01 201 | ||
| + | 10260 2004-06-16 357 | ||
| + | 10262 2004-06-24 141 | ||
| + | </pre> | ||
==Exercici 4== | ==Exercici 4== | ||
Revisió del 16:24, 18 abr 2022
Contingut
Introducció
Realitzarem uns quants exercicis per consolidar els conceptes de procediments, cursors i funcions.
Tasques a realitzar
Exercici 1
Ens basem en el següent exercici de Oracle (PL/SQL):
però de fet és molt senzill. Es tracta de definir un cursor i mostrar la llista de tots els productes (codi del producte, nom del producte i preu, amb el format separat per comes). Ho farem amb el procediment llistarProductes().
DROP PROCEDURE IF EXISTS llistarProductes;
DELIMITER $$
CREATE PROCEDURE llistarProductes ()
BEGIN
DECLARE finished INT DEFAULT 0;
DECLARE vproductCode varchar(15);
DECLARE vproductName varchar(70);
DECLARE vbuyPrice decimal(10,2);
DECLARE curProductes CURSOR FOR SELECT productCode, productName, buyPrice FROM products;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN curProductes;
getProductes: LOOP
FETCH curProductes INTO vproductCode,vproductName,vbuyPrice;
IF finished = 1 THEN
LEAVE getProductes;
END IF;
SELECT CONCAT(vproductCode,';',vproductName,';',vbuyPrice);
END LOOP getProductes;
CLOSE curProductes;
END$$
DELIMITER ;
CALL llistarProductes(); S10_1678;1969 Harley Davidson Ultimate Chopper;48.81 S10_1949;1952 Alpine Renault 1300;98.58 S10_2016;1996 Moto Guzzi 1100i;68.99 S10_4698;2003 Harley-Davidson Eagle Drag Bike;91.02 S10_4757;1972 Alfa Romeo GTA;85.68 S10_4962;1962 LanciaA Delta 16V;103.42 S12_1099;1968 Ford Mustang;95.34 S12_1108;2001 Ferrari Enzo;95.59 S12_1666;1958 Setra Bus;77.90 ...
NOTA: per tal de què no surtin totes les línies de separació farem servir l'opció silent:
$ mysql -u alumne -pkeiL2lai -ss classicmodels
Si volem executar directament el procediment des de la línia de comandes de Linux, i generar un fitxer CSV, podem fer:
$ mysql -u alumne -pkeiL2lai -ss classicmodels -e 'CALL llistarProductes()' > llistaProductes.csv $ cat llistaProductes.csv S10_1678;1969 Harley Davidson Ultimate Chopper;48.81 S10_1949;1952 Alpine Renault 1300;98.58 S10_2016;1996 Moto Guzzi 1100i;68.99 ...
Exercici 2
Ens basem en el següent exemple, que està basat en Oracle i el llenguatge PL/SQL:
L'equivalent a l'exemple és utilitzar la base de dades classicmodels. Tenim la taula orders i orderdetails.
mysql> desc orders; +----------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------------+-------------+------+-----+---------+-------+ | orderNumber | int | NO | PRI | NULL | | | orderDate | date | NO | | NULL | | | requiredDate | date | NO | | NULL | | | shippedDate | date | YES | | NULL | | | status | varchar(15) | NO | | NULL | | | comments | text | YES | | NULL | | | customerNumber | int | NO | MUL | NULL | | +----------------+-------------+------+-----+---------+-------+ mysql> desc orderdetails; +-----------------+---------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------------+---------------+------+-----+---------+-------+ | orderNumber | int | NO | PRI | NULL | | | productCode | varchar(15) | NO | PRI | NULL | | | quantityOrdered | int | NO | | NULL | | | priceEach | decimal(10,2) | NO | | NULL | | | orderLineNumber | smallint | NO | | NULL | | +-----------------+---------------+------+-----+---------+-------+
Podem crear la vista sales:
CREATE VIEW sales AS
SELECT customerNumber,
SUM(priceEach * quantityOrdered) total,
ROUND(SUM(priceEach * quantityOrdered) * 0.05) credit
FROM orderdetails
INNER JOIN orders USING (orderNumber)
WHERE status = 'Shipped'
GROUP BY customerNumber;
La idea és que com més bo és un client (com més comandes hagi fet), el seu crèdit serà més bo. Assignarem als clients un crèdit del 5% del total que han facturat.
L'objectiu del procediment assignarLimitCredit() serà:
1.. Fer un reset del crèdit dels clients (camp customers.creditLimit). Ho farem amb un udpate creditLimit=0 ....
2. Tenim un pressupost de 1 milió (1000000) per assignar crèdit als clients. Recorrem (cursor) els clients amb la vista sales, i anirem assignant el nou crèdit als clients fins mentre no ens passem del pressupost que tenim.
La solució amb PL/SQL la tens a l'enllaç. Ho hem de fer amb MySQL.
I la solució amb MySQL:
DROP PROCEDURE IF EXISTS assignarLimitCredit;
DELIMITER $$
CREATE PROCEDURE assignarLimitCredit ()
BEGIN
DECLARE finished INT DEFAULT 0;
DECLARE vbudget INT DEFAULT 1000000;
DECLARE vcustomerNumber INT;
DECLARE vcredit decimal(42,0);
DECLARE curSales CURSOR FOR SELECT customerNumber, credit FROM sales;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
UPDATE customers SET creditLimit = 0;
OPEN curSales;
getSales: LOOP
FETCH curSales INTO vcustomerNumber, vcredit;
IF finished = 1 THEN
LEAVE getSales;
END IF;
IF vbudget > vcredit THEN
UPDATE customers SET creditLimit=vcredit WHERE customerNumber=vcustomerNumber;
END IF;
SET vbudget = vbudget - vcredit;
IF vbudget <= 0 THEN
LEAVE getSales;
END IF;
END LOOP getSales;
CLOSE curSales;
END$$
DELIMITER ;
Abans d'executar el procediment (les dades originals):
mysql> select customerNumber,customerName,creditLimit from customers limit 10; +----------------+------------------------------+-------------+ | customerNumber | customerName | creditLimit | +----------------+------------------------------+-------------+ | 103 | Atelier graphique | 21000.00 | | 112 | Signal Gift Stores | 71800.00 | | 114 | Australian Collectors, Co. | 117300.00 | | 119 | La Rochelle Gifts | 118200.00 | | 121 | Baane Mini Imports | 81700.00 | | 124 | Mini Gifts Distributors Ltd. | 210500.00 | | 125 | Havel & Zbyszek Co | 0.00 | | 128 | Blauer See Auto, Co. | 59700.00 | | 129 | Mini Wheels Co. | 64600.00 | | 131 | Land of Toys Inc. | 114900.00 | +----------------+------------------------------+-------------+
Executem el procediment i assignem nous valors de límit de crèdit als clients:
CALL assignarLimitCredit();
Després d'executar el procediment:
mysql> select customerNumber,customerName,creditLimit from customers limit 10; +----------------+------------------------------+-------------+ | customerNumber | customerName | creditLimit | +----------------+------------------------------+-------------+ | 103 | Atelier graphique | 1116.00 | | 112 | Signal Gift Stores | 4009.00 | | 114 | Australian Collectors, Co. | 9029.00 | | 119 | La Rochelle Gifts | 5847.00 | | 121 | Baane Mini Imports | 5211.00 | | 124 | Mini Gifts Distributors Ltd. | 29209.00 | | 125 | Havel & Zbyszek Co | 0.00 | | 128 | Blauer See Auto, Co. | 3797.00 | | 129 | Mini Wheels Co. | 3336.00 | | 131 | Land of Toys Inc. | 5382.00 | +----------------+------------------------------+-------------+
Exercici 3: cursor amb paràmetres
Ens fixem en el següent exemple, com a punt de partida:
Sobre la base de dades classicmodels i la taula orders, crear el procediment llistarComandes(vstatus) per saber el número de comanda, dia de la comanda i número del client, filtrant per un valor vàlid del camp status. Crearem un cursor per a les comandes, filtrant pel valor de status.
Solució:
DROP PROCEDURE IF EXISTS llistarComandes;
DELIMITER $$
CREATE PROCEDURE llistarComandes(IN vstatus varchar(15))
BEGIN
DECLARE vorderNumber INT;
DECLARE vorderDate DATE;
DECLARE vcustomerNumber INT;
DECLARE done INT DEFAULT FALSE;
DECLARE curLlistaComandes CURSOR FOR
SELECT orderNumber, orderDate, customerNumber FROM orders WHERE status = vstatus;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN curLlistaComandes;
read_loop: LOOP
FETCH curLlistaComandes INTO vorderNumber, vorderDate, vcustomerNumber;
IF done THEN LEAVE read_loop; END IF;
select vorderNumber, vorderDate, vcustomerNumber;
END LOOP;
CLOSE curLlistaComandes;
END$$
DELIMITER ;
CALL llistarComandes('Shipped');
Les diferents opcions que tenim per al camp status són:
mysql> select distinct status from orders; Shipped Resolved Cancelled On Hold Disputed In Process
mysql> CALL llistarComandes('Resolved');
10164 2003-10-21 452
10327 2004-11-10 145
10367 2005-01-12 205
10386 2005-03-01 141
mysql> CALL llistarComandes('Cancelled');
10167 2003-10-23 448
10179 2003-11-11 496
10248 2004-05-07 131
10253 2004-06-01 201
10260 2004-06-16 357
10262 2004-06-24 141
Exercici 4
- Funcions
Exercici 5
creat per Joan Quintana Compte, abril 2022