22-2014-Store… · Web vie

31
Stored Programs, Triggers The following uses MySQL from the Murach MySQL textbook. A stored procedure is a program that is stored with the database and can be called from an application that has access to the database. A trigger is a special type of stored procedure that executes automatically when a specific statement (INSERT, UPDATE, or DELETE) is executed on a specific table. Example Download the Murach_mySQL_allFiles.zip file and unzip it. Run the exe file. Extract the files to your desktop or network drive. Start the MySQL service (mysql_start.bat). Start MySQLWorkbench. Create the sample files. In the db_setup folder, load the following script: SScreate_databases.sql. Refresh the Schemas window on the left side. The following databases should be available: AP, EX, and OM. Load the following SQL script: book_scripts/ch13/13-01.sql. Examine the code: USE ap; DROP PROCEDURE IF EXISTS test; -- Change statement delimiter from semicolon to double front slash DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE sum_balance_due DECIMAL(9, 2); SELECT SUM(invoice_total - payment_total - credit_total) INTO sum_balance_due FROM invoices WHERE vendor_id = 95; -- for testing, the vendor with an ID of 37 has a balance due IF sum_balance_due > 0 THEN SELECT CONCAT('Balance due: $', sum_balance_due) AS message; ELSE 6/21/2022 document.docx Page 1 of 31

Transcript of 22-2014-Store… · Web vie

Page 1: 22-2014-Store… · Web vie

Stored Programs, TriggersThe following uses MySQL from the Murach MySQL textbook.A stored procedure is a program that is stored with the database and can be called from an application that has access to the database. A trigger is a special type of stored procedure that executes automatically when a specific statement (INSERT, UPDATE, or DELETE) is executed on a specific table.

ExampleDownload the Murach_mySQL_allFiles.zip file and unzip it. Run the exe file. Extract the files to your desktop or network drive.Start the MySQL service (mysql_start.bat).Start MySQLWorkbench.Create the sample files. In the db_setup folder, load the following script: SScreate_databases.sql.Refresh the Schemas window on the left side. The following databases should be available: AP, EX, and OM.Load the following SQL script: book_scripts/ch13/13-01.sql. Examine the code:

USE ap;

DROP PROCEDURE IF EXISTS test;

-- Change statement delimiter from semicolon to double front slashDELIMITER //

CREATE PROCEDURE test()BEGIN DECLARE sum_balance_due DECIMAL(9, 2);

SELECT SUM(invoice_total - payment_total - credit_total) INTO sum_balance_due FROM invoices WHERE vendor_id = 95; -- for testing, the vendor with an ID of 37 has a balance due

IF sum_balance_due > 0 THEN SELECT CONCAT('Balance due: $', sum_balance_due) AS message; ELSE SELECT 'Balance paid in full' AS message; END IF; END//

-- Change statement delimiter from double front slash to semicolonDELIMITER ;

CALL test();

5/7/2023 document.docx Page 1 of 24

Page 2: 22-2014-Store… · Web vie

What this stored procedure does:Specify which database to use.

USE ap;

If a stored procedure called test currently exists, drop (delete) it.DROP PROCEDURE IF EXISTS test;

Change the delimiter from ";" to "//". This allows us to use semicolons in our stored procedure.

DELIMITER //

Create the procedure using the code between the BEGIN and END keywords. There are no arguments passed to this procedure.

CREATE PROCEDURE test()BEGIN

Declare a local variable DECLARE sum_balance_due_var DECIMAL(9, 2);

Select data from the INVOICES table and store the value in the sum_balance_due variable. SELECT SUM(invoice_total - payment_total - credit_total) INTO sum_balance_due_var FROM invoices WHERE vendor_id = 95; -- for testing, the vendor with an ID of 37 has a balance due

Display an appropriate message IF sum_balance_due_var > 0 THEN SELECT CONCAT('Balance due: $', sum_balance_due_var) AS message; ELSE SELECT 'Balance paid in full' AS message; END IF;

Delimit the end of the procedureEND//

Change statement delimiter from double front slash back to semicolon.DELIMITER ;

Run the procedure.CALL test();

Run the procedure by clicking on the lightning bolt at the top of the SQL editor window. For vendor 95, the results should be "Balance paid in full".Replace the 95 with 37 (see the comment) and run it again. The results should be "Balance due: $224.00".

