Wer sich mit dem SQL-Skript aus dem [post id=2634]MySQL Cursor Beispiel[/post] beschäftigt hat wird festgestellt haben, dass der dort definierte Trigger check_amount
nur funktioniert, wenn nicht mehr Produkte bestellt werden, als insgesamt überhaupt in allen Geschäften (engl. stores) verfügbar sind. Im Beispiel-Skript gibt es insgesamt 550 Fernseher:
1 2 3 4 | -- Insert some products INSERT INTO products(prod_id,name,store,stock) VALUES (NULL,'Fernseher','Berlin',250)$ INSERT INTO products(prod_id,name,store,stock) VALUES (NULL,'Fernseher','Potsdam',200)$ INSERT INTO products(prod_id,name,store,stock) VALUES (NULL,'Fernseher','Bremen',100)$ |
Wird nun die folgende Anfrage gestellt, dann werden mehr als 550 Fernseher bestellt und es kommt zu einem Trigger-Fehler:
1 | INSERT INTO orders(user_id,product,amount) VALUES (1,'Fernseher',1300)$ |
Um diesen Fehler zu beseitigen, habe ich den check_amount
-Trigger erweitert, so dass eine Stored Procedure namens availableQuantity
abgefragt wird, welche dann Auskunft darüber liefert, wie hoch die verfügbare Anzahl eines Produktes ist. Ist diese Anzahl kleiner als die bestellte Menge, dann wird die bestellte Menge einfach auf die maximal verfügbare Anzahl gesetzt.
SQL-Skript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | -- Settings DELIMITER $ -- Create database DROP DATABASE IF EXISTS testdb_trigger$ CREATE DATABASE testdb_trigger DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci$ USE testdb_trigger$ -- Create tables CREATE TABLE IF NOT EXISTS users ( user_id INT(10) NOT NULL AUTO_INCREMENT ,surname VARCHAR(255) NOT NULL ,prename VARCHAR(255) NOT NULL ,PRIMARY KEY(user_id) ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci$ CREATE TABLE IF NOT EXISTS users_a_m ( id INT(10) NOT NULL AUTO_INCREMENT ,user_id INT(10) NOT NULL ,surname VARCHAR(255) NOT NULL ,prename VARCHAR(255) NOT NULL ,PRIMARY KEY(id) ,FOREIGN KEY(user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci$ CREATE TABLE IF NOT EXISTS users_n_z ( id INT(10) NOT NULL AUTO_INCREMENT ,user_id INT(10) NOT NULL ,surname VARCHAR(255) NOT NULL ,prename VARCHAR(255) NOT NULL ,PRIMARY KEY(id) ,FOREIGN KEY(user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci$ CREATE TABLE IF NOT EXISTS products ( prod_id INT(10) NOT NULL AUTO_INCREMENT ,name VARCHAR(255) NOT NULL ,store VARCHAR(255) NOT NULL ,stock INT(5) NOT NULL ,PRIMARY KEY(prod_id) ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci$ CREATE TABLE IF NOT EXISTS orders ( id INT(10) NOT NULL AUTO_INCREMENT ,user_id INT(10) NOT NULL ,product VARCHAR(255) NOT NULL ,amount INT(5) NOT NULL ,PRIMARY KEY(id) ,FOREIGN KEY(user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci$ -- Create Triggers CREATE TRIGGER split_users AFTER INSERT ON users FOR EACH ROW BEGIN IF(NEW.surname BETWEEN 'A' AND 'N') THEN INSERT INTO users_a_m VALUES (NULL, NEW.user_id, NEW.surname, NEW.prename); ELSE INSERT INTO users_n_z VALUES (NULL, NEW.user_id, NEW.surname, NEW.prename); END IF; END$ -- Stored procedure CREATE PROCEDURE availableQuantity(IN product VARCHAR(255), OUT total INT(5)) BEGIN SELECT SUM(stock) INTO total FROM products WHERE name = product; END$ -- Trigger with Cursor CREATE TRIGGER check_amount BEFORE INSERT ON orders FOR EACH ROW BEGIN DECLARE product_id INT(10); DECLARE order_quantity INT(5); DECLARE available_quantity INT(5); DECLARE total_quantity INT(5); -- Cursor DECLARE amount_cursor CURSOR FOR SELECT prod_id, stock FROM products WHERE name=NEW.product ORDER BY stock DESC; OPEN amount_cursor; SET order_quantity = NEW.amount; -- Call stored procedure CALL availableQuantity(NEW.product, @total); SET total_quantity = (SELECT @total); IF(order_quantity > total_quantity) THEN SET NEW.amount = total_quantity; SET order_quantity = total_quantity; END IF; -- Continue... WHILE (order_quantity > 0) DO FETCH amount_cursor INTO product_id, available_quantity; IF(order_quantity > available_quantity) THEN UPDATE products SET stock = 0 WHERE prod_id = product_id; ELSE UPDATE products SET stock = stock - order_quantity WHERE prod_id = product_id; END IF; SET order_quantity = order_quantity - available_quantity; END WHILE; CLOSE amount_cursor; END$ -- Insert 10 people INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Quander','Nadja')$ INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Labuhn','Milan')$ INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Ebisch','Alena')$ INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Paffenholz','Ellen')$ INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Haas','Vin')$ INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Jaber','Chris')$ INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Radicke','Yannik')$ INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Abadre','Sean')$ INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Nähr','Friedrich')$ INSERT INTO users(user_id,surname,prename) VALUES (NULL,'Waagner','Lilian')$ -- Insert some products INSERT INTO products(prod_id,name,store,stock) VALUES (NULL,'Fernseher','Berlin',250)$ INSERT INTO products(prod_id,name,store,stock) VALUES (NULL,'Fernseher','Potsdam',200)$ INSERT INTO products(prod_id,name,store,stock) VALUES (NULL,'Fernseher','Bremen',100)$ INSERT INTO products(prod_id,name,store,stock) VALUES (NULL,'Radio','Berlin',100)$ -- Insert some orders INSERT INTO orders(user_id,product,amount) VALUES (1,'Fernseher',1300)$ |