CS669_Lab4ExplanationForSQLServer.pdf

   

Page 1 of 34  Copyright 2012‐2015, 2017‐2019 Boston University. All Rights Reserved. 

 

MET CS 669 Database Design and Implementation for Business Lab 4 Explanation: Procedural SQL

Lab 4 Explanations For SQL Server  This explanation document illustrates how to correctly execute each SQL construct step‐by‐step for Lab 4, and 

explains important theoretical and practical details. Before completing a step, read its explanation here first. 

  Use this explanations document only if you are using SQL Server. If you are using Oracle or PostgreSQL,  explanations for those DBMS’ are available in a different document in the assignments section of the course.     

Page 2 of 34 

Table of Contents Section One – Aggregating Data ............................................................................................................................. 4 

STEP 1 .................................................................................................................................................................. 4 

Code: Creating Customer Order Schema ........................................................................................................ 5 

Screenshot: Creating the Customer Order Schema ........................................................................................ 6 

Code: Inserting Customer Order Data ............................................................................................................ 7 

Screenshot: Inserting Customer Order Data .................................................................................................. 8 

STEP 2 .................................................................................................................................................................. 9 

Code: Creating add_customer_harry Procedure ............................................................................................ 9 

Screenshot: Creating add_customer_harry Procedure ................................................................................ 10 

Code: Executing add_customer_harry Procedure ........................................................................................ 10 

Screenshot: Executing add_customer_harry Procedure .............................................................................. 11 

Screenshot: Customer Table After Execution ............................................................................................... 11 

STEP 3 ................................................................................................................................................................ 12 

Screenshot: Second Execution of add_customer_harry .............................................................................. 12 

STEP 4 ................................................................................................................................................................ 13 

Code: Creating add_customer Procedure .................................................................................................... 13 

Screenshot: Creating add_customer Procedure ........................................................................................... 14 

Code: Executing add_customer Procedure .................................................................................................. 14 

Screenshot: Executing add_customer Procedure ......................................................................................... 14 

Screenshot: Listing Customer Table After Add ............................................................................................. 15 

STEP 5 ................................................................................................................................................................ 16 

Pseudocode for Basic Variable Use ............................................................................................................... 16 

Code: Creating add_item Procedure ............................................................................................................ 17 

Screenshot: Compiling and Executing add_item .......................................................................................... 18 

Screenshot: Listing Item Table ...................................................................................................................... 18 

STEP 6 ................................................................................................................................................................ 20 

Code: ADD_LINE_ITEM procedure ................................................................................................................ 20 

Screenshot: Compiling and Executing add_line_item .................................................................................. 21 

Screenshot: Listing Line_item After Execution ............................................................................................. 22 

STEP 7 ................................................................................................................................................................ 23 

Code: DELETE_CUSTOMER procedure .......................................................................................................... 24 

Screenshot: Deleting Customer John Smith ................................................................................................. 25 

Screenshot: Listing John Smith After Deletion ............................................................................................. 25 

Section Two – Triggers .......................................................................................................................................... 26 

STEP 8 ................................................................................................................................................................ 26 

Code: No Negative Balance Validation Trigger ............................................................................................. 26 

Page 3 of 34 

Screenshot: Compile the No Negative Balance Trigger ................................................................................ 28 

Screenshot: Test the No Negative Balance Trigger ...................................................................................... 28 

STEP 9 ................................................................................................................................................................ 29 

Code: Correct Line Price Validation Trigger .................................................................................................. 29 

Screenshot: Test the No Negative Balance Trigger ...................................................................................... 30 

STEP 10 .............................................................................................................................................................. 31 

Code: Create the Item_price_history Table .................................................................................................. 32 

Code: The Item_price_history Trigger .......................................................................................................... 32 

Screenshot: Compilation and Update for Item_price_history ..................................................................... 33 

Screenshot: Listing Item_price_history ........................................................................................................ 33 

       

Page 4 of 34 

Section One – Aggregating Data   

    To help demonstrate how to complete the commands in this section, we work with simplified customer order  schema which tracks customers and their orders of items. The schema itself is illustrated below.    

  The Item table contains items that can be purchased, with a primary key, a description of the item, a price,  and an item_code which is an identifier by which the item can be referenced. The Customer table contains  basic information on customers such as their first and last name, and a total balance which they owe. The  Customer_order table contains basic information about an order itself, including a reference to the customer  who placed the order, the sum total for the order, and the date the order was placed. The Line_item table  contains information on individual lines in the order, including a reference to the item that was purchased, the  quantity that was purchased, and the total amount for that line (for example, if an item costs $10 and 2 of  them were purchased, the total amount for the line would be $20).    We create the customer odder schema illustrated above in its entirety, including all primary and foreign key  constraints, with the SQL below.    Here’s the code we used for creating the schema.     

   

STEP 1

Create the tables in the social networking schema, including all of their columns,  datatypes, and constraints. Populate the tables with data, ensuring that there are at  least 5 people, at least 8 posts, and at least 4 likes. Most of the fields are self‐ explanatory. As far as the “content” field in Post, make them whatever you like, such  as “Take a look at these new pics” or “Just arrived in the Bahamas”, and set the  summary as the first 12 characters of the content, followed by “…”. 

Page 5 of 34 

                                                      Below are screenshots of creating the schema.   