5/7/2023 document.docx Page 2 of 24

Page 3: 22-2014-Store… · Web vie

Language for writing stored proceduresTo display dataUse a select statement. If you don't store the result of the SELECT statement in a variable, it will be displayed in the output window.

To declare variablesThe syntax:

DECLARE variableName dataType;

To set a variable to a constant or expression:SET variable = constant;

SET variable = expression;

To set a variable to a selected value:SELECT column1, column2, …

INTO variable1, variable2, …;

A stored procedure that uses variables:USE ap;

DROP PROCEDURE IF EXISTS test;

DELIMITER //

CREATE PROCEDURE test()BEGIN DECLARE max_invoice_total DECIMAL(9,2); DECLARE min_invoice_total DECIMAL(9,2); DECLARE percent_difference DECIMAL(9,4); DECLARE count_invoice_id INT; DECLARE vendor_id_var INT; SET vendor_id_var = 95;

SELECT MAX(invoice_total), MIN(invoice_total), COUNT(invoice_id) INTO max_invoice_total, min_invoice_total, count_invoice_id FROM invoices WHERE vendor_id = vendor_id_var;

SET percent_difference = (max_invoice_total - min_invoice_total) / min_invoice_total * 100; SELECT CONCAT('$', max_invoice_total) AS 'Maximum invoice', CONCAT('$', min_invoice_total) AS 'Minimum invoice', CONCAT('%', ROUND(percent_difference, 2)) AS 'Percent difference', count_invoice_id AS 'Number of invoices';END//

DELIMITER ;

CALL test();

5/7/2023 document.docx Page 3 of 24

Page 4: 22-2014-Store… · Web vie

The output from this procedure is:Maximum Minimum Percent Number of

$46.21 $16.33 %182.98 6

IF statementsThe syntax for the IF statement:

IF Boolean THENStatements;

ELSEIF Boolean THENStatements;

ELSEStatements;

END IF;

The ELSEIF and ELSE are optional. Multiple statements must be separated by semicolons.

ExampleUSE ap;

DROP PROCEDURE IF EXISTS test;

DELIMITER //

CREATE PROCEDURE test()BEGIN DECLARE first_invoice_due_date DATE;

SELECT MIN(invoice_due_date) INTO first_invoice_due_date FROM invoices WHERE invoice_total - payment_total - credit_total > 0;

IF first_invoice_due_date < NOW() THEN SELECT 'Outstanding invoices overdue!'; ELSEIF first_invoice_due_date = SYSDATE() THEN SELECT 'Outstanding invoices are due today!'; ELSE SELECT 'No invoices are overdue.'; END IF;

END//

DELIMITER ;

CALL test();

5/7/2023 document.docx Page 4 of 24

Page 5: 22-2014-Store… · Web vie

CASE statementsThe CASE statement is similar to the switch statement in C#. Syntax:

CASE expressionWHEN expressionValue1 THEN

Statements;WHEN expressionValue2 THEN

Statements;…ELSE

StatementsEND CASE;

ExampleUSE ap;

DROP PROCEDURE IF EXISTS test;

DELIMITER //

CREATE PROCEDURE test()BEGIN DECLARE terms_id_var INT;

SELECT terms_id INTO terms_id_var FROM invoices WHERE invoice_id = 4;

CASE terms_id_var WHEN 1 THEN SELECT 'Net due 10 days' AS Terms; WHEN 2 THEN SELECT 'Net due 20 days' AS Terms; WHEN 3 THEN SELECT 'Net due 30 days' AS Terms; ELSE SELECT 'Net due more than 30 days' AS Terms; END CASE;

-- rewritten as a Searched CASE statement /* CASE WHEN terms_id_var = 1 THEN SELECT 'Net due 10 days' AS Terms; WHEN terms_id_var = 2 THEN SELECT 'Net due 20 days' AS Terms; WHEN terms_id_var = 3 THEN SELECT 'Net due 30 days' AS Terms; ELSE SELECT 'Net due more than 30 days' AS Terms;

5/7/2023 document.docx Page 5 of 24

Page 6: 22-2014-Store… · Web vie

END CASE; */

END//

DELIMITER ;

CALL test();

WHILE LoopsThe WHILE loop is similar to the while loop in other programming languages. Syntax:

