Implementing Batch_stored procedure_UDFfunctions_Cursor
Implementing Batch_stored procedure_UDFfunctions_Cursor
Objectives
In this Unit, you will learn to:
Implement batches
Implement functions
Implement Cursor
Implementing Batches
As a database developer, you might need to execute more than one SQL statement to
perform a task. For example, when a new employee joins AdventureWorks, Inc., you
need to insert the employee details in the database. The details of the employees are
stored in more than one table. Therefore, you need to execute multiple insert
statements to store the details in each table. In such a case, you can send all the SQL
statements together to SQL Server to be executed as a unit. This helps in reducing
the network traffic.
At times, you might also need to check conditions before executing the SQL
statements. For example, in a manufacturing unit, the InventoryIssue table stores the
details of an item issued for the manufacturing process. When you insert a record in
this table, you need to check that the quantity on hand is more than or equal to the
quantity issued. In such a case, you can create conditional constructs that check for a
condition before executing a statement.
Creating Batches
A batch is a group of SQL statements submitted together to SQL Server for
execution. While executing batches, SQL Server compiles the statements of a batch
into a single executable unit called an execution plan. This helps in saving execution
time.
For example, you have to execute 10 statements and you are executing them one by one
by sending 10 requests. This process takes time if your queries are in a queue. All the
statements might not get executed together. Instead, if you execute all the 10
statements together in a batch, then the execution process becomes faster as all the
statements are sent to the server together. To create a batch, you can write multiple
SQL statements followed by the keyword GO at the end. The syntax of creating a
batch is:
<T-SQL Statement1>
<T-SQL Statement2>
<T-SQL Statement3>
…
GO
GO is a command that specifies the end of the batch and sends the SQL statements
for execution. For example, if you want to store the details of new employees in the
AdventureWorks database, you can write multiple INSERT statements in a batch, as
shown in the following statements:
GO
When a batch is submitted to SQL Server, it is compiled to create an execution plan.
If any compilation error occurs, such as a syntax error, the execution plan is not
created. Therefore, none of the statements in the batch is executed. However, if a
run-time error occurs after the execution plan is created, the execution of the batch
stops. In such a case, the statements executed before the statement that
encountered the run-time error are not affected.
Using Variables
While creating batches, you might need to store some values temporarily during the
execution time. For example, you might need to store some intermediate values while
performing calculations. To store the intermediate values, you can declare variables
and assign values to them. You can declare a variable by using the DECLARE statement.
A variable name is always preceded by the "@" symbol.
Variables that are declared in a batch and can be used in any statement inside the
batch are called local variables. The following statements declare a variable, @Rate,
and assigns the maximum value of the Rate column from the EmployeePayHistory table
to the variable:
DECLARE @Rate int
SELECT @Rate = max(Rate) FROM HumanResources.EmployeePayHistory
GO
In the preceding statements, the max aggregate function is used to retrieve the
maximum pay rate from the EmployeePayHistory table.
The following statements display the value of the @Rate variable by using the PRINT
statement:
DECLARE @Rate int
SELECT @Rate = max(Rate) FROM HumanResources.EmployeePayHistory
PRINT @Rate
GO
You can also use comment entries in batches to write a description of the code. This
will help understand the purpose of the code. A comment entry can be written in the
following ways:
You cannot bind rules and defaults to columns and use them in the same batch.
You cannot define and use the CHECK constraint in the same batch.
You cannot drop objects and recreate them in the same batch.
You cannot alter a table by adding a column and then referring to the new column in
the batch created earlier.
Using Constructs
SQL Server allows you to use programming constructs in batches for conditional
execution of statements. For example, you need to retrieve data based on a condition.
If the condition is not satisfied, a message should be displayed. SQL Server allows you
to use the following constructs to control the flow of statements:
IF…ELSE statement
CASE statement
WHILE statement
IF boolean_expression
{sql_statement | statement_block}
ELSE
{sql_statement | statement_block}]
statement_block statements. You need to use the BEGIN and END keywords to define
multiple T-SQL statements.
For example, you can retrieve the pay rate of an employee from the
EmployeePayHistory table to a variable, @Rate. The value of the @Rate variable is
compared with the value 15 by using the < (less than) comparison operator. Based on
the condition, different messages are displayed, as shown in the following statements:
DECLARE @Rate money
SELECT @Rate = Rate FROM HumanResources.EmployeePayHistory WHERE
EmployeeID = 23
IF @Rate < 15
PRINT 'Review of the rate is required'
ELSE
BEGIN
PRINT 'Review of the rate is not required'
PRINT 'Rate =' PRINT @Rate
END
GO
In the preceding statements, the IF statement checks whether the @Rate variable is
storing a value less than 15. If the result is true, the PRINT statement displays
"Review of the rate is required", else it displays "Review of the rate is not required".
Further, the next PRINT statement displays the value of the rate.
Consider another example, where a check is performed to see the existence of the
Sales department, as shown in the following statement:
IF EXISTS (SELECT * FROM HumanResources.Department WHERE Name = 'Sales')
BEGIN
SELECT * FROM HumanResources.Department WHERE Name = 'Sales'
END
ELSE
PRINT 'Department details not available'
GO
In the preceding statement, if the Sales department exists, all the details are
displayed; otherwise, a user-defined message is displayed.
where,
Consider the following statements, where a CASE construct is included in the SELECT
statement to display the marital status as "Married" or "Single":
SELECT EmployeeID, 'Marital Status' =
CASE MaritalStatus
WHEN 'M'
THEN 'Married'
WHEN 'S'
THEN 'Single'
ELSE
'Not specified'
END
FROM HumanResources.Employee
GO
BREAK causes the control to exit from the WHILE loop. CONTINUE causes the
WHILE loop to restart, skipping all the statements after the CONTINUE keyword.
SQL Server provides the BREAK and CONTINUE statements to control the
statements within the WHILE loop. The BREAK statement causes an exit from the
WHILE loop. Any statements that appear after the END keyword, which marks the end
of the loop, are executed after the BREAK statement is executed. The CONTINUE
statement causes the WHILE loop to restart, skipping any statements after this
statement inside the loop.
For example, the HR department of AdventureWorks, Inc. has decided to review the
salary of all the employees. As per the current HR policy, the average hourly salary
rate of all the employees should be approximately $20. You need to increase the hourly
salary of all the employees until the average hourly salary reaches near $20. In
addition, you need to ensure that the maximum hourly salary should not exceed $127.
To accomplish this task, you can use the following statement:
For example, there is a primary key constraint applied on the EmployeeID attribute of
the Employee table. When you try to insert an employee ID that already exists in the
table, an error occurs while executing the INSERT statement.
By using the RAISERROR statement and handling the error in the application
Using TRY…CATCH
A TRY…CATCH construct includes a TRY block followed by a CATCH block. A TRY block
is a group of SQL statements enclosed in a batch, stored procedure, trigger, or
function. If an error occurs in any statement of the TRY block, the control is passed to
another group of statements that are enclosed in a CATCH block. A CATCH block
contains SQL statements that perform some operations when an error occurs.
Therefore, an associated CATCH block must immediately follow a TRY block, as shown
in the following syntax:
TRY <SQL statements>
…
CATCH <SQL statements>
…
END CATCH
If there are no errors in the code that is enclosed in a TRY block, the control is passed
to the statement immediately after the associated END CATCH statement. In this
case, statements enclosed in the CATCH block are not executed. The TRY…CATCH
construct can be nested. Either a TRY block or a CATCH block can contain nested
TRY… CATCH constructs. A CATCH block can contain an embedded TRY…CATCH
construct to handle errors encountered by the CATCH block.
In the CATCH block, you can use the following system functions to determine the
information about errors:
For example, the EmployeeID attribute of the Employee table in the AdventureWorks
database is an IDENTITY column and its value cannot be specified while inserting a
new record. However, if you specify the value for EmployeeID in the INSERT
statement, an error will occur. To handle such run-time errors, you can include the
insert statement in a TRY block and send the control to the CATCH block where the
error information is displayed, as shown in the following statements:
BEGIN TRY
INSERT INTO [AdventureWorks].[Person]. [Contact] VALUES (0, null, 'Robert',
'J', 'Langdon', NULL ,'[email protected]', 0, '1 (11) 500 555-0172'
,'9E685955-ACD0-4218-AD7F-60DDF224C452', '2a31OEw=', NULL, newid(),
getdate()) INSERT INTO [AdventureWorks]. [HumanResources].[Employee] VALUES
('AS01AS25R2E365W', 19979, 'robertl', 16, 'Tool Designer', '1972-05-15',
'S', 'M', '1996-07-31', 0, 16, 20, 1, newid(), getdate())
END TRY
BEGIN CATCH
SELECT 'There was an error! ' + ERROR_MESSAGE() AS ErrorMessage,
ERROR_LINE() AS ErrorLine,
ERROR_NUMBER() AS ErrorNumber,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState
END CATCH
GO
Stored procedures can invoke the Data Definition Language (DDL) and Data
Manipulation Language (DML) statements and can return values. If you need to assign
values to the variables declared in the procedures at run time, you can pass parameters
while executing them. You can also execute a procedure from another procedure. This
helps in using the functionality of the called procedure within the calling procedure.
An application is designed in three layers. These layers are the presentation, business,
and data access layers.
An application is well maintained if the stored procedures are used. These procedures
are indeed a part of the data access layer. They are mapped to certain methods of the
business layer. The application can access the required data from the database by
invoking the specific methods of the business layer. These methods, in turn, invoke the
statements or the procedures in the data access layer. Therefore, the entire process
is abstracted. As a database developer, it is important for you to learn how to
implement procedures.
AS
BEGIN
sql_statement1 sql_statement2
END
where,
ENCRYPTION is used to encrypt the original text of the CREATE PROCEDURE statement.
RECOMPILE specifies that the stored procedure is recompiled every time it executes.
EXECUTE AS specifies the security context under which the stored procedure is executed.
The following statement creates a stored procedure to view the department names from the
Department table:
CREATE PROCEDURE prcDept
AS
BEGIN
END
When the CREATE PROCEDURE statement is executed, the server compiles the
procedure and saves it as a database object. The procedure is then available for
various applications to execute.
1. The procedure is compiled and its components are broken into various pieces.
This process is known as parsing.
2. The existence of the referred objects, such as tables and views, are checked.
This process is known as resolving.
3. The name of the procedure is stored in the sysobjects table and the code that
creates the stored procedure is stored in the syscomments table.
4. The procedure is compiled and a blueprint for how the query will run is created.
This blueprint is specified as an execution plan. The execution plan is saved in
the procedure cache.
5. When the procedure is executed for the first time, the execution plan will be
read, fully optimized, and then run. When the procedure is executed again in the
same session, it will be read directly from the cache. This increases
performance, as there is no repeated compilation.
NOTE :- After creating the stored procedure, you can view the code of the procedure by using the
sp_helptext statement.
You cannot combine the CREATE PROCEDURE statement with other SQL
statements in a single batch.
You must have the CREATE PROCEDURE permission to create a procedure in the
database and the ALTER permission on the schema, where the procedure is
being created.
You can create a stored procedure only in the current database.
After creating a stored procedure, you can execute the procedure. You can also alter
the procedure definition or drop it, if the existing procedure is not required.
where, proc_name is the name of the procedure that you need to execute. You can
execute the stored procedure, prcDept, as shown in the following statement:
EXEC prcDept
You can alter the stored procedure by using the following statement:
AS
BEGIN
END
In the preceding statement, the prcDept stored procedure will be modified to display
department Ids along with the department name. When you execute a stored
procedure, the number of rows affected by it is also returned. If the stored
procedure is getting executed by a Web application, then the database engine returns
the result set along with the number of rows affected. This creates an overhead on
the network and reduces the performance of the application. To avoid this, you can
turn off the message that contains the number of rows affected by the SQL
statement.
You can perform this task by using the SET NOCOUNT statement.
where,
ON prevents the message containing the count of the number of rows affected by the
SQL statement or stored procedure from being returned.
OFF allows the message containing the count of the number of rows affected by the
SQL statement or stored procedure to be returned.
For example,
AS
BEGIN
HumanResources.Department
END
When you execute the prcDept stored procedure, the result set does not display the
number of rows affected by the procedure.
AS
BEGIN
END
You can execute the stored procedure, prcListEmployee, by using the following
statement:
While executing stored procedures, you can also provide the values for the parameters
by explicitly specifying the name and value of the parameter. In the previous example,
you can also pass the parameter value by using the name of variable, as shown in the
following statement:
The OUTPUT keyword has to be specified in both the CREATE PROCEDURE and the
EXECUTE statements. If the OUTPUT keyword is omitted, the procedure will be
executed but will not return any value. The syntax of declaring an output parameter
using the OUTPUT keyword is:
AS
sql_statement [...n]
@parameter data_type [OUTPUT] allows the stored procedure to pass a data value to
the calling procedure. If the OUTPUT keyword is not used, then the parameter is
treated as an input parameter.
You can also return values from the stored procedure by using the RETURN statement.
The RETURN statement allows the stored procedure to return only an integer value to
the calling application. The syntax of the RETURN statement is:
RETURN value
If a value is not specified, then the stored procedure returns a default value of 0 to
specify success and a nonzero value to specify failure.
For example,
you need to display the details of an employee whose employee ID has been provided as
an input. For this, you need to create a procedure prcGetEmployeeDetail that will
accept employee ID as an input and return the department name and ID of the shift in
which the employee works. You can create the procedure, as shown in the following
statement:
CREATE PROCEDURE prcGetEmployeeDetail @EmpId int, @DepName char(50)
OUTPUT, @ShiftId int OUTPUT
AS
BEGIN
BEGIN
RETURN 0
END
ELSE
RETURN 1
END
You can also execute a procedure from another procedure if you need to use the
functionality provided by one into another.
Consider the previous example where the prcGetEmployeeDetail procedure returns the
employee details for a given employee ID. You can create the
prcDisplayEmployeeStatus procedure, which accepts the employee ID of an employee
as an input and displays the department name and shift ID where the employee is
working along with the manager ID and the title of the employee. To perform this task,
you need to call the prcGetEmployeeDetail procedure from the
prcDisplayEmployeeStatus procedure, as shown in the following statement:
AS
BEGIN
IF (@ReturnValue = 0)
BEGIN
PRINT 'Department Name: ' + @DepNamePRINT 'Shift ID: ' + convert( char
(1), @ShiftId)
END
ELSE
END
EXEC prcDisplayEmployeeStatus 2
Exercises
Exercise 1
Create a batch that finds the average pay rate of the employees and then lists the
details of the employees who have a pay rate less than the average pay rate.
Implementing Functions
Similar to stored procedures, you can also create functions to store a set of T-SQL
statements permanently. These functions are also referred to as User-Defined
Functions (UDFs). A UDF is a database object that contains a set of T-SQL
statements, accepts parameters, performs an action, and returns the result of that
action as a value. The return value can be either a single scalar value or a result set.
UDFs have a limited scope as compared to stored procedures. You can create functions
in situations when you need to implement a programming logic that does not involve any
permanent changes to the database objects outside the function. For example, you
cannot modify a database table from a function.
Creating UDFs
A UDF contains the following components:
BEGIN
END
You can execute the preceding function by using the following statements:
PRINT @PayRate
In the preceding statements, @PayRate is a variable that will store a value returned by
the MonthlySal function.
pay rate and returns a single value after multiplying the rate with the number of hours
and number of days. You can create this function by using the following statement:
CREATE FUNCTION HumanResources.MonthlySal (@PayRate float) RETURNS float
AS
END
You can execute the preceding function by using the following statements:
PRINT @PayRate
In the preceding statements, @PayRate is a variable that will store a value returned by
the MonthlySal function.
An inline table-valued function returns a variable of a table data type from the result
set of a single SELECT statement. An inline function does not contain a function body
within the BEGIN and END statements.
For example,
AS
You can use the following query to execute the fx_Department_GName function with a
specified argument:
Problem Statement
Employee ID
Name of the employee
Title of the employee
Number of other employees working under the employee
Implementing Cursors
You have been following a set-based approach to work with data in SQL
Server. This approach allows working on multiple rows together as a single
unit. Being a relational database system, SQL Server is designed to
process the sets, which are an unordered collection of rows, also known as
a result set.
A cursor in SQL Server allows you to work with individual rows of a result
set. Associating a cursor with the result set enables you to process the
result set one row at a time. A cursor allows you to perform the following
operations:
Declare a cursor.
Open the cursor.
Fetch rows from the cursor.
Close the cursor.
Deallocate the cursor.
Declaring a Cursor
where,
LOCAL specifies that the scope of the cursor is local. It means that the
cursor can be referenced only the batch, stored procedure, or trigger in
which it has been created.
GLOBAL specifies that the scope of the cursor is global. It means that
the cursor can be referenced in any batch, stored procedure, or trigger
executed by the current connection.
SCROLL specifies that all the fetch options are available and the cursor
can fetch data in all directions.
STATIC specifies that the cursor makes a temporary copy of the data and
all the requests to the cursor are answered from this temporary table. A
static cursor can move in both the directions forward and backward.
However, data cannot be updated or deleted by using static cursors.
DYNAMIC specifies that the cursor reflects all data changes made to the
rows in its result set as you scroll around the cursor.
READ_ONLY specifies that the cursor is read only and cannot be updated.
SCROLL_LOCKS specifies that the rows will be locked as they are read
into the cursor. This is done to ensure that the updates or deletes made
through the cursor succeeds.
OPTIMISTIC specifies that the cursor does not lock rows as they are
read into the cursor. Therefore, if any user changes the data in the rows
being read by the cursor, the updates or deletes made through the cursor
will not succeed.
where,
where,
NEXT is used to return the row immediately following the current row.
If FETCH NEXT is the first fetch against a cursor, it returns the first row in the
result set.
PRIOR is used to return the result row immediately preceding the current row.
If FETCH PRIOR is the first fetch against a cursor, no row is returned and the cursor
is positioned left before the first row. FIRST is used to return the first row in the
cursor. LAST is used to return the last row in the cursor.
ABSOLUTE { n| @nvar}is used to return the nth row in the cursor. If n or @nvar is
positive, the rows are counted from top of the result set. If n or @nvar is negative,
the rows are counted from bottom of the result set. If n or @nvar is 0, no rows are
returned. n must be an integer constant and @nvar must be smallint, tinyint, or int.
RELATIVE { n| @nvar}is used to return the nth row related to the current row. If n or
@nvar is positive, the nth row after the current row is returned. If n or @nvar is
negative, nth row prior to the current row is returned. If n or @nvar is 0, the current
row is returned. GLOBAL is used to specify the name of the cursor that refers to a
global cursor.
cursor_name specifies the name of the open cursor from which the fetch should be
made. @cursor_variable_name is used as the name of a cursor variable referencing the
open cursor from which the fetch should be made.
INTO @variable_name[ ,...n]specifies the local variable names used to hold the data
returned from the cursor. The number of these variables must be equal to the number
of columns specified in the cursor select list. Each variable in the list, from left to
right, is associated with the corresponding column in the cursor result set.
CLOSE cursor_name
Consider an example.
You want to retrieve the title of an employee on the basis of EmployeeID. You need to
write the following statements for the same:
CREATE PROCEDURE cursor_demo @id int
AS
BEGIN
DECLARE @Ctitle varchar(50);
DECLARE cur1 CURSOR
FOR
SELECT HumanResources.Employee.Title FROM HumanResources.Employee
WHERE HumanResources.Employee.EmployeeID = @id;
OPEN cur1;
FETCH cur1 INTO @Ctitle;
SELECT @Ctitle;
CLOSE cur1;
END
EXEC cursor_demo 10
The preceding statements displays the title of the employee whose ID is 10, as shown
in the following figure.
Consider another example, where you need to display the first name, middle name, and
the last name of a person on the basis of the contact ID. For this, you have written the
following statements:
FOR
OPEN EmpDetail
FETCH NEXT FROM EmpDetail INTO @firstname, @middlename , @lastname
WHILE @@FETCH_STATUS = 0
BEGIN
END
CLOSE EmpDetail