CREATE TABLE Customer( customer_id DECIMAL(12) NOT NULL, customer_first VARCHAR(32), customer_last VARCHAR(32), customer_total DECIMAL(12, 2), PRIMARY KEY (customer_ID)); CREATE TABLE Item( item_id DECIMAL(12) NOT NULL, description VARCHAR(64) NOT NULL, price DECIMAL(10, 2) NOT NULL, item_code VARCHAR(4) NOT NULL, PRIMARY KEY (item_id)); CREATE TABLE Customer_order ( order_id DECIMAL(12) NOT NULL, customer_id DECIMAL(12) NOT NULL, order_total DECIMAL(12,2) NOT NULL, order_date DATE NOT NULL, PRIMARY KEY (ORDER_ID), FOREIGN KEY (CUSTOMER_ID) REFERENCES customer); CREATE TABLE Line_item( order_id DECIMAL(12) NOT NULL, item_id DECIMAL(12) NOT NULL, item_quantity DECIMAL(10) NOT NULL, line_price DECIMAL(12,2) NOT NULL, PRIMARY KEY (ORDER_ID, ITEM_ID), FOREIGN KEY (ORDER_ID) REFERENCES customer_order, FOREIGN KEY (ITEM_ID) REFERENCES item);

Code: Creating Customer Order Schema

Page 6 of 34 

Screenshot: Creating the Customer Order Schema

SQL Server

  We then insert the following made up data into the tables, making sure to relate them properly with primary  and foreign keys.       

Page 7 of 34 

                                                                    Below are screenshot snippets of inserting the values. There are too many commands to fit in one screenshot,  and screenshotting them all is not important in this context.   

--Customers INSERT INTO customer VALUES(1,'John','Smith',0); INSERT INTO customer VALUES(2,'Mary','Berman',0); INSERT INTO customer VALUES(3,'Elizabeth','Johnson',0); INSERT INTO customer VALUES(4,'Peter','Quigley',0); INSERT INTO customer VALUES(5,'Stanton','Hurley',0); INSERT INTO customer VALUES(6,'Yvette','Presley',0); INSERT INTO customer VALUES(7,'Hilary','Marsh',0); --Items INSERT INTO item VALUES(1,'Plate',10, 'P001'); INSERT INTO item VALUES(2,'Bowl',11, 'B002'); INSERT INTO item VALUES(3,'Knife',5, 'K003'); INSERT INTO item VALUES(4,'Fork',5, 'F004'); INSERT INTO item VALUES(5,'Spoon',5, 'S005'); INSERT INTO item VALUES(6,'Cup',12, 'C006'); --Orders INSERT INTO customer_order VALUES(1,1,506,CAST('18-DEC-2005' AS DATE)); INSERT INTO customer_order VALUES(2,1,1000,CAST('17-DEC-2005' AS DATE)); INSERT INTO customer_order VALUES(3,3,15,CAST('19-DEC-2005' AS DATE)); INSERT INTO customer_order VALUES(4,3,15,CAST('20-DEC-2005' AS DATE)); INSERT INTO customer_order VALUES(5,2,1584,CAST('18-DEC-2005' AS DATE)); INSERT INTO customer_order VALUES(6,4,100,CAST('17-DEC-2005' AS DATE)); INSERT INTO customer_order VALUES(7,5,40,CAST('18-DEC-2005' AS DATE)); INSERT INTO customer_order VALUES(8,1,10,CAST('19-DEC-2005' AS DATE)); --Line Items INSERT INTO line_item VALUES(1,1,10,100); INSERT INTO line_item VALUES(1,5,2,10); INSERT INTO line_item VALUES(1,2,36,396); INSERT INTO line_item VALUES(2,1,95,950); INSERT INTO line_item VALUES(2,3,10,50); INSERT INTO line_item VALUES(3,4,3,15); INSERT INTO line_item VALUES(4,4,3,15); INSERT INTO line_item VALUES(5,6,132,1584); INSERT INTO line_item VALUES(6,1,10,100); INSERT INTO line_item VALUES(7,5,5,25); INSERT INTO line_item VALUES(7,4,3,15); INSERT INTO line_item VALUES(8,5,2,10);

Code: Inserting Customer Order Data

Page 8 of 34 

Screenshot: Inserting Customer Order Data

SQL Server

         

Page 9 of 34 

    To demonstrate something similar, we’ll create a stored procedure named “add_customer_harry” that adds a  customer named Harry Joker to the customer order schema. Below is code for this procedure.                    Let us go through code line by line and discuss the meaning.     

Line 1: CREATE PROCEDURE ADD_CUSTOMER_HARRY 

The CREATE PROCEDURE phrase indicates to SQL Server that a stored procedure  is to be created.  All of the words in this phrase are SQL keywords, meaning that  they are words predefined in the SQL language to have a specific meaning. We  must use keywords exactly as they are defined by the language in order to tell  the language what we want to do.    The ADD_CUSTOMER_HARRY word is the name of the stored procedure. This  name is an identifier, which means that the language allows us to define our  own name. SQL Server does restrict the length of identifiers to be no longer than  128 characters, and has some character restrictions, for example, that identifiers  should not contain the “%” character. Within these restrictions we can specify  any name we like. Of course, it is best to name a stored procedure reasonably  based upon the function it performs. For this procedure, I chose the name  ADD_CUSTOMER_HARRY because the logic of the procedure inserts a customer  named “Harry” into the Customer table. SQL Server relaxes many of its  restrictions on an identifier if the identifier is quoted or enclosed in brackets;  however, it is best not to use an identifier that must be quoted each time it is  used, so we stay within the regular identifier guidelines.  

Line 2: AS    AS is a SQL keyword that is required by the language to define a stored 

procedure, but otherwise has no significant meaning. SQL is defined to be  natural for English speakers, so the full phrase CREATE PROCEDURE ADD_CUSTOMER_HARRY AS leads an English speaker to naturally think that the  definition of the stored procedure follows the AS keyword. 

Line 3: BEGIN 

   

STEP 2 Create a stored procedure named “add_zana_sage” which adds a person named “Zana  Sage” to the Person table, then execute the stored procedure. List out the rows in the  Person table to show that Zana Sage has been added. 

CREATE PROCEDURE ADD_CUSTOMER_HARRY AS BEGIN INSERT INTO CUSTOMER (CUSTOMER_ID,CUSTOMER_FIRST,CUSTOMER_LAST,CUSTOMER_TOTAL) VALUES(8, 'Harry', 'Joker', 0); END;