WHILE Boolean DOStatements;

END WHILE;

ExampleThis procedure prints out i=1 | i=2 | i= 3 |

USE ap;

DROP PROCEDURE IF EXISTS test;

DELIMITER //

CREATE PROCEDURE test()BEGIN DECLARE i INT DEFAULT 1; DECLARE s VARCHAR(400) DEFAULT '';

-- WHILE loop WHILE i < 4 DO SET s = CONCAT(s, 'i=', i, ' | '); SET i = i + 1; END WHILE;

SELECT s AS message;

END//

DELIMITER ;

CALL test();

5/7/2023 document.docx Page 6 of 24

Page 7: 22-2014-Store… · Web vie

REPEAT loopsThe REPEAT loop tests at the bottom of the loop body and therefore the loop body is always executed at least once. The REPEAT loop also uses the condition for QUITTING, where the WHILE loop uses the condition for STAYING.

ExampleThis example does the same thing as the previous WHILE loop.

USE ap;

DROP PROCEDURE IF EXISTS test;

DELIMITER //

CREATE PROCEDURE test()BEGIN DECLARE i INT DEFAULT 1; DECLARE s VARCHAR(400) DEFAULT '';

-- REPEAT loop REPEAT SET s = CONCAT(s, 'i=', i, ' | '); SET i = i + 1; UNTIL i = 4 END REPEAT; SELECT s AS message;

END//

DELIMITER ;

CALL test();

5/7/2023 document.docx Page 7 of 24

Page 8: 22-2014-Store… · Web vie

CursorsSometimes you need to step through the result set of a SQL SELECT command one row at a time. A cursor is an object that allows you to iterate the records in a set. It has concepts of order and current record.To declare a cursor:

DECLARE cursorName CURSOR FOR selectStatement;

To declare an error handler for when no more rows exist in the cursor:DECLARE CONTINUE HANDLER FOR NOT FOUND handlerStatement;

To open a cursor:OPEN cursorName;

To get a row from a cursor:FETCH cursorName INTO variable1, variable2, …;

To close a cursor:CLOSE cursorName;

ExampleUSE ap;

DROP PROCEDURE IF EXISTS test;

DELIMITER //

CREATE PROCEDURE test()BEGIN DECLARE invoice_id_var INT; DECLARE invoice_total_var DECIMAL(9,2); DECLARE row_not_found TINYINT DEFAULT FALSE; DECLARE update_count INT DEFAULT 0;

DECLARE invoices_cursor CURSOR FOR SELECT invoice_id, invoice_total FROM invoices WHERE invoice_total - payment_total - credit_total > 0; DECLARE CONTINUE HANDLER FOR NOT FOUND SET row_not_found = TRUE;

OPEN invoices_cursor; WHILE row_not_found = FALSE DO FETCH invoices_cursor INTO invoice_id_var, invoice_total_var;

IF invoice_total_var > 1000 THEN UPDATE invoices SET credit_total = credit_total + (invoice_total * .1)

5/7/2023 document.docx Page 8 of 24

Page 9: 22-2014-Store… · Web vie

WHERE invoice_id = invoice_id_var;

SET update_count = update_count + 1; END IF; END WHILE; CLOSE invoices_cursor; SELECT CONCAT(update_count, ' row(s) updated.'); END//

DELIMITER ;

CALL test();

Declaring MySQL Condition HandlersA condition handler is instructions that are executed when an error occurs. In C# and Java, conditions (exceptions) are handled by using a try/catch block.There are more than 700 MySQL numbered error codes (numbers identifying specific errors). We will not consider them. However, we will look at the three named error conditions in MySQL.NOT FOUND: occurs when an attempt is made to SELECT or FETCH data and none is there.SQLEXCEPTION: occurs when any error condition other than NOT FOUND occurs.SQLWARNING: occurs when any error or warning condition other than NOT FOUND occurs.To declare a condition handler for a named error condition:

DECLARE {CONTINUE | EXIT} HANDLER

FOR {errorCode | namedCondition}

handlerStatements;

ExampleDECLARE CONTINUE HANDLER FOR NOT FOUND

SET row_not_found = TRUE;

Using MySQL condition handlers

ExampleThis stored procedure does not have an error handler.

USE ap;

DROP PROCEDURE IF EXISTS test;

DELIMITER //

CREATE PROCEDURE test()BEGIN

5/7/2023 document.docx Page 9 of 24

Page 10: 22-2014-Store… · Web vie

INSERT INTO general_Ledger_accounts VALUES (130, 'Cash'); SELECT '1 row was inserted.';END//

DELIMITER ;

CALL test();

The following error message appears:Error Code: 1062. Duplicate entry 'Cash' for key 'account_description'

ExampleThis stored procedure does have an error handler.

USE ap;

DROP PROCEDURE IF EXISTS test;

DELIMITER //

CREATE PROCEDURE test()BEGIN DECLARE duplicate_entry_for_key TINYINT DEFAULT FALSE; DECLARE CONTINUE HANDLER FOR sqlexception SET duplicate_entry_for_key = TRUE;

INSERT INTO general_Ledger_accounts VALUES (130, 'Cash'); IF duplicate_entry_for_key = TRUE THEN SELECT 'Row was not inserted. Duplicate key found.'; ELSE SELECT '1 row was inserted.'; END IF;

END//

DELIMITER ;

CALL test();

This will produce the message: 'Row was not inserted. Duplicate key found.'

Stored procedures with parametersTo allow parameters to be passed, declare a parameter list in parentheses after the procedure's name.

USE ap;

DROP PROCEDURE IF EXISTS update_invoices_credit_total;

DELIMITER //

CREATE PROCEDURE update_invoices_credit_total

5/7/2023 document.docx Page 10 of 24

Page 11: 22-2014-Store… · Web vie

( invoice_id_param INT, credit_total_param DECIMAL(9,2) )BEGIN DECLARE sql_error INT DEFAULT FALSE; DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET sql_error = TRUE;

START TRANSACTION;

UPDATE invoices SET credit_total = credit_total_param WHERE invoice_id = invoice_id_param;

IF sql_error = FALSE THEN COMMIT; ELSE ROLLBACK; END IF;END//

DELIMITER ;

CALL update_invoices_credit_total(56, 300);

Input and Output parametersParameters can be IN, OUT, or INOUT. The default is IN. An IN parameter is a value parameter. That is, a copy of its value is made and the procedure performs all operations on the copy. The original value remains unchanged. An OUT parameter is a reference parameter. That is, the address (reference) of the parameter is passed to the procedure, and the procedure uses the address to actually modify the original value. An OUT parameter need not be initialized before calling the procedure. An INOUT parameter is also a reference parameter that is used for two-way communication. It should be given an initial value before calling the procedure. If the initial value doesn't matter, use an OUT parameter.

ExampleUSE ap;

DROP PROCEDURE IF EXISTS update_invoices_credit_total;

DELIMITER //

CREATE PROCEDURE update_invoices_credit_total( IN invoice_id_param INT,

5/7/2023 document.docx Page 11 of 24

Page 12: 22-2014-Store… · Web vie

IN credit_total_param DECIMAL(9,2), OUT update_count INT)BEGIN DECLARE sql_error INT DEFAULT FALSE; DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET sql_error = TRUE;

START TRANSACTION; UPDATE invoices SET credit_total = credit_total_param WHERE invoice_id = invoice_id_param; IF sql_error = FALSE THEN SET update_count = 1; COMMIT; ELSE SET update_count = 0; ROLLBACK; END IF;END//

DELIMITER ;

CALL update_invoices_credit_total(56, 200, @row_count);

-- CALL update_invoices_credit_total(56, 0, @row_count);

SELECT CONCAT('row_count: ', @row_count) AS update_count;