Code: Creating add_customer_harry Procedure

Page 10 of 34 

  This word is optional when creating stored procedures in SQL Server. Its use  helps in the readability of the stored procedure, so that one can determine at a  glance where the content of the stored procedure begins. If the BEGIN word is  used, it must be coupled with the END keyword described below. 

Lines 4-5: INSERT INTO CUSTOMER (customer_id,customer_first,customer_last,customer_total) VALUES(8, ’Harry’, ’Joker’, 0);

  You might think this command looks familiar, because it does! It is just a  standard SQL statement. Wait a minute. The procedural language and the SQL  language are two different languages, right? So why is this SQL statement inside  of a stored procedure that uses the procedural language? Simple! Because  certain SQL commands can be embedded inside of the procedural language in  the right context. In this case, I have embedded an insert statement that inserts  a new customer “Harry” into the Customer table. This way, when you execute  this stored procedure, the stored procedure will insert the new customer on  your behalf, without the need for you to type the SQL command yourself. 

Line 6: END;    The END keyword is optional and is only required if the BEGIN keyword is used. 

This word helps in the readability of the stored procedure, so that one can  determine at a glance where the content of the stored procedure ends.  Likewise, the semicolon after the END keyword is optional. 

  Now to be clear, the above code, when executed, creates the stored procedure so that it’s available for use.  Below is a screenshot of creating this stored procedure.   

Screenshot: Creating add_customer_harry Procedure

SQL Server

    Notice that the output indicates that the commands completed successfully. This is a technical way for the  DBMS to state that the procedure has been processed and is available for use.    Now that we have created the stored procedure, we need to execute it for its code to take effect.  First, let’s  look at the code to do so.               

EXECUTE ADD_CUSTOMER_HARRY;

Code: Executing add_customer_harry Procedure

Page 11 of 34 

The EXECUTE keyword can be used to execute many different kinds of objects in SQL Server, and in this  context we use to execute the stored procedure we have created. We used the name of our stored procedure,  ADD_CUSTOMER_HARRY, to specify which stored procedure to execute.    Below is a screenshot of executing this code.   

Screenshot: Executing add_customer_harry Procedure

SQL Server

  The output states “1 row(s) affected)” to indicate that the code within the anonymous block has executed (by  inserting 1 row). We can now select all rows from the Customer table to make sure that our stored procedure  inserted a row as we would expect.   

Screenshot: Customer Table After Execution

SQL Server

  Sure enough, we see that the customer “Harry Joker” is listed in the table as the last row listed. We have now  successfully created and executed a stored procedure!      You can now perform a similar series of steps in order to add Zana to the Person table through use of a stored  procedure.             

Page 12 of 34 

   

To demonstrate this, we’ll attempt to execute the add_customer_harry procedure a second time. Realistically  the ADD_CUSTOMER_HARRY stored procedure can be executed only once successfully. Inside the procedure,  we placed the literal value “8” for the customer_id column, the literal value “Harry” for the customer_first  column, the literal value “Joker” for the customer_last column, and the literal value “0” for the customer_total  column. This placement is termed “hardcoding” by computer programmers, a term which means that a value  is embedded directly into the source of a program, instead of obtaining the value dynamically. Attempting to  execute it a second time would result at the very least in a primary key violation, since it’s attempting to insert  the same primary key value a second time. This is illustrated below.   

Screenshot: Second Execution of add_customer_harry

SQL Server

  Notice that the error is indicating that the primary key constraint would be violated by this execution, and it  even lists out the duplicate value, 8. The insert was blocked. It’s a good thing the constraint blocked it actually,  because otherwise we would have inserted the same customer twice, possibly without realizing it.     You will get similar results when you execute your stored procedure a second time.     

   

STEP 3 Attempt to execute the “add_zana_sage” procedure a second time. Summarize what  the issue is from the error that occurs as a result. 

Page 13 of 34 

   

It is best to make our stored procedures reusable, so that they can be executed wherever the logic contained  in them is needed. The fact that our ADD_CUSTOMER_HARRY stored procedure cannot be executed multiple  times makes it less valuable as a resource. To achieve reusability, instead of hardcoding literal values in a  procedure, we use placeholders which instruct the DBMS to use whatever value is given to the stored  procedure when it is executed. These placeholders are termed parameters. At a minimum, a parameter has a  name, which is an identifier by which it is referred, and a datatype, which determines the set of legal values  that can be assigned to the parameter.    For example, if instead of hardcoding the value “8” for the customer_id column, we defined a parameter  named “cus_id_arg” with a datatype of “DECIMAL”, the particular value can be specified when the stored  procedure is executed. Below is an ADD_CUSTOMER stored procedure that makes use of several parameters  and is therefore reusable, allowing us to add any customer rather than just one specific customer. Comments  next to the parameters help explain their purpose.                            Notice that instead of hardcoding particular values in the insert statement, the parameter names are  referenced instead, particularly in the VALUES(@cus_id_arg,@first_name_arg,@last_name_arg,0) part of the  insert statement. Instead of “8”, the parameter cus_id_arg is used. Instead of “Harry” and “Joker”, the  first_name_arg and last_name_arg parameters are used. Essentially, this is instructing the SQL engine to insert  whatever values have been passed into the stored procedure, rather than specific values. Creating the stored  procedure in SQL Server looks as follows.   

   

STEP 4 Create a reusable stored procedure named “add_person” that uses parameters and  allows you to insert any new person into the Person table. Execute the stored  procedure with a person of your choosing, then list out the Person table to show  that the person was added to the table. 

CREATE PROCEDURE ADD_CUSTOMER -- Create a new customer @cus_id_arg DECIMAL, -- The new customer's ID. @first_name_arg VARCHAR(30), -- The new customer’s first name. @last_name_arg VARCHAR(40) -- The new customer's last name. AS -- This "AS" is required by the syntax of stored procedures. BEGIN -- Insert the new customer with a 0 balance. INSERT INTO CUSTOMER (CUSTOMER_ID,CUSTOMER_FIRST,CUSTOMER_LAST,CUSTOMER_TOTAL) VALUES(@cus_id_arg,@first_name_arg,@last_name_arg,0); END;

Code: Creating add_customer Procedure

Page 14 of 34 

Screenshot: Creating add_customer Procedure

SQL Server

    The same as with the add_harry procedure, the DBMS indicates the procedure was compiled with a generic  error message.    When the stored procedure is executed, the values to be used are specified by the executor. The syntax is  similar to calling a stored procedure with no parameters, with the parameters themselves comma separated  within parentheses. The code for adding a customer named “Mary Smith” with a unique primary key, using  the stored procedure, is given below.            Notice that “9, ‘Mary’, ‘Smith’” part of the stored procedure call which specifies what parameters to use. The  order in which the parameters appear matters, as this ordering is correlated with the ordering the parameters  are declared in the stored procedure. Since “9” comes first, it’s matched to cus_id_arg comes first in the  stored procedure declaration. Since “Mary” comes second, it’s matched to first_name_arg, and “Smith” is  matched to last_name_arg. Using this approach, you need to know the order in which the parameters are  declared in the stored procedure in order to execute the stored procedure.     Executing the stored procedure gives us the same confirmation we saw with the prior execution of the  “add_customer_harry” procedure, as shown below.   

Screenshot: Executing add_customer Procedure

SQL Server

  Just to make sure Mary Smith made it in, we’ll list out our Customer table again, illustrated below.   

EXECUTE ADD_CUSTOMER 9,'Mary','Smith'

Code: Executing add_customer Procedure

Page 15 of 34 

Screenshot: Listing Customer Table After Add

SQL Server

  Notice that Mary Smith is listed in the table with an ID of 9, just as we specified when executing the  add_customer procedure. We could add many more customers using this stored procedure just by changing  the parameter values given to the stored procedure! Hopefully this gives you an idea of the usefulness of  parameterized stored procedures. Code the logic once, then execute the stored procedure whenever you  need it. Of course, the logic we put into this procedure is a simple insert statement, but you can put much  more than that into more complex procedures, which we’ll look at in subsequent steps.    You can now use a similar approach to the one demonstrated to address Step 4.     

Page 16 of 34 

   

You learned in Step 4 how to create reusable stored procedures by using parameters, so using a variable is the  new skill for this step. The basic concepts of variables are not too complex. A variable is a named placeholder  that can store a value, and can later be referenced by name to retrieve the stored value.    Let’s take an example from the customer order schema we have been using throughout this section. What if,  instead of hardcoding the item code for an item, we wanted the database to assign it a unique value? What  we could do is, create a variable, calculate the item code and store it in the variable, then reference the  variable when inserting into the item table. Let’s first illustrate this in pseudo‐code so that you understand the  concepts.                    In line 1 in the pseudocode, the v_item_code variable is declared. A variable declaration identifies the  existence, name, and datatype of the variable. In programmatic SQL (and also in many programming  languages), a variable cannot be used unless it is first declared. Its name identifies how the variable will be  later referenced, and its datatype indicates what kind of value it can store (such as character string, number,  date, etc …)    In line 2 in the pseudocode, the variable is assigned a value. A variable assignment places a value into the  variable. Referencing the variable later will use the value assigned.    In line 3, the variable is used by referencing it by name. A variable reference uses whatever value is in the  variable. Of course, the references to the variable are what makes a variable useful, since simply declaring one  and assigning a value to it would not be useful alone.     Now that you understand the pseudocode, let’s look at the stored procedure code then analyze the lines.     

   

STEP 5

Create a reusable stored procedure named “add_post” that uses parameters and  allows you to insert any new post into the Post table. Instead of passing in the  summary as a parameter, derive the summary from the content, storing the  derivation temporarily in a variable (which is then used as part of the insert  statement). Execute the stored procedure to add a post of your choosing, then list  out the Post table to show that the addition succeeded. 

1: Declare variable v_item_code as a character string

2: Calculate a unique value and store it in the v_item_code variable

3: Insert whatever value is in the v_item_code variable into the item table

Pseudocode for Basic Variable Use

Page 17 of 34 

                            First, you’ll notice that the procedure is named “add_item” since it allows for adding an item to the database.  Next, you’ll notice that three of the four values needed in the Item table – item_id, description, and price – all  have corresponding parameters. The executor will decide what these values are whenever the stored  procedure is invoked (you have already witnessed this strategy in the prior step).     Notice the DECLARE @v_item_code VARCHAR(4); code. This line is the variable declaration, where we indicate 

the variable exists (by the existence of the declaration), give the variable its name (v_item_code), and its 

datatype (VARCHAR(4)). We give it that datatype since that is the same datatype as found in the table. This 

variable declaration sets up the variable so it can be assigned values and its values can be retrieved. 

 

Next, you’ll notice the SET @v_item_code = CONCAT(SUBSTRING(@p_description,1,1), FLOOR(RAND()*1000));

line. There are several pieces of code here you may not recognize, but don’t let that keep you from 

understanding the basic fact that this is the line that sets the value for the variable. What we are setting it to 

is the first character of the description, followed by a random 3‐digit number. For example, if the item 

description is “Napkin”, then the item code would start with “N” since that is the first letter, followed by a 

random 3‐digit number. If the database randomly selects 867 for example, then the item code would be 

“N867”.  

 

The SUBSTRING function in SQL Server returns a portion of a character string. The SUBSTRING(@p_description,

1, 1) code indicates to start at the first character (the first 1 argument), and to grab 1 character from there 

(the second 1 argument), thereby retrieving the first character. The RAND() function obtains a random 

number between 0 and 1, so we multiply it times 1000 to get a number between 0 and 999. The FLOOR 

function chops off the decimal point and leaves the nearest whole number. The CONCAT() function 

concatenates two or more values together as a string. When the results of these functions are combined 

through concatenation, it results in a 4‐character item code as described above. 

 

Last, you’ll notice the insert line, which inserts the values into the Item table, with a reference to 

“v_item_code” for the item_code value. Referencing the variable instructs the SQL engine to pull the value 

stored in the variable. 

  Let’s try out compiling and executing the stored procedure to add a “napkin” item.   

CREATE PROCEDURE ADD_ITEM @p_item_id DECIMAL(12), -- The new item ID, must be unused @p_description VARCHAR(64), -- The item's description @p_price DECIMAL(10,2) -- The item's price AS BEGIN DECLARE @v_item_code VARCHAR(4); --Declare a variable to hold an item_code value. --Calculate the item_code value and put it into the variable. SET @v_item_code = CONCAT(SUBSTRING(@p_description,1,1), FLOOR(RAND()*1000)); --Insert a row with the combined values of the parameters and the variable. INSERT INTO ITEM (item_id, description, price, item_code) VALUES(@p_item_id, @p_description, @p_price, @v_item_code); END;

Code: Creating add_item Procedure

Page 18 of 34 

 

Screenshot: Compiling and Executing add_item

SQL Server

  Notice that it was necessary to put the GO keyword after the stored procedure definition, so that we could  combine DDL (data definition language) with DML (data manipulation language). We otherwise execute the  add_item stored procedure just as we executed the add_customer procedure in the prior step. Let’s look at  the item table now to see if our item was added.     

Screenshot: Listing Item Table

SQL Server

 

  Sure enough, the Napkin item was added, and the item_code value was automatically calculated rather than  being passed in as a parameter by the executor. We achieved something new!    There is one more important concept you need to understand about variables to make effective use of them.  Variable scope is the region in which a variable is accessible. In the examples in this step, we declare the 

Page 19 of 34 

variable within the stored procedure, which means that the variable is only accessible within that same stored  procedure. Another stored procedure, or the SQL engine itself, cannot access the variable. When the  procedure is invoked, the variable becomes accessible by code in the procedure. Unless a value is given in its  declaration, the variable is initialized to null, and another line of code can explicitly set its value to something  else.    What about multiple executions of the same procedure? Does the variable’s value remain across executions?  The simple answer is no. Every time the procedure is invoked, the variable’s value is initialized and available  only to that particular execution. Different executions of the stored procedure have access to different values  of the variable. That is, even though it appears multiple executions are accessing the same variable since it  carries the same and declaration, each execution has its own copy of the variable so that each execution can  use the variable independent of another execution.     Hopefully the examples in this step help illustrate one purpose of using variables. A variable provides a place  to store values, which can be calculated by using expressions, and then the variable can later be referenced to  retrieve its value.    You now have enough knowledge to create the “add_post” stored procedure for this step.     

Page 20 of 34 

    What you’re being asked to do is certainly becoming more complex, but don’t worry, you already have most of  the skills you need. You already know how to create parameterized stored procedures, and declare and use  variables. The one skill you have not learned yet is setting the value of a variable based upon the results of a  query. Since your procedure will be given a username and not a person_id, you will need to look this up by  executing a query. There is a way to do this and store it into a variable.    We’ll demonstrate how to do this by creating an “add_line_item” stored procedure that supports adding line  items to the database. Rather than the executor specifying the item_id, the stored procedure will take the  item_code as a parameter, then lookup the item_id. The other parameters will be specified as usual. Such a  procedure can look like this.                                        There are four columns in the line_item table – item_id, order_id, item_quantity, and line_price. Order_id and  item_quantity are specified explicitly as parameters, so the executor decides what values to pass in explicitly.  However, the other two are not specified explicitly, but are looked up by using the item_code. Notice that  there are two variables declared, v_item_id and v_line_price; these will be used to store these values. It’s this  select statement that is interesting for this step.    SELECT @v_item_id=item_id, @v_line_price=price*@p_quantity FROM Item WHERE item_code = @p_item_code;

 

   

STEP 6

Create a reusable stored procedure named “add_like” that uses parameters and  allows you to insert any new “like”. Rather than passing in the person_id value as  parameters to identify which person is liking which post, pass in the username of the  person. The stored procedure should then lookup the person_id and store it in a  variable to be used in the insert statement. Execute the procedure to add a “like” of  your choosing, then list out the Like table to show the addition succeeded. 

CREATE PROCEDURE ADD_LINE_ITEM @p_item_code VARCHAR(4), -- The code of the item. @p_order_id DECIMAL(12), -- The ID of the order for the line item. @p_quantity DECIMAL(10) -- The quantity of the item. AS BEGIN DECLARE @v_item_id DECIMAL(12); --Declare a variable to hold the ID of the item code. DECLARE @v_line_price DECIMAL(12,2); --Declare a variable to calculate line price. --Get the item_id based upon the item_code, as well as the line total. SELECT @v_item_id=item_id, @v_line_price=price*@p_quantity FROM Item WHERE item_code = @p_item_code; --Insert the new line item. INSERT INTO LINE_ITEM(item_id, order_id, item_quantity, line_price) VALUES(@v_item_id, @p_order_id, @p_quantity, @v_line_price); END;

Code: ADD_LINE_ITEM procedure

Page 21 of 34 

The standard SELECT, FROM, WHERE clauses combined are retrieving the item corresponding to the item code  passed in as a parameter, and pulling back the item_id column, and calculating what the line price would be by  multiplying the price times the quantity specified. You might have guessed that the syntax we have not dealt  with before, the @v_item_id= and the @v_line_price=, provide a way to assign the column’s value into a  variable. @v_item_id is set to the item_id column value, and @v_line_price is set to the expression of price *  @p_quantity.     Do you see the power of this syntax? You can use it to lookup values in other tables, store them in variables,  then later use those variables as needed. In our case, we are using the syntax to determine what the item_id  and line_price values should be, then using those variables in our insert statement.    Next, let’s use our stored procedure to add a line item for an order, where three “fork” items are added. As a  reminder, the “fork” item has these values:    item_id  description  price  item_code