A stored procedure that inserts a rowThe following inserts a row into the INVOICE table. Examine the contents of the INVOICE table and take note of the highest invoice number (should be 114). NOTE: Before running, comment out the last three lines of code (the one that causes an error, and the two labeled "clean-up").After running this re-examine the contents of the INVOICE table. There should be rows numbered 115 and 116. Then comment out the two valid CALL commands (after the "-- test" comment) and un-comment the third CALL command and re-run the procedure (it should produce an error). Then comment out the invalid CALL command (the one that produced an error) and un-comment the last two commands. Run the program again. This time, the final two "clean-up" commands will be executed and the rows that were just inserted (#115 and #116) will be deleted. Open the INVOICE table to verify this.

USE ap;

5/7/2023 document.docx Page 12 of 24

Page 13: 22-2014-Store… · Web vie

DROP PROCEDURE IF EXISTS insert_invoice;

DELIMITER //

CREATE PROCEDURE insert_invoice( vendor_id_param INT, invoice_number_param VARCHAR(50), invoice_date_param DATE, invoice_total_param DECIMAL(9,2), terms_id_param INT, invoice_due_date_param DATE)BEGIN DECLARE terms_id_var INT; DECLARE invoice_due_date_var DATE; DECLARE terms_due_days_var INT;

-- Validate paramater values IF invoice_total_param < 0 THEN SIGNAL SQLSTATE '22003' SET MESSAGE_TEXT = 'Invoice_total column must be positive.', MYSQL_ERRNO = 1264; ELSEIF invoice_total_param >= 1000000 THEN SIGNAL SQLSTATE '22003' SET MESSAGE_TEXT = 'Invoice_total column must be < 1,000,000.', MYSQL_ERRNO = 1264; END IF;

-- Set default values for parameters IF terms_id_param IS NULL THEN SELECT default_terms_id INTO terms_id_var FROM vendors WHERE vendor_id = vendor_id_param; ELSE SET terms_id_var = terms_id_param; END IF; IF invoice_due_date_param IS NULL THEN SELECT terms_due_days INTO terms_due_days_var FROM terms WHERE terms_id = terms_id_var; SELECT DATE_ADD(invoice_date_param, INTERVAL terms_due_days_var DAY) INTO invoice_due_date_var; ELSE SET invoice_due_date_var = invoice_due_date_param; END IF;

INSERT INTO invoices (vendor_id, invoice_number, invoice_date, invoice_total, terms_id, invoice_due_date)

5/7/2023 document.docx Page 13 of 24

Page 14: 22-2014-Store… · Web vie

VALUES (vendor_id_param, invoice_number_param, invoice_date_param, invoice_total_param, terms_id_var, invoice_due_date_var);END//

DELIMITER ;

-- testCALL insert_invoice(34, 'ZXA-080', '2012-01-18', 14092.59, 3, '2012-03-18');CALL insert_invoice(34, 'ZXA-082', '2012-01-18', 14092.59, NULL, NULL);

-- this statement raises an errorCALL insert_invoice(34, 'ZXA-083', '2012-01-18', -14092.59, NULL, NULL);

-- clean upSELECT * FROM invoices WHERE invoice_id >= 115;

DELETE FROM invoices WHERE invoice_id >= 115;

User VariablesA user variable is a variable that is globally available to the current user as long as the user remains connected to the server. User variables begin with the "@" symbol and are loosely typed (that is, you don't have to declare them to be of a specific type). Local variables and parameters maintain their value only until the end of the procedure in which they are declared.User variables maintain their value until the end of the current session (the user disconnects from the server).

ExampleThe following example first calls a procedure that sets the value of @count to the value of the parameter that was passed in (100). The second procedure adds 1 to the value of @count, giving 101.

USE ap;

DROP PROCEDURE IF EXISTS set_global_count;DROP PROCEDURE IF EXISTS increment_global_count;

DELIMITER //

CREATE PROCEDURE set_global_count( count_var INT )BEGIN SET @count = count_var; END//

CREATE PROCEDURE increment_global_count()BEGIN

5/7/2023 document.docx Page 14 of 24

Page 15: 22-2014-Store… · Web vie

SET @count = @count + 1;END//

DELIMITER ;

CALL set_global_count(100);CALL increment_global_count();

SELECT @count AS count_var

5/7/2023 document.docx Page 15 of 24

Page 16: 22-2014-Store… · Web vie

Dynamic SQLDynamic SQL involves creating a SQL command on the fly at run time by building an SQL statement.

ExampleThe following procedure builds a SQL command string from the parameters that were passed in and executes the command with an EXECUTE command.

USE ap;

DROP PROCEDURE IF EXISTS select_invoices;

DELIMITER //

CREATE PROCEDURE select_invoices( min_invoice_date_param DATE, min_invoice_total_param DECIMAL(9,2))BEGIN DECLARE select_clause VARCHAR(200); DECLARE where_clause VARCHAR(200); SET select_clause = "SELECT invoice_id, invoice_number, invoice_date, invoice_total FROM invoices "; SET where_clause = "WHERE "; IF min_invoice_date_param IS NOT NULL THEN SET where_clause = CONCAT(where_clause, " invoice_date > '", min_invoice_date_param, "'"); END IF;

IF min_invoice_total_param IS NOT NULL THEN IF where_clause != "WHERE " THEN SET where_clause = CONCAT(where_clause, "AND "); END IF; SET where_clause = CONCAT(where_clause, "invoice_total > ", min_invoice_total_param); END IF;

IF where_clause = "WHERE " THEN SET @dynamic_sql = select_clause; ELSE SET @dynamic_sql = CONCAT(select_clause, where_clause); END IF; PREPARE select_invoices_statement FROM @dynamic_sql;

5/7/2023 document.docx Page 16 of 24

Page 17: 22-2014-Store… · Web vie

EXECUTE select_invoices_statement; SELECT "---" AS MESSAGE; DEALLOCATE PREPARE select_invoices_statement; END//

DELIMITER ;

CALL select_invoices('2011-07-25', 100);

CALL select_invoices('2011-07-25', NULL);

CALL select_invoices(NULL, 1000);

CALL select_invoices(NULL, NULL);

Stored FunctionsA stored function is a stored procedure that returns a value. The syntax differs from a stored procedure in three ways: (1) Use CREATE FUNCTION instead of CREATE PROCEDURE. (2) Add RETURNS <dataType> after the parameter list. (3) Add a RETURN statement at the end of the function body that returns the result.

ExampleThis example accepts the name of a vendor (VARCHAR(50)) and returns the vendor_id number (INT) for that vendor.

USE ap;

DROP FUNCTION IF EXISTS get_vendor_id;

DELIMITER //

CREATE FUNCTION get_vendor_id(vendor_name_param VARCHAR(50))RETURNS INTBEGIN DECLARE vendor_id_var INT; SELECT vendor_id INTO vendor_id_var FROM vendors WHERE vendor_name = vendor_name_param; RETURN(vendor_id_var);END//

DELIMITER ;

SELECT invoice_number, invoice_totalFROM invoices

5/7/2023 document.docx Page 17 of 24

Page 18: 22-2014-Store… · Web vie

WHERE vendor_id = get_vendor_id('IBM');

Another function exampleThe following example uses a function that accepts a vendor's ID and uses it to query the INVOICES table and return the balance due.

USE ap;

DROP FUNCTION IF EXISTS get_balance_due;

DELIMITER //

CREATE FUNCTION get_balance_due(invoice_id_param INT)RETURNS DECIMAL(9,2)BEGIN DECLARE balance_due_var DECIMAL(9,2); SELECT invoice_total - payment_total - credit_total INTO balance_due_var FROM invoices WHERE invoice_id = invoice_id_param; RETURN balance_due_var;END//

DELIMITER ;

SELECT vendor_id, invoice_number, get_balance_due(invoice_id) AS balance_due FROM invoicesWHERE vendor_id = 37;

Triggers A trigger is a stored program that is executed by the DBMS whenever a specified event

occurs on a specified table or view Access does not have triggers. MySQL does. Three trigger types: BEFORE, INSTEAD OF, and AFTER Each type can be declared for Insert, Update, and Delete Resulting in a total of nine trigger types Oracle supports all nine trigger types SQL Server supports six trigger types (only for INSTEAD OF and AFTER triggers)

Firing Triggers When a trigger is fired, the DBMS supplies

– Old and new values for the update– New values for inserts– Old values for deletions

The way the values are supplied depends on the DBMS product Trigger applications:

5/7/2023 document.docx Page 18 of 24

Page 19: 22-2014-Store… · Web vie

– Providing default values (Slide 7-51)– Checking validity (Slide 7-52, 7-53)– Updating views – Enforcing referential integrity actions

A Before TriggerA Before trigger gets executed before an event occurs (i.e., before an INSERT, UPDATE, or DELETE). In all trigger examples, the word NEW refers to the record being inserted, updated, or deleted.

ExampleThis creates a trigger that gets fired before the VENDORS table is UPDATEd. The name of the trigger is vendors_before_update. It gets executed before an UPDATE event occurs on the VENDORS table. The UPDATE command sets the state field of VENDOR #1 to 'wi' (Wisconsin). The trigger makes sure that all vendor_state values are capitalized before storing them in the table.

USE ap;

DROP TRIGGER IF EXISTS vendors_before_update;

DELIMITER //

CREATE TRIGGER vendors_before_update BEFORE UPDATE ON vendors FOR EACH ROW BEGIN SET NEW.vendor_state = UPPER(NEW.vendor_state);END//

DELIMITER ;

UPDATE vendorsSET vendor_state = 'wi'WHERE vendor_id = 1;

SELECT vendor_name, vendor_stateFROM vendorsWHERE vendor_id = 1;

Enforcing data consistency with triggersA common use of triggers is to enforce data consistency.

ExampleThe following trigger checks to make sure that the sum of the line items in the INVOICE_LINE_ITEMS table matches the total in the INVOICES table. It gets executed before the table is updated, and if the numbers do not match, then it raises an error condition:

SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = 'Line item total must match invoice total.';

5/7/2023 document.docx Page 19 of 24

Page 20: 22-2014-Store… · Web vie

This is the body of the trigger.USE ap;

DROP TRIGGER IF EXISTS invoices_before_update;

DELIMITER //

CREATE TRIGGER invoices_before_update BEFORE UPDATE ON invoices FOR EACH ROWBEGIN DECLARE sum_line_item_amount DECIMAL(9,2); SELECT SUM(line_item_amount) INTO sum_line_item_amount FROM invoice_line_items WHERE invoice_id = NEW.invoice_id; IF sum_line_item_amount != NEW.invoice_total THEN SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = 'Line item total must match invoice total.'; END IF;END//

DELIMITER ;

UPDATE invoicesSET invoice_total = 600WHERE invoice_id = 100;

SELECT invoice_id, invoice_total, credit_total, payment_total FROM invoices WHERE invoice_id = 100;

An AFTER triggerAn AFTER trigger gets executed after some event happens.

ExampleThe following script creates an invoices_audit table and creates an AFTER INSERT trigger and an AFTER DELETE trigger. When a record is inserted into the table, the AFTER INSERT trigger creates a record in the INVOICES_AUDIT table making a record of the insertion. And the AFTER DELETE trigger creates another record in the INVOICES_AUDIT table making a record of the deletion.

USE ap;

DROP TABLE IF EXISTS invoices_audit;

CREATE TABLE invoices_audit( vendor_id INT NOT NULL,

5/7/2023 document.docx Page 20 of 24

Page 21: 22-2014-Store… · Web vie

invoice_number VARCHAR(50) NOT NULL, invoice_total DECIMAL(9,2) NOT NULL, action_type VARCHAR(50) NOT NULL, action_date DATETIME NOT NULL);

DROP TRIGGER IF EXISTS invoices_after_insert;DROP TRIGGER IF EXISTS invoices_after_delete;

DELIMITER //

CREATE TRIGGER invoices_after_insert AFTER INSERT ON invoices FOR EACH ROWBEGIN INSERT INTO invoices_audit VALUES (NEW.vendor_id, NEW.invoice_number, NEW.invoice_total, 'INSERTED', NOW());END//

CREATE TRIGGER invoices_after_delete AFTER DELETE ON invoices FOR EACH ROWBEGIN INSERT INTO invoices_audit VALUES (OLD.vendor_id, OLD.invoice_number, OLD.invoice_total, 'DELETED', NOW());END//

DELIMITER ;

-- make sure that there is at least one record to deleteINSERT INTO invoices VALUES (115, 34, 'ZXA-080', '2011-08-30', 14092.59, 0, 0, 3, '2011-09-30', NULL);

DELETE FROM invoices WHERE invoice_id = 115;

SELECT * FROM invoices_audit;

-- clean up-- DELETE FROM invoices_audit;

Viewing and Dropping TriggersTo get all information about the triggers in a database:

SHOW TRIGGERS

OrSHOW TRIGGERS IN <databaseName>

5/7/2023 document.docx Page 21 of 24

Page 22: 22-2014-Store… · Web vie

MySQL doesn't provide a way to edit a trigger. You must DROP an existing trigger and replace it with a new one.To DROP a trigger:

DROP TRIGGER IF EXISTS <triggerName>

EventsAn event is a named block of code that executes, or fires, according to the event scheduler.Events only fire if the Event Scheduler is turned on. To see if the Event Scheduler is turned on:

SHOW VARIABLES;

This returns a large number of variables that are associated with the current database. We can restrict the values returned by adding a LIKE clause:

SHOW VARIABLES LIKE 'e%';

The Event Schedule should be turned off by default. To turn it on:SET GLOBAL event_scheduler = ON;

An Event that executes only onceThe following CREATE EVENT statement creates an event that executes only once, one month from the time the script is run (ON SCHEDULE AT NOW() + INTERVAL 1 MONTH):

SHOW VARIABLES LIKE 'event_scheduler';

SET GLOBAL event_scheduler = ON;

DROP EVENT IF EXISTS one_time_delete_audit_rows;

DELIMITER //

CREATE EVENT one_time_delete_audit_rowsON SCHEDULE AT NOW() + INTERVAL 1 MONTHDO BEGIN DELETE FROM invoices_audit WHERE action_date < NOW() - INTERVAL 1 MONTH LIMIT 100;END//

END//

DELIMITER ;

If you run the above script, nothing will happen until a month from now. Change the "ON SCHEDULE" line to the following:

ON SCHEDULE AT NOW()

And change the DELETE FROM line to this: DELETE FROM invoices_audit WHERE action_date < NOW()

Then run the script and it will delete all records in the INVOICES_AUDIT table.

5/7/2023 document.docx Page 22 of 24

Page 23: 22-2014-Store… · Web vie

An Event that occurs every monthGo back and re-run the script that executes a CREATE AFTER trigger to re-populate the INVOICES_AUDIT table. The following script will run once a month.

SHOW VARIABLES LIKE 'event_scheduler';

SET GLOBAL event_scheduler = ON;

DROP EVENT IF EXISTS monthly_delete_audit_rows;

DELIMITER //

CREATE EVENT monthly_delete_audit_rowsON SCHEDULE EVERY 1 MONTHSTARTS '2014-04-24 00:00:00'DO BEGIN DELETE FROM invoices_audit WHERE action_date < NOW() - INTERVAL 1 MONTH LIMIT 100;END//

DELIMITER ;

If you run the above script, nothing happens! There are two reasons for this: (1) it starts at midnight of the current day (if the current day is 4/24/2014), and it is no longer midnight, so we have passed the first execution date. (2) The condition for deletion is that the record must be a month old or more. So make the following changes.Change the STARTS line to this:

STARTS NOW()

And change the DELETE FROM line to:DELETE FROM invoices_audit WHERE action_date < NOW()

How to view, alter, or drop eventsTo view all events:

SHOW EVENTS

To view all events on a particular database:SHOW EVENTS IN ap

To disable an event:ALTER EVENT monthly_delte_audit_rows DISABLE

To enable an event:ALTER EVENT monthly_delete_audit_rows ENABLE

To rename an event:ALTER EVENT one_time_delete_audit_rows RENAME TO one_time_delete_audits

To drop an event:DROP EVENT IF EXISTS monthly_delet_audit_rows

5/7/2023 document.docx Page 23 of 24

Page 24: 22-2014-Store… · Web vie

Review of Stored Procedures (slide 7-54) A stored procedure is a program that is stored within the database and is compiled

when used– In Oracle, it can be written in PL/SQL or Java– In SQL Server, it can be written in TRANSACT-SQL– NEW in 2005: can be written in any .NET language, which means that T-SQL

is on the way out. Stored procedures can receive input parameters and they can return results Stored procedures can be called from

– Programs written in standard languages, e.g., Java, C#– Scripting languages, e.g., JavaScript, VBScript– SQL command prompt, e.g., SQL Plus, Query Analyzer

Stored Procedure Advantages (slide 7-55) Greater security as stored procedures are always stored on the database server Decreased network traffic SQL can be optimized by the DBMS compiler Code sharing resulting in

– Less work– Standardized processing– Specialization among developers

Using SQL In Application Code SQL can be embedded in application programs, triggers, and stored procedures Several SQL statements need to be executed to populate an external view The application program causes the statements to be executed and then displays the

results of the query in the form’s grid controls The application program also processes and coordinates user actions on a form, e.g.

populating a drop-down list box The particulars by which SQL code is inserted into applications depend on the language

and data-manipulation methodology used

5/7/2023 document.docx Page 24 of 24