4  Fork  5  F004 

  It’s item_code is “F004”, its price is $5, and its ID is 4. Here is a screenshot of the code used to add three fork  items to an order (order with id 8).   

 

Screenshot: Compiling and Executing add_line_item

SQL Server

 

  The ADD_LINE_ITEM procedure was invoked, referencing the “F004” item code, order id 8, and a quantity of  3. Now let’s see if our results made it into the table by selecting all line items for order 8.   

Page 22 of 34 

Screenshot: Listing Line_item After Execution

SQL Server

 

  Notice that the first line is the one we just inserted using the procedure! Order with ID 8 now has an additional  line item for forks (with ID 4), quantity 3, and a price of $15 (since $5 * 3 = $15).     We are able to use this procedure to automatically calculate the line price, and to retrieve the correct item, all  with its item code. Do you see now the power of lookups and storing the values in? It gives a new dimension  to your stored procedures, as your procedures can now take parameters, lookup values in various tables, and  use them as needed. You’re on your way to becoming an expert!    Now you can use a similar strategy to create your “add_like” procedure.     

Page 23 of 34 

    In addition to reusability through the use of parameters, stored procedures can also be made more useful by  executing multiple SQL commands that make up one logical unit of work. For example, what if we want to  delete the Customer John Smith, as well as all of his Order and Line_item information? We would need to  execute several delete statements in the correct order in order to do so. We could do so manually, but better  yet we could create a parameterized stored procedure that would delete any customer’s information of our  choosing. We would just need to specify the customer_id when executing the stored procedure.     We create such a stored procedure, then execute it to delete John Smith and all Order and Line_item  information associated with John Smith. To do so, we need to make use of a simple subquery to delete the line  items. Subqueries are not covered fully in Module 4, so below is a basic form of the subquery. 

DELETE FROM Line_item WHERE order_id IN (SELECT order_id FROM Customer_order WHERE customer_id = 5);

  This command instructs the database to delete any row in the Line_item table that has an order_id placed by a  Customer with customer_id 5. The part in parentheses, (SELECT order_id...), is the subquery which  retrieves all order ids associated to Customer 5. The IN clause matches any of the IDs in that list. You will learn  more details about subqueries in lab 5, but this basic knowledge of subqueries is sufficient for this step. For  illustrative purposes, here we hardcode 5, but of course make the results more dynamic by using parameters  in the stored procedure.    Below is the code for the stored procedure.       

   

STEP 7

Create a reusable stored procedure named “delete_person” that takes only one  parameter, the username of a person, and deletes all record of that person from the  database. This means deleting all of a person’s posts, likes, and the Person record  itself. Execute the procedure to delete a person of your choosing (make sure the  person has at least one post and at least one like. List out all three tables to show  that all record of the person is gone. 

Page 24 of 34 

                                    The procedure takes a single parameter for the customer id. It then performs the following steps.    a. Delete records from LINE_ITEM table.  b. Delete records from CUSTOMER_ORDER table.  c. Delete records from CUSTOMER table.     The order of deletion is important. We cannot delete the customer before deleting his or her orders. We  cannot delete the orders before deleting the orders’ line items. So, we delete the line items first, followed by  the orders, followed by the customer.    John Smith has an ID of 1. Below is a screenshot of deleting him from the database using this stored  procedure.   

CREATE PROCEDURE DELETE_CUSTOMER @p_customer_id DECIMAL(12) AS BEGIN --Delete all line items associated with orders associated --with the customer. DELETE FROM Line_item WHERE order_id IN (SELECT order_id FROM Customer_order WHERE customer_id = @p_customer_id); --Delete all orders associated with the customer. DELETE FROM Customer_order WHERE customer_id = @p_customer_id; --Delete the customer. DELETE FROM Customer WHERE customer_id = @p_customer_id; END;

Code: DELETE_CUSTOMER procedure

Page 25 of 34 

Screenshot: Deleting Customer John Smith

SQL Server

  We just deleted John Smith by using the procedure! To make sure, let’s list out a couple of the tables, looking  for John Smith.   

 

Screenshot: Listing John Smith After Deletion

SQL Server

 

  Voila! John Smith no longer exists. You can use similar logic to create your delete_person procedure.     

Page 26 of 34 

Section Two – Triggers   

    To demonstrate how to do this, we will create a trigger that prevents the customer balance from being  negative. This validation only needs information from the table being modified and so qualifies as an intra‐ table validation. Below is the code for such a trigger.                                Since we have not yet reviewed triggers, let’s examine this line by line.    

Line 1: CREATE TRIGGER no_neg_bal_trg 

The CREATE TRIGGER phrase indicates that a trigger is to be created. The  no_neg_bal_trg word is the name of the trigger, an identifier of our own  choosing. We put “trg” at the end of the identifier as a convention, so it’s  recognizable as the name of a trigger. The rest of the name helps describe what  the trigger does, which is validate that there is no negative balance.  

Line 2: ON Customer AFTER INSERT,UPDATE

  ON Customer ties to trigger to the Customer table, that is, indicates that the  aforementioned actions (update and insert) will fire this trigger only if they  happen on the Customer table. Triggers are inexorably linked to one table by  definition. If the table is dropped, the trigger is also automatically dropped. 

AFTER is a SQL keyword that instructs the trigger is to be executed after the  insert or update occurs in the database (but still within the same transaction).  INSERT,UPDATE  indicates that the trigger is to be fired when either an insert or 

   

STEP 8

One practical use of a trigger is validation within a single table (that is, the validation  can be performed by using columns in the table being modified). Create a trigger  that validates that the summary is being inserted correctly, that is, that the summary  is actually the first 12 characters of the content followed by “…”. The trigger should  reject an insert that does not have a valid summary value. Verify the trigger works by  issuing two insert commands – one with a correct summary, and one with an  incorrect summary. List out the Post table after the inserts to show one insert was  blocked and the other succeeded. 

CREATE TRIGGER no_neg_bal_trg ON Customer AFTER INSERT,UPDATE AS BEGIN DECLARE @CUSTOMER_TOTAL DECIMAL; SET @CUSTOMER_TOTAL=(SELECT INSERTED.customer_total FROM INSERTED); IF @CUSTOMER_TOTAL < 0 BEGIN ROLLBACK; RAISERROR('Customer balance cannot be negative',14,1); END; END;

Code: No Negative Balance Validation Trigger

Page 27 of 34 

an update statement happens on the table. If we had omitted “INSERT” for  example, then the trigger would only fire when an update occurs. We want to  block all negative balances so we have the trigger fire on both updates and  inserts.  

Line 3: AS    This is just part of the syntax of creating the trigger (similar to stored 

procedures).  Line 4: BEGIN    Just as with stored procedures, code for triggers needs to be defined within a 

BEGIN/END block. The BEGIN keyword opens the block for the trigger.  Line 5: DECLARE @CUSTOMER_TOTAL DECIMAL;

  This declares a variable that will be used to store the total just updated or  inserted. 

Line 6: SET @CUSTOMER_TOTAL=(SELECT INSERTED.customer_total FROM INSERTED);    This sets the customer_total variable to the total just inserted or updated. 

INSERTED is a pseudo‐table only available in triggers which has all of the same  columns as the table the trigger is attached to (in this case, Customer), and has  all of the updated rows (since inserts and updates can affect more than one row  in SQL Server). By accessing INSERTED.customer_total, we are accessing the  total column after it was updated or inserted. Notice that we used the SET  keyword to set the value of the customer_total variable, with a nested query.  This is just an alternative method for setting the variable. We could have also  used the “@customer_total=” syntax within the query itself. 

Line 7: IF @CUSTOMER_TOTAL < 0     The IF keyword tells SQL Server that the block following is only to be executed 

of the Boolean expression evaluates to true. If statements allow us to  conditionally execute code. For this trigger, we only want to reject SQL  statements that attempt to create a negative balance, so we use an if statement.  The @CUSTOMER_TOTAL < 0 component is the Boolean expression that the if  statement will evaluate, which evaluates to true only when the new  customer_total value is less than 0 (negative). 

Line 8: BEGIN

  This begins the block for the if statement. Any code within this block is  conditionally executed based upon the Boolean expression. 

Line 9: ROLLBACK;    This is a transaction control statement that rolls back the in‐progress 

transaction. Even though the trigger is firing after the update has been made,  the trigger is still executing within the same transaction as the insert or update  statement that caused the trigger to fire. Since we don’t want the insert or  update to make it into the database, we must rollback the transaction. 

Line 10: END;    This ends the IF block. Code outside the IF block is not conditional, and code 

within the IF block is conditional.  Line 11: END;    This ends the block for the trigger itself. 

  First, we compile the trigger as illustrated in the screenshot below.    

Page 28 of 34 

Screenshot: Compile the No Negative Balance Trigger

SQL Server

  As soon as the trigger is compiled successfully, it is active in the database and will execute when the triggering  event happens from that point forward (until, of course, the trigger is disabled or dropped). If we attempt to  insert a customer with a negative balance, the trigger will fire and reject it, shown below.   

Screenshot: Test the No Negative Balance Trigger

SQL Server

  Notice that the insert was immediately rejected by the trigger, and the message is “Customer balance cannot  be negative.” We cannot execute the trigger directly, but we can see the effects of the trigger when we  execute a triggering statement such as an insert.    You can use similar logic to create a trigger to validate the summary field in the Post table.     

Page 29 of 34 

    To demonstrate cross‐table validation, imagine we want to validate the fact that the line price for a line item  actually equals the quantity times the item price. The quantity is stored in the Line_item table while the item  price is stored in the Item table. We can setup a trigger on the Line_item table to perform this validation using  constructs we’ve already used in prior steps in this lab. Here is the code for such a trigger.                                    You’ve seen all of the constructs here individually, but the integration of them needs more explanation.   Two variables are declared, one to store the actual line price being inserted or updated, and the other to store  the correct line price. Both of these are set by using the SELECT statement to pull from both the Item table as  well as the INSERTED pseudo‐table. The trigger then uses an if statement to determine if the line price of the  new or updated row is correct. If it’s not, it rolls back the transaction and raises an error that indicates the line  price is not correct. You’ve seen all of these constructs before, so you’re not witnessing just another use case.    Let’s try it out. We’ll try to insert a line item with an invalid line price, as follows.   

   

STEP 9

Another practical use of a trigger is cross‐table validation (that is, the validation  needs columns from at least one table external to the table being updated). Create a  trigger that blocks a “like” from being inserted if its “liked_on” date is before the  post’s “created_on” date. Verify the trigger works by inserting two “likes” – one that  passes this validation, and one that does not. List out the Likes table after the inserts  to show one insert was blocked and the other succeeded. 

CREATE TRIGGER line_price_trg ON Line_item AFTER INSERT,UPDATE AS BEGIN DECLARE @v_actual_line_price DECIMAL(12,2); DECLARE @v_correct_line_price DECIMAL(12,2); SELECT @v_actual_line_price=INSERTED.line_price, @v_correct_line_price=INSERTED.item_quantity * Item.price FROM Item JOIN INSERTED ON INSERTED.item_id = Item.item_id; IF @v_actual_line_price <> @v_correct_line_price BEGIN ROLLBACK; RAISERROR('The line price is not correct.',14,1); END; END;

Code: Correct Line Price Validation Trigger

Page 30 of 34 

Screenshot: Test the Price Validation Trigger

SQL Server

  We attempted to insert a line item with a line price of 100, and the trigger rejected it because the line price  should be 25. Why? Item with ID 3 has a price of 5, and there is a quantity of 5, so the line price would be 25  and not 100. We successfully used a trigger to perform a cross‐table validation, simply by combining  constructs we were already familiar with.    With all of this learning, hopefully by now you are beginning to feel very powerful (just make sure to use your  powers for good and not evil). You can use similar logic to create a cross‐table validation on the Likes table.     

Page 31 of 34 

    To demonstrate capturing history, imagine that we would like to store a history of price changes for each item,  so that we know the price of an item at any point in time despite any price updates. Abstractly, these fields  should be included in a history table – a reference (foreign key) to the table being changed, the old value of  the column, the new value of the column, and the date of change. You can think of this set of fields as a design  pattern for history tables. For our example, we would create an Item_price_history table that stores a  reference to the item, its old price, its new price, and the date of the change.     Before showing you code, let’s make sure to differentiate history and auditing, which have two different  purposes. A history table records changes over time but remains active in the schema, with proper foreign  keys for references. The fields are setup so that SQL queries and transactions can use the table along with the  other tables in the schema, that is, so that the table can be used regularly like any other table in the schema.  The purpose of a history table is to make prior values available to the people and applications that use the  database in a way that coexists with the current values.    An audit table also records changes over time, but it does not remain active in the schema. The purpose of an  audit table is to simply record that a change happened in a way that people can manually review the changes  later in case of any concern or dispute. An audit table has no foreign keys, and acts more like a log which  contains the full value of each field. Since an audit table does not make use of foreign keys, and flattens out  the needed fields, it survives schema changes over time well. For example, as already mentioned a history  table for price changes would have a foreign key to the item, but an audit table would instead have the  description of the item along with any other information needed to identify the item.  Someone could  manually review the audit table to see which prices changes over time for which items.    It’s a best practice to be aware of which kind of table you’re creating – history or audit – and follow the design  patterns for that table. Some organizations inadvertently create hybrid tables that perhaps start out strictly for  auditing, but then later add in foreign keys, and this can cause problems as changes are made to the database.  In this step, we are creating a history table that remains active in the schema.    First, let’s look at the code for creating the Item_price_history table, below.     

   

STEP 10

Another practical use of trigger is to maintain a history of values as they change.  Create a table named post_content_history that is used to record updates to the  content of a post, then create a trigger that keeps this table up‐to‐date when  updates happen to post contents. Verify the trigger works by updating a post’s  content, then listing out the post_content_history table (which should have a record  of the update). 

Page 32 of 34 

                  You’re very familiar with table creation syntax at this point, so there’s no need to belabor the basics of this  SQL. Instead, we’ll focus on what’s important here. We opted to avoid giving this table a primary key; a  primary key is not necessary to record the history. In some real‐world schemas, the standards employed by  the organization may mandate a primary key, especially if the schema will be mapped by an object‐relational  mapping tool within an application. There is a foreign key to the Item which has been changed. The old_price  and new_price columns have the same datatype as the original Item.price column, since it will be a record of  the change.     Once the table is created, we then define a trigger on the Item table that inserts a row into the  Item_price_history table whenever an item price is updated. The code for the trigger is below.                                This trigger only fires on updates, because we only want to record changes in price. We’ve opted not record  the initial price so we don’t have the trigger fire on insert; however, one variation of the history table would  record every price including the initial one. In that case, the trigger would be modified to also trigger on insert,  and the old_price column would be nullable so that when the first price is created, the old_price is null (since  there is no price).     DELETED is a pseudo‐table that records the row before it was updated, in case of an update. If we had  assigned the trigger fire on DELETE as well, then it would record the row before it was deleted. We use the  DELETED pseudo‐table to extract the old price, and the INSERTED pseudo‐table to extract the new price and  the item id. If the old price is different than the new price, it is recorded as an insert into the history table. The  function “GETDATE()” is used to retrieve the current date.     Let’s test that our trigger works by modifying the price of an item in the Item table. The compilation and  update are illustrated first, below.      

CREATE TABLE Item_price_history ( item_id DECIMAL(12) NOT NULL, old_price DECIMAL(10,2) NOT NULL, new_price DECIMAL(10,2) NOT NULL, change_date DATE NOT NULL, FOREIGN KEY (item_id) REFERENCES Item(item_id));

Code: Create the Item_price_history Table

CREATE TRIGGER item_history_trg ON Item AFTER UPDATE AS BEGIN DECLARE @v_old_price DECIMAL(10,2) = (SELECT price FROM DELETED); DECLARE @v_new_price DECIMAL(10,2) = (SELECT price FROM INSERTED); DECLARE @v_item_id DECIMAL(12) = (SELECT item_id FROM INSERTED); IF @v_old_price <> @v_new_price BEGIN INSERT INTO Item_price_history(item_id, old_price, new_price, change_date) VALUES(@v_item_id, @v_old_price, @v_new_price, GETDATE()); END; END;

Code: The Item_price_history Trigger

Page 33 of 34 

Screenshot: Compilation and Update for Item_price_history

SQL Server

  Next, the listing of the table itself is included, to show that the trigger recorded the update.   

Screenshot: Listing Item_price_history

SQL Server

 

  We now see a row in the history table indicating the item with ID 1 (Plate) had an old price of 10, a new price  of 35, and the change happened on the specific date. With this structure, all such price changes will be  recorded over time. And following this pattern, we could record a history for whatever column we like for  whatever table we need. Amazing!     You can follow this pattern to create and populate the post_content_history pattern for your lab.       

Page 34 of 34 

Congratulations! You are well on your way to becoming a database design and development guru.