You are on page 1of 323

What is SQL?

SQL is a database language designed for the retrieval and management of


data in a relational database.

SQL is the standard language for database management. All the RDBMS
systems like MySQL, MS Access, Oracle, Sybase, Postgres, and SQL Server
use SQL as their standard database language. SQL programming language
uses various commands for different operations. We will learn about the like
DCL, TCL, DQL, DDL and DML commands in SQL with examples.
In this SQL commands in DBMS tutorial, you will learn:

 What is SQL?
 Why Use SQL?
 Brief History of SQL
 Types of SQL
 What is DDL?
 What is Data Manipulation Language?
 What is DCL?
 What is TCL?
 What is DQL?

Why Use SQL?


Here, are important reasons for using SQL

 It helps users to access data in the RDBMS system.


 It helps you to describe the data.
 It allows you to define the data in a database and manipulate that
specific data.
 With the help of SQL commands in DBMS, you can create and drop
databases and tables.
 SQL offers you to use the function in a database, create a view, and
stored procedure.
 You can set permissions on tables, procedures, and views.

Brief History of SQL


Here, are important landmarks from the history of SQL:
 1970 – Dr. Edgar F. “Ted” Codd described a relational model for
databases.
 1974 – Structured Query Language appeared.
 1978 – IBM released a product called System/R.
 1986 – IBM developed the prototype of a relational database, which is
standardized by ANSI.
 1989- First ever version launched of SQL
 1999 – SQL 3 launched with features like triggers, object-orientation,
etc.
 SQL2003- window functions, XML-related features, etc.
 SQL2006- Support for XML Query Language
 SQL2011-improved support for temporal databases

Types of SQL
Here are five types of widely used SQL queries.

 Data Definition Language (DDL)


 Data Manipulation Language (DML)
 Data Control Language(DCL)
 Transaction Control Language(TCL)
 Data Query Language (DQL)
Types of SQL
Let see each of them in detail:

What is DDL?
Data Definition Language helps you to define the database structure or
schema. Let’s learn about DDL commands with syntax.

Five types of DDL commands in SQL are:

CREATE
CREATE statements is used to define the database structure schema:

Syntax:
CREATE TABLE TABLE_NAME (COLUMN_NAME DATATYPES[,....]);
For example:
Create database university;
Create table students;
Create view for_students;
DROP
Drops commands remove tables and databases from RDBMS.

Syntax
DROP TABLE ;
For example:
Drop object_type object_name;
Drop database university;
Drop table student;
ALTER
Alters command allows you to alter the structure of the database.

Syntax:

To add a new column in the table


ALTER TABLE table_name ADD column_name COLUMN-definition;
To modify an existing column in the table:
ALTER TABLE MODIFY(COLUMN DEFINITION....);
For example:
Alter table guru99 add subject varchar;
TRUNCATE:
This command used to delete all the rows from the table and free the space
containing the table.

Syntax:
TRUNCATE TABLE table_name;
Example:
TRUNCATE table students;

What is Data Manipulation Language?


Data Manipulation Language (DML) allows you to modify the database
instance by inserting, modifying, and deleting its data. It is responsible for
performing all types of data modification in a database.
There are three basic constructs which allow database program and user to
enter data and information are:

Here are some important DML commands in SQL:

 INSERT
 UPDATE
 DELETE

INSERT:
This is a statement is a SQL query. This command is used to insert data into
the row of a table.

Syntax:
INSERT INTO TABLE_NAME (col1, col2, col3,.... col N)
VALUES (value1, value2, value3, .... valueN);
Or
INSERT INTO TABLE_NAME
VALUES (value1, value2, value3, .... valueN);
For example:
INSERT INTO students (RollNo, FIrstName, LastName) VALUES ('60', 'Tom', Erichsen');
UPDATE:
This command is used to update or modify the value of a column in the table.

Syntax:
UPDATE table_name SET [column_name1= value1,...column_nameN = valueN] [WHERE CONDITION]
For example:
UPDATE students
SET FirstName = 'Jhon', LastName= 'Wick'
WHERE StudID = 3;
DELETE:
This command is used to remove one or more rows from a table.

Syntax:
DELETE FROM table_name [WHERE condition];
For example:
DELETE FROM students
WHERE FirstName = 'Jhon';
What is DCL?
DCL (Data Control Language) includes commands like GRANT and REVOKE,
which are useful to give “rights & permissions.” Other permission controls
parameters of the database system.
Examples of DCL commands:
Commands that come under DCL:

 Grant
 Revoke

Grant:
This command is use to give user access privileges to a database.

Syntax:
GRANT SELECT, UPDATE ON MY_TABLE TO SOME_USER, ANOTHER_USER;
For example:
GRANT SELECT ON Users TO'Tom'@'localhost;
Revoke:
It is useful to back permissions from the user.

Syntax:
REVOKE privilege_nameON object_nameFROM {user_name |PUBLIC |role_name}
For example:
REVOKE SELECT, UPDATE ON student FROM BCA, MCA;

What is TCL?
Transaction control language or TCL commands deal with the transaction
within the database.
Commit
This command is used to save all the transactions to the database.

Syntax:
Commit;
For example:
DELETE FROM Students
WHERE RollNo =25;
COMMIT;
Rollback
Rollback command allows you to undo transactions that have not already
been saved to the database.

Syntax:
ROLLBACK;
Example:
DELETE FROM Students
WHERE RollNo =25;
SAVEPOINT
This command helps you to sets a savepoint within a transaction.

Syntax:
SAVEPOINT SAVEPOINT_NAME;
Example:
SAVEPOINT RollNo;

What is DQL?
Data Query Language (DQL) is used to fetch the data from the database. It
uses only one command:
SELECT:
This command helps you to select the attribute based on the condition
described by the WHERE clause.

Syntax:
SELECT expressions
FROM TABLES
WHERE conditions;
For example:
SELECT FirstName
FROM Student
WHERE RollNo > 15;
Summary:
 SQL is a database language designed for the retrieval and management
of data in a relational database.
 It helps users to access data in the RDBMS system
 In the year 1974, the term Structured Query Language appeared
 Five types of SQL queries are 1) Data Definition Language (DDL) 2)
Data Manipulation Language (DML) 3) Data Control Language(DCL) 4)
Transaction Control Language(TCL) and, 5) Data Query Language
(DQL)
 Data Definition Language(DDL) helps you to define the database
structure or schema.
 Data Manipulation Language (DML) allows you to modify the database
instance by inserting, modifying, and deleting its data.
 DCL (Data Control Language) includes commands like GRANT and
REVOKE, which are useful to give “rights & permissions.”
 Transaction control language or TCL commands deal with the
transaction within the database.
 Data Query Language (DQL) is used to fetch the data from
the database.
What is Join in DBMS?
Join in DBMS is a binary operation which allows you to combine join product
and selection in one single statement. The goal of creating a join condition is
that it helps you to combine the data from two or more DBMS tables. The
tables in DBMS are associated using the primary key and foreign keys.
In this DBMS tutorial, you will learn:

 Types of Join
 Inner Join
 Theta Join
 EQUI join:
 Natural Join (⋈)
 Outer Join
 Left Outer Join (A   B)
 Right Outer Join (A   B)
 Full Outer Join (A   B)

Types of Join
There are mainly two types of joins in DBMS:

1. Inner Joins: Theta, Natural, EQUI


2. Outer Join: Left, Right, Full

Let’s see them in detail:

Inner Join
Inner Join is used to return rows from both tables which satisfy the given
condition. It is the most widely used join operation and can be considered as a
default join-type

An Inner join or equijoin is a comparator-based join which uses equality


comparisons in the join-predicate. However, if you use other comparison
operators like “>” it can’t be called equijoin.
Inner Join further divided into three subtypes:

 Theta join
 Natural join
 EQUI join

Theta Join
Theta Join allows you to merge two tables based on the condition
represented by theta. Theta joins work for all comparison operators. It is
denoted by symbol θ. The general case of JOIN operation is called a Theta
join.
Syntax:
A ⋈θ B
Theta join can use any conditions in the selection criteria.

Consider the following tables.

Table A Table B
column 1 column 2 column 1 column 2
1 1 1 1
1 2 1 3
For example:
A ⋈ A.column 2 > B.column 2 (B)
A ⋈ A.column 2 > B.column 2 (B)
column 1 column 2
1 2

EQUI Join
EQUI Join is done when a Theta join uses only the equivalence condition.
EQUI join is the most difficult operation to implement efficiently in an RDBMS,
and one reason why RDBMS have essential performance problems.
For example:
A ⋈ A.column 2 = B.column 2 (B)
A ⋈ A.column 2 = B.column 2 (B)
column 1 column 2
1 1

Natural Join (⋈)


Natural Join does not utilize any of the comparison operators. In this type of
join, the attributes should have the same name and domain. In Natural Join,
there should be at least one common attribute between two relations.
It performs selection forming equality on those attributes which appear in both
relations and eliminates the duplicate attributes.

Example:
Consider the following two tables

C
Num Square
2 4
3 9
D
Num Cube
2 8
3 18
C⋈D
C⋈D
Num Square Cube
2 4 8
3 9 18

Outer Join
An Outer Join doesn’t require each record in the two join tables to have a
matching record. In this type of join, the table retains each record even if no
other matching record exists.

Three types of Outer Joins are:

 Left Outer Join


 Right Outer Join
 Full Outer Join

Left Outer Join (A   B)


Left Outer Join returns all the rows from the table on the left even if no
matching rows have been found in the table on the right. When no matching
record is found in the table on the right, NULL is returned.
Consider the following 2 Tables

A
Num Square
2 4
3 9
4 16
B
Num Cube
2 8
3 18
5 75
A B
A⋈B
Num Square Cube
2 4 8
3 9 18
4 16 –

Right Outer Join ( A   B )


Right Outer Join returns all the columns from the table on the right even if no
matching rows have been found in the table on the left. Where no matches
have been found in the table on the left, NULL is returned. RIGHT outer JOIN
is the opposite of LEFT JOIN
In our example, let’s assume that you need to get the names of members and
movies rented by them. Now we have a new member who has not rented any
movie yet.
A B
A⋈B
Num Cube Square
2 8 4
3 18 9
5 75 –

Full Outer Join ( A   B)


In a Full Outer Join , all tuples from both relations are included in the result,
irrespective of the matching condition.
Example:
A B
A⋈B
Num Square Cube
2 4 8
3 9 18
4 16 –
5 – 75

Summary:
 There are mainly two types of joins in DBMS 1) Inner Join 2) Outer Join
 An inner join is the widely used join operation and can be considered as
a default join-type.
 Inner Join is further divided into three subtypes: 1) Theta join 2) Natural
join 3) EQUI join
 Theta Join allows you to merge two tables based on the condition
represented by theta
 When a theta join uses only equivalence condition, it becomes an equi
join.
 Natural join does not utilize any of the comparison operators.
 An outer join doesn’t require each record in the two join tables to have a
matching record.
 Outer Join is further divided into three subtypes are: 1)Left Outer Join 2)
Right Outer Join 3) Full Outer Join
 The LEFT Outer Join returns all the rows from the table on the left, even
if no matching rows have been found in the table on the right.
 The RIGHT Outer Join returns all the columns from the table on the
right, even if no matching rows have been found in the table on the left.
 In a full outer join, all tuples from both relations are included in the
result, irrespective of the matching condition.
A subquery is a query that is nested inside a SELECT, INSERT, UPDATE, or DELETE statement,
or inside another subquery.

 Note

A subquery can be used anywhere an expression is allowed. In this example a subquery


is used as a column expression named MaxUnitPrice in a SELECT statement.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT Ord.SalesOrderID, Ord.OrderDate,
(SELECT MAX(OrdDet.UnitPrice)
FROM Sales.SalesOrderDetail AS OrdDet
WHERE Ord.SalesOrderID = OrdDet.SalesOrderID) AS MaxUnitPrice
FROM Sales.SalesOrderHeader AS Ord;
GO

Subquery fundamentals
A subquery is also called an inner query or inner select, while the statement containing a
subquery is also called an outer query or outer select.

Many Transact-SQL statements that include subqueries can be alternatively formulated


as joins. Other questions can be posed only with subqueries. In Transact-SQL, there is
usually no performance difference between a statement that includes a subquery and a
semantically equivalent version that does not. For architectural information on how SQL
Server processes queries, see SQL statement processing.However, in some cases where
existence must be checked, a join yields better performance. Otherwise, the nested
query must be processed for each result of the outer query to ensure elimination of
duplicates. In such cases, a join approach would yield better results.

The following is an example showing both a subquery SELECT and a join SELECT that


return the same result set and execution plan:

SQL Snippet
USE[AdventureWorks2019];
GO

/* SELECT statement built using a subquery. */


SELECT [Name]
FROM Production.Product
WHERE ListPrice =
(SELECT ListPrice
FROM Production.Product
WHERE [Name] = 'Chainring Bolts' );
GO

/* SELECT statement built using a join that returns


the same result set. */
SELECT Prd1.[Name]
FROM Production.Product AS Prd1
JOIN Production.Product AS Prd2
ON (Prd1.ListPrice = Prd2.ListPrice)
WHERE Prd2.[Name] = 'Chainring Bolts';
GO

A subquery nested in the outer SELECT statement has the following components:

 A regular SELECT query including the regular select list components.


 A regular FROM clause including one or more table or view names.
 An optional WHERE clause.
 An optional GROUP BY clause.
 An optional HAVING clause.

The SELECT query of a subquery is always enclosed in parentheses. It cannot include


a COMPUTE or FOR BROWSE clause, and may only include an ORDER BY clause when a TOP
clause is also specified.

A subquery can be nested inside the WHERE or HAVING clause of an


outer SELECT, INSERT, UPDATE, or DELETE statement, or inside another subquery. Up to 32
levels of nesting is possible, although the limit varies based on available memory and
the complexity of other expressions in the query. Individual queries may not support
nesting up to 32 levels. A subquery can appear anywhere an expression can be used, if it
returns a single value.

If a table appears only in a subquery and not in the outer query, then columns from that
table cannot be included in the output (the select list of the outer query).

Statements that include a subquery usually take one of these formats:

 WHERE expression \[NOT] IN (subquery)


 WHERE expression comparison_operator \[ANY | ALL] (subquery)
 WHERE \[NOT] EXISTS (subquery)

In some Transact-SQL statements, the subquery can be evaluated as if it were an


independent query. Conceptually, the subquery results are substituted into the outer
query (although this is not necessarily how SQL Server actually processes Transact-SQL
statements with subqueries).

There are three basic types of subqueries. Those that:

 Operate on lists introduced with IN, or those that a comparison operator modified


by ANY or ALL.
 Are introduced with an unmodified comparison operator and must return a single value.
 Are existence tests introduced with EXISTS.

Subquery rules
A subquery is subject to the following restrictions:

 The select list of a subquery introduced with a comparison operator can include only one
expression or column name (except that EXISTS and IN operate on SELECT * or a list,
respectively).
 If the WHERE clause of an outer query includes a column name, it must be join-compatible
with the column in the subquery select list.
 The ntext, text, and image data types cannot be used in the select list of subqueries.
 Because they must return a single value, subqueries introduced by an unmodified
comparison operator (one not followed by the keyword ANY or ALL) cannot include GROUP
BY and HAVING clauses.
 The DISTINCT keyword cannot be used with subqueries that include `GROUP BY.
 The COMPUTE and INTO clauses cannot be specified.
 ORDER BY can only be specified when TOP is also specified.
 A view created by using a subquery cannot be updated.
 The select list of a subquery introduced with EXISTS, by convention, has an asterisk (*)
instead of a single column name. The rules for a subquery introduced with EXISTS are the
same as those for a standard select list, because a subquery introduced
with EXISTS creates an existence test and returns TRUE or FALSE, instead of data.

Qualifying column names in subqueries


In the following example, the BusinessEntityID column in the WHERE clause of the outer
query is implicitly qualified by the table name in the outer query FROM clause
(Sales.Store). The reference to CustomerID in the select list of the subquery is qualified
by the subquery FROM clause, that is, by the Sales.Customer table.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Sales.Store
WHERE BusinessEntityID NOT IN
(SELECT CustomerID
FROM Sales.Customer
WHERE TerritoryID = 5);
GO

The general rule is that column names in a statement are implicitly qualified by the table
referenced in the FROM clause at the same level. If a column does not exist in the table
referenced in the FROM clause of a subquery, it is implicitly qualified by the table
referenced in the FROM clause of the outer query.

Here is what the query looks like with these implicit assumptions specified:

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Sales.Store
WHERE Sales.Store.BusinessEntityID NOT IN
(SELECT Sales.Customer.CustomerID
FROM Sales.Customer
WHERE TerritoryID = 5);
GO

It is never wrong to state the table name explicitly, and it is always possible to override
implicit assumptions about table names with explicit qualifications.

 Important

If a column is referenced in a subquery that does not exist in the table referenced by the
subquery's FROM clause, but exists in a table referenced by the outer query's FROM clause,
the query executes without error. SQL Server implicitly qualifies the column in the
subquery with the table name in the outer query.

Multiple levels of nesting


A subquery can itself include one or more subqueries. Any number of subqueries can be
nested in a statement.

The following query finds the names of employees who are also sales persons.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT LastName, FirstName
FROM Person.Person
WHERE BusinessEntityID IN
(SELECT BusinessEntityID
FROM HumanResources.Employee
WHERE BusinessEntityID IN
(SELECT BusinessEntityID
FROM Sales.SalesPerson)
);
GO

Here is the result set.

Copy
LastName FirstName
-------------------------------------------------- -----------------------
Jiang Stephen
Abbas Syed
Alberts Amy
Ansman-Wolfe Pamela
Campbell David
Carson Jillian
Ito Shu
Mitchell Linda
Reiter Tsvi
Saraiva Jos
Vargas Garrett
Varkey Chudukatil Ranjit
Valdez Rachel
Tsoflias Lynn
Pak Jae
Blythe Michael
Mensa-Annan Tete

(17 row(s) affected)

The innermost query returns the sales person IDs. The query at the next higher level is
evaluated with these sales person IDs and returns the contact ID numbers of the
employees. Finally, the outer query uses the contact IDs to find the names of the
employees.

You can also express this query as a join:

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT LastName, FirstName
FROM Person.Person c
INNER JOIN HumanResources.Employee e
ON c.BusinessEntityID = e.BusinessEntityID
JOIN Sales.SalesPerson s
ON e.BusinessEntityID = s.BusinessEntityID;
GO

Correlated subqueries
Many queries can be evaluated by executing the subquery once and substituting the
resulting value or values into the WHERE clause of the outer query. In queries that include
a correlated subquery (also known as a repeating subquery), the subquery depends on
the outer query for its values. This means that the subquery is executed repeatedly, once
for each row that might be selected by the outer query. This query retrieves one
instance of each employee's first and last name for which the bonus in
the SalesPerson table is 5000 and for which the employee identification numbers match
in the Employee and SalesPerson tables.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT DISTINCT c.LastName, c.FirstName, e.BusinessEntityID
FROM Person.Person AS c JOIN HumanResources.Employee AS e
ON e.BusinessEntityID = c.BusinessEntityID
WHERE 5000.00 IN
(SELECT Bonus
FROM Sales.SalesPerson sp
WHERE e.BusinessEntityID = sp.BusinessEntityID) ;
GO

Here is the result set.

Copy
LastName FirstName BusinessEntityID
-------------------------- ---------- ------------
Ansman-Wolfe Pamela 280
Saraiva José 282

(2 row(s) affected)

The previous subquery in this statement cannot be evaluated independently of the outer
query. It needs a value for Employee.BusinessEntityID, but this value changes as SQL
Server examines different rows in Employee.
That is exactly how this query is evaluated: SQL Server considers each row of the
Employee table for inclusion in the results by substituting the value in each row into the
inner query. For example, if SQL Server first examines the row for Syed Abbas, the
variable Employee.BusinessEntityID takes the value 285, which SQL Server substitutes
into the inner query. These two query samples represent a decomposition of the
previous sample with the correlated subquery.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT Bonus
FROM Sales.SalesPerson
WHERE BusinessEntityID = 285;
GO

The result is 0.00 (Syed Abbas did not receive a bonus because he is not a sales person),
so the outer query evaluates to:

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT LastName, FirstName
FROM Person.Person AS c JOIN HumanResources.Employee AS e
ON e.BusinessEntityID = c.BusinessEntityID
WHERE 5000 IN (0.00);
GO

Because this is false, the row for Syed Abbas is not included in the results of the previous
sample query with the correlated subquery. Go through the same procedure with the
row for Pamela Ansman-Wolfe. You will see that this row is included in the results,
because WHERE 5000 IN (5000) includes results.

Correlated subqueries can also include table-valued functions in the FROM clause by


referencing columns from a table in the outer query as an argument of the table-valued
function. In this case, for each row of the outer query, the table-valued function is
evaluated according to the subquery.

Subquery types
Subqueries can be specified in many places:

 With aliases. For more information, see Subqueries with Aliases.


 With IN or NOT IN. For more information, see Subqueries with IN and Subqueries with NOT
IN.
 In UPDATE, DELETE, and INSERT statements. For more information, see Subqueries in
UPDATE, DELETE, and INSERT Statements.
 With comparison operators. For more information, see Subqueries with Comparison
Operators.
 With ANY, SOME, or ALL. For more information, see Comparison Operators Modified by ANY,
SOME, or ALL.
 With EXISTS or NOT EXISTS. For more information, see Subqueries with
EXISTS and Subqueries with NOT EXISTS.
 In place of an expression. For more information, see Subqueries used in place of an
Expression.
Subqueries with table aliases

Many statements in which the subquery and the outer query refer to the same table can
be stated as self-joins (joining a table to itself). For example, you can find addresses of
employees from a particular state using a subquery:

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT StateProvinceID, AddressID
FROM Person.Address
WHERE AddressID IN
(SELECT AddressID
FROM Person.Address
WHERE StateProvinceID = 39);
GO

Here is the result set.

Copy
StateProvinceID AddressID
----------- -----------
39 942
39 955
39 972
39 22660

(4 row(s) affected)

Or you can use a self-join:

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT e1.StateProvinceID, e1.AddressID
FROM Person.Address AS e1
INNER JOIN Person.Address AS e2
ON e1.AddressID = e2.AddressID
AND e2.StateProvinceID = 39;
GO
Table aliases e1 and e2 are required because the table being joined to itself appears in
two different roles. Aliases can also be used in nested queries that refer to the same
table in an inner and outer query.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT e1.StateProvinceID, e1.AddressID
FROM Person.Address AS e1
WHERE e1.AddressID IN
(SELECT e2.AddressID
FROM Person.Address AS e2
WHERE e2.StateProvinceID = 39);
GO

Explicit table aliases make it clear that a reference to Person.Address in the subquery
does not mean the same thing as the reference in the outer query.

Subqueries with IN

The result of a subquery introduced with IN (or with NOT IN) is a list of zero or more
values. After the subquery returns results, the outer query makes use of them.
The following query finds the names of all the wheel products that Adventure Works
Cycles makes.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID IN
(SELECT ProductSubcategoryID
FROM Production.ProductSubcategory
WHERE [Name] = 'Wheels');
GO

Here is the result set.

Copy
Name
----------------------------
LL Mountain Front Wheel
ML Mountain Front Wheel
HL Mountain Front Wheel
LL Road Front Wheel
ML Road Front Wheel
HL Road Front Wheel
Touring Front Wheel
LL Mountain Rear Wheel
ML Mountain Rear Wheel
HL Mountain Rear Wheel
LL Road Rear Wheel
ML Road Rear Wheel
HL Road Rear Wheel
Touring Rear Wheel

(14 row(s) affected)

This statement is evaluated in two steps. First, the inner query returns the subcategory
identification number that matches the name 'Wheel' (17). Second, this value is
substituted into the outer query, which finds the product names that go with the
subcategory identification numbers in Production.Product.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID IN ('17');
GO

One difference in using a join rather than a subquery for this and similar problems is
that the join lets you show columns from more than one table in the result. For example,
if you want to include the name of the product subcategory in the result, you must use a
join version.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT p.[Name], s.[Name]
FROM Production.Product p
INNER JOIN Production.ProductSubcategory s
ON p.ProductSubcategoryID = s.ProductSubcategoryID
AND s.[Name] = 'Wheels';
GO

Here is the result set.

Copy
Name
LL Mountain Front Wheel Wheels
ML Mountain Front Wheel Wheels
HL Mountain Front Wheel Wheels
LL Road Front Wheel Wheels
ML Road Front Wheel Wheels
HL Road Front Wheel Wheels
Touring Front Wheel Wheels
LL Mountain Rear Wheel Wheels
ML Mountain Rear Wheel Wheels
HL Mountain Rear Wheel Wheels
LL Road Rear Wheel Wheels
ML Road Rear Wheel Wheels
HL Road Rear Wheel Wheels
Touring Rear Wheel Wheels

(14 row(s) affected)

The following query finds the name of all vendors whose credit rating is good, from
whom Adventure Works Cycles orders at least 20 items, and whose average lead time to
deliver is less than 16 days.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Purchasing.Vendor
WHERE CreditRating = 1
AND BusinessEntityID IN
(SELECT BusinessEntityID
FROM Purchasing.ProductVendor
WHERE MinOrderQty >= 20
AND AverageLeadTime < 16);
GO

Here is the result set.

Copy
Name
--------------------------------------------------
Compete Enterprises, Inc
International Trek Center
First National Sport Co.
Comfort Road Bicycles
Circuit Cycles
First Rate Bicycles
Jeff's Sporting Goods
Competition Bike Training Systems
Electronic Bike Repair & Supplies
Crowley Sport
Expert Bike Co
Team Athletic Co.
Compete, Inc.

(13 row(s) affected)


The inner query is evaluated, producing the ID numbers of the vendors who meet the
subquery qualifications. The outer query is then evaluated. Notice that you can include
more than one condition in the WHERE clause of both the inner and the outer query.

Using a join, the same query is expressed like this:

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT DISTINCT [Name]
FROM Purchasing.Vendor v
INNER JOIN Purchasing.ProductVendor p
ON v.BusinessEntityID = p.BusinessEntityID
WHERE CreditRating = 1
AND MinOrderQty >= 20
AND AverageLeadTime < 16;
GO

A join can always be expressed as a subquery. A subquery can often, but not always, be
expressed as a join. This is because joins are symmetric: you can join table A to B in
either order and get the same answer. The same is not true if a subquery is involved.

Subqueries with NOT IN
Subqueries introduced with the keyword NOT IN also return a list of zero or more values.
The following query finds the names of the products that are not finished bicycles.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID NOT IN
(SELECT ProductSubcategoryID
FROM Production.ProductSubcategory
WHERE [Name] = 'Mountain Bikes'
OR [Name] = 'Road Bikes'
OR [Name] = 'Touring Bikes');
GO

This statement cannot be converted to a join. The analogous not-equal join has a
different meaning: It finds the names of products that are in some subcategory that is
not a finished bicycle.
Subqueries in UPDATE, DELETE, and INSERT statements

Subqueries can be nested in the UPDATE, DELETE, INSERT and SELECT data manipulation


(DML) statements.

The following example doubles the value in the ListPrice column in


the Production.Product table. The subquery in the WHERE clause references
the Purchasing.ProductVendor table to restrict the rows updated in the Product table to
just those supplied by BusinessEntity 1540.

SQL Snippet
USE[AdventureWorks2019];
GO
UPDATE Production.Product
SET ListPrice = ListPrice * 2
WHERE ProductID IN
(SELECT ProductID
FROM Purchasing.ProductVendor
WHERE BusinessEntityID = 1540);
GO

Here is an equivalent UPDATE statement using a join:

SQL Snippet
USE[AdventureWorks2019];
GO
UPDATE Production.Product
SET ListPrice = ListPrice * 2
FROM Production.Product AS p
INNER JOIN Purchasing.ProductVendor AS pv
ON p.ProductID = pv.ProductID AND BusinessEntityID = 1540;
GO

For clarity in case the same table is itself referenced in other subqueries, use the target
table's alias:

SQL Snippet
USE[AdventureWorks2019];
GO
UPDATE p
SET ListPrice = ListPrice * 2
FROM Production.Product AS p
INNER JOIN Purchasing.ProductVendor AS pv
ON p.ProductID = pv.ProductID AND BusinessEntityID = 1540;
GO
Subqueries with comparison operators

Subqueries can be introduced with one of the comparison operators ( =, < >, >, > =, <, !
>, ! <, or < =).

A subquery introduced with an unmodified comparison operator (a comparison


operator not followed by ANY or ALL) must return a single value rather than a list of
values, like subqueries introduced with IN. If such a subquery returns more than one
value, SQL Server displays an error message.

To use a subquery introduced with an unmodified comparison operator, you must be


familiar enough with your data and with the nature of the problem to know that the
subquery will return exactly one value.

For example, if you assume each sales person only covers one sales territory, and you
want to find the customers located in the territory covered by Linda Mitchell, you can
write a statement with a subquery introduced with the simple = comparison operator.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT CustomerID
FROM Sales.Customer
WHERE TerritoryID =
(SELECT TerritoryID
FROM Sales.SalesPerson
WHERE BusinessEntityID = 276);
GO

If, however, Linda Mitchell covered more than one sales territory, then an error message
would result. Instead of the = comparison operator, an IN formulation could be used
(=ANY also works).

Subqueries introduced with unmodified comparison operators often include aggregate


functions, because these return a single value. For example, the following statement
finds the names of all products whose list price is greater than the average list price.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE ListPrice >
(SELECT AVG (ListPrice)
FROM Production.Product);
GO

Because subqueries introduced with unmodified comparison operators must return a


single value, they cannot include GROUP BY or HAVING clauses unless you know the GROUP
BY or HAVING clause itself returns a single value. For example, the following query finds
the products priced higher than the lowest-priced product that is in
ProductSubcategoryID 14.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE ListPrice >
(SELECT MIN (ListPrice)
FROM Production.Product
GROUP BY ProductSubcategoryID
HAVING ProductSubcategoryID = 14);
GO
Comparison operators modified by ANY, SOME, or ALL

Comparison operators that introduce a subquery can be modified by the


keywords ALL or ANY. SOME is an ISO standard equivalent for ANY. For more information on
these comparison operators, see SOME | ANY.

Subqueries introduced with a modified comparison operator return a list of zero or


more values and can include a GROUP BY or HAVING clause. These subqueries can be
restated with EXISTS.

Using the > comparison operator as an example, >ALL means greater than every value. In
other words, it means greater than the maximum value. For example, >ALL (1, 2,
3) means greater than 3. >ANY means greater than at least one value, that is, greater than
the minimum. So >ANY (1, 2, 3) means greater than 1. For a row in a subquery
with >ALL to satisfy the condition specified in the outer query, the value in the column
introducing the subquery must be greater than each value in the list of values returned
by the subquery.

Similarly, >ANY means that for a row to satisfy the condition specified in the outer query,
the value in the column that introduces the subquery must be greater than at least one
of the values in the list of values returned by the subquery.
The following query provides an example of a subquery introduced with a comparison
operator modified by ANY. It finds the products whose list prices are greater than or
equal to the maximum list price of any product subcategory.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE ListPrice >= ANY
(SELECT MAX (ListPrice)
FROM Production.Product
GROUP BY ProductSubcategoryID);
GO

For each Product subcategory, the inner query finds the maximum list price. The outer
query looks at all of these values and determines which individual product's list prices
are greater than or equal to any product subcategory's maximum list price. If ANY is
changed to ALL, the query will return only those products whose list price is greater than
or equal to all the list prices returned in the inner query.

If the subquery does not return any values, the entire query fails to return any values.

The =ANY operator is equivalent to IN. For example, to find the names of all the wheel
products that Adventure Works Cycles makes, you can use either IN or =ANY.

SQL Snippet
--Using =ANY
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID =ANY
(SELECT ProductSubcategoryID
FROM Production.ProductSubcategory
WHERE Name = 'Wheels');
GO

--Using IN
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID IN
(SELECT ProductSubcategoryID
FROM Production.ProductSubcategory
WHERE Name = 'Wheels');
GO
Here is the result set for either query:

Copy
Name
--------------------------------------------------
LL Mountain Front Wheel
ML Mountain Front Wheel
HL Mountain Front Wheel
LL Road Front Wheel
ML Road Front Wheel
HL Road Front Wheel
Touring Front Wheel
LL Mountain Rear Wheel
ML Mountain Rear Wheel
HL Mountain Rear Wheel
LL Road Rear Wheel
ML Road Rear Wheel
HL Road Rear Wheel
Touring Rear Wheel

(14 row(s) affected)

The <>ANY operator, however, differs from NOT IN:

 <>ANY means not = a, or not = b, or not = c


 NOT IN means not = a, and not = b, and not = c
 <>ALL means the same as NOT IN

For example, the following query finds customers located in a territory not covered by
any sales persons.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT CustomerID
FROM Sales.Customer
WHERE TerritoryID <> ANY
(SELECT TerritoryID
FROM Sales.SalesPerson);
GO

The results include all customers, except those whose sales territories are NULL, because
every territory that is assigned to a customer is covered by a sales person. The inner
query finds all the sales territories covered by sales persons, and then, for each territory,
the outer query finds the customers who are not in one.
For the same reason, when you use NOT IN in this query, the results include none of the
customers.

You can get the same results with the <>ALL operator, which is equivalent to NOT IN.

Subqueries with EXISTS

When a subquery is introduced with the keyword EXISTS, the subquery functions as an


existence test. The WHERE clause of the outer query tests whether the rows that are
returned by the subquery exist. The subquery does not actually produce any data; it
returns a value of TRUE or FALSE.

A subquery introduced with EXISTS has the following syntax:

WHERE [NOT] EXISTS (subquery)

The following query finds the names of all products that are in the Wheels subcategory:

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE EXISTS
(SELECT *
FROM Production.ProductSubcategory
WHERE ProductSubcategoryID =
Production.Product.ProductSubcategoryID
AND [Name] = 'Wheels');
GO

Here is the result set.

Copy
Name
--------------------------------------------------
LL Mountain Front Wheel
ML Mountain Front Wheel
HL Mountain Front Wheel
LL Road Front Wheel
ML Road Front Wheel
HL Road Front Wheel
Touring Front Wheel
LL Mountain Rear Wheel
ML Mountain Rear Wheel
HL Mountain Rear Wheel
LL Road Rear Wheel
ML Road Rear Wheel
HL Road Rear Wheel
Touring Rear Wheel

(14 row(s) affected)

To understand the results of this query, consider the name of each product in turn. Does
this value cause the subquery to return at least one row? In other words, does the query
cause the existence test to evaluate to TRUE?

Notice that subqueries that are introduced with EXISTS are a bit different from other
subqueries in the following ways:

 The keyword EXISTS is not preceded by a column name, constant, or other expression.


 The select list of a subquery introduced by EXISTS almost always consists of an asterisk (*).
There is no reason to list column names because you are just testing whether rows that
meet the conditions specified in the subquery exist.

The EXISTS keyword is important because frequently there is no alternative formulation


without subqueries. Although some queries that are created with EXISTS cannot be
expressed any other way, many queries can use IN or a comparison operator modified
by ANY or ALL to achieve similar results.

For example, the preceding query can be expressed by using IN:

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID IN
(SELECT ProductSubcategoryID
FROM Production.ProductSubcategory
WHERE [Name] = 'Wheels');
GO
Subqueries with NOT EXISTS
NOT EXISTS works like EXISTS, except the WHERE clause in which it is used is satisfied if no
rows are returned by the subquery.

For example, to find the names of products that are not in the wheels subcategory:

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name]
FROM Production.Product
WHERE NOT EXISTS
(SELECT *
FROM Production.ProductSubcategory
WHERE ProductSubcategoryID =
Production.Product.ProductSubcategoryID
AND [Name] = 'Wheels');
GO
Subqueries used in place of an expression

In Transact-SQL, a subquery can be substituted anywhere an expression can be used


in SELECT, UPDATE, INSERT, and DELETE statements, except in an ORDER BY list.

The following example illustrates how you might use this enhancement. This query finds
the prices of all mountain bike products, their average price, and the difference between
the price of each mountain bike and the average price.

SQL Snippet
USE[AdventureWorks2019];
GO
SELECT [Name], ListPrice,
(SELECT AVG(ListPrice) FROM Production.Product) AS Average,
ListPrice - (SELECT AVG(ListPrice) FROM Production.Product)
AS Difference
FROM Production.Product
WHERE ProductSubcategoryID = 1;
GO
Views

Link : https://docs.microsoft.com/en-us/sql/relational-databases/views/views?view=sql-
server-ver15

A view is a virtual table whose contents are defined by a query. Like a table, a view
consists of a set of named columns and rows of data. Unless indexed, a view does not
exist as a stored set of data values in a database. The rows and columns of data come
from tables referenced in the query defining the view and are produced dynamically
when the view is referenced.

A view acts as a filter on the underlying tables referenced in the view. The query that
defines the view can be from one or more tables or from other views in the current or
other databases. Distributed queries can also be used to define views that use data from
multiple heterogeneous sources. This is useful, for example, if you want to combine
similarly structured data from different servers, each of which stores data for a different
region of your organization.

Views are generally used to focus, simplify, and customize the perception each user has
of the database. Views can be used as security mechanisms by letting users access data
through the view, without granting the users permissions to directly access the
underlying base tables of the view. Views can be used to provide a backward compatible
interface to emulate a table that used to exist but whose schema has changed. Views
can also be used when you copy data to and from SQL Server to improve performance
and to partition data.

Types of Views
Besides the standard role of basic user-defined views, SQL Server provides the following
types of views that serve special purposes in a database.

Indexed Views
An indexed view is a view that has been materialized. This means the view definition has
been computed and the resulting data stored just like a table. You index a view by
creating a unique clustered index on it. Indexed views can dramatically improve the
performance of some types of queries. Indexed views work best for queries that
aggregate many rows. They are not well-suited for underlying data sets that are
frequently updated.

Partitioned Views
A partitioned view joins horizontally partitioned data from a set of member tables across
one or more servers. This makes the data appear as if from one table. A view that joins
member tables on the same instance of SQL Server is a local partitioned view.

System Views
System views expose catalog metadata. You can use system views to return information
about the instance of SQL Server or the objects defined in the instance. For example,
you can query the sys.databases catalog view to return information about the user-
defined databases available in the instance. For more information, see System Views
(Transact-SQL)

Common View Tasks


The following table provides links to common tasks associated with creating or
modifying a view.

COMMON VIEW TASKS

View Tasks Topic

Describes how to create a view. Create Views

Describes how to create an indexed view. Create Indexed Views

Describes how to modify the view definition. Modify Views

Describes how to modify data through a view. Modify Data Through a View

Describes how to delete a view. Delete Views

Describes how to return information about a view such as the view Get Information About a
definition. View

Describes how to rename a view. Rename Views


Query Processing Architecture Guide

https://docs.microsoft.com/en-us/sql/relational-databases/query-processing-
architecture-guide?view=sql-server-ver15

The SQL Server Database Engine processes queries on various data storage architectures
such as local tables, partitioned tables, and tables distributed across multiple servers.
The following topics cover how SQL Server processes queries and optimizes query reuse
through execution plan caching.

Execution modes
The SQL Server Database Engine can process Transact-SQL statements using two
distinct processing modes:

 Row mode execution


 Batch mode execution
Row mode execution

Row mode execution is a query processing method used with traditional RDBMS tables,
where data is stored in row format. When a query is executed and accesses data in row
store tables, the execution tree operators and child operators read each required row,
across all the columns specified in the table schema. From each row that is read, SQL
Server then retrieves the columns that are required for the result set, as referenced by a
SELECT statement, JOIN predicate, or filter predicate.

 Note

Row mode execution is very efficient for OLTP scenarios, but can be less efficient when
scanning large amounts of data, for example in Data Warehousing scenarios.

Batch mode execution

Batch mode execution is a query processing method used to process multiple rows
together (hence the term batch). Each column within a batch is stored as a vector in a
separate area of memory, so batch mode processing is vector-based. Batch mode
processing also uses algorithms that are optimized for the multi-core CPUs and
increased memory throughput that are found on modern hardware.

When it was first introduced, batch mode execution was closely integrated with, and
optimized around, the columnstore storage format. However, starting with SQL Server
2019 (15.x) and in Azure SQL Database, batch mode execution no longer requires
columnstore indexes. For more information, see Batch mode on rowstore.

Batch mode processing operates on compressed data when possible, and eliminates
the exchange operator used by row mode execution. The result is better parallelism and
faster performance.

When a query is executed in batch mode, and accesses data in columnstore indexes, the
execution tree operators and child operators read multiple rows together in column
segments. SQL Server reads only the columns required for the result, as referenced by a
SELECT statement, JOIN predicate, or filter predicate. For more information on
columnstore indexes, see Columnstore Index Architecture.

 Note

Batch mode execution is very efficient Data Warehousing scenarios, where large
amounts of data are read and aggregated.

SQL Statement Processing


Processing a single Transact-SQL statement is the most basic way that SQL Server
executes Transact-SQL statements. The steps used to process a single SELECT statement
that references only local base tables (no views or remote tables) illustrates the basic
process.

Logical Operator Precedence

When more than one logical operator is used in a statement, NOT is evaluated first,
then AND, and finally OR. Arithmetic, and bitwise, operators are handled before logical
operators. For more information, see Operator Precedence.

In the following example, the color condition pertains to product model 21, and not to
product model 20, because AND has precedence over OR.

SQLCopy
SELECT ProductID, ProductModelID
FROM Production.Product
WHERE ProductModelID = 20 OR ProductModelID = 21
AND Color = 'Red';
GO

You can change the meaning of the query by adding parentheses to force evaluation of
the OR first. The following query finds only products under models 20 and 21 that are
red.

SQLCopy
SELECT ProductID, ProductModelID
FROM Production.Product
WHERE (ProductModelID = 20 OR ProductModelID = 21)
AND Color = 'Red';
GO

Using parentheses, even when they are not required, can improve the readability of
queries, and reduce the chance of making a subtle mistake because of operator
precedence. There is no significant performance penalty in using parentheses. The
following example is more readable than the original example, although they are
syntactically the same.

SQLCopy
SELECT ProductID, ProductModelID
FROM Production.Product
WHERE ProductModelID = 20 OR (ProductModelID = 21
AND Color = 'Red');
GO
Optimizing SELECT statements

A SELECT statement is non-procedural; it does not state the exact steps that the database
server should use to retrieve the requested data. This means that the database server
must analyze the statement to determine the most efficient way to extract the requested
data. This is referred to as optimizing the SELECT statement. The component that does
this is called the Query Optimizer. The input to the Query Optimizer consists of the
query, the database schema (table and index definitions), and the database statistics.
The output of the Query Optimizer is a query execution plan, sometimes referred to as a
query plan, or execution plan. The contents of an execution plan are described in more
detail later in this topic.

The inputs and outputs of the Query Optimizer during optimization of a


single SELECT statement are illustrated in the following diagram:
A SELECT statement defines only the following:

 The format of the result set. This is specified mostly in the select list. However, other
clauses such as ORDER BY and GROUP BY also affect the final form of the result set.
 The tables that contain the source data. This is specified in the FROM clause.
 How the tables are logically related for the purposes of the SELECT statement. This is
defined in the join specifications, which may appear in the WHERE clause or in an ON clause
following FROM.
 The conditions that the rows in the source tables must satisfy to qualify for
the SELECT statement. These are specified in the WHERE and HAVING clauses.

A query execution plan is a definition of the following:

 The sequence in which the source tables are accessed. Typically, there are many
sequences in which the database server can access the base tables to build the
result set. For example, if the SELECT statement references three tables, the
database server could first access TableA, use the data from TableA to extract
matching rows from TableB, and then use the data from TableB to extract data
from TableC. The other sequences in which the database server could access the
tables are:
TableC, TableB, TableA, or
TableB, TableA, TableC, or
TableB, TableC, TableA, or
TableC, TableA, TableB

 The methods used to extract data from each table.


Generally, there are different methods for accessing the data in each table. If only a
few rows with specific key values are required, the database server can use an
index. If all the rows in the table are required, the database server can ignore the
indexes and perform a table scan. If all the rows in a table are required but there is
an index whose key columns are in an ORDER BY, performing an index scan instead
of a table scan may save a separate sort of the result set. If a table is very small,
table scans may be the most efficient method for almost all access to the table.
 The methods used to compute calculations, and how to filter, aggregate, and
sort data from each table.
As data is accessed from tables, there are different methods to perform
calculations over data such as computing scalar values, and to aggregate and sort
data as defined in the query text, for example when using a GROUP BY or ORDER
BY clause, and how to filter data, for example when using a WHERE or HAVING clause.

The process of selecting one execution plan from potentially many possible plans is
referred to as optimization. The Query Optimizer is one of the most important
components of the Database Engine. While some overhead is used by the Query
Optimizer to analyze the query and select a plan, this overhead is typically saved
several-fold when the Query Optimizer picks an efficient execution plan. For example,
two construction companies can be given identical blueprints for a house. If one
company spends a few days at the beginning to plan how they will build the house, and
the other company begins building without planning, the company that takes the time
to plan their project will probably finish first.

The SQL Server Query Optimizer is a cost-based optimizer. Each possible execution plan
has an associated cost in terms of the amount of computing resources used. The Query
Optimizer must analyze the possible plans and choose the one with the lowest
estimated cost. Some complex SELECT statements have thousands of possible execution
plans. In these cases, the Query Optimizer does not analyze all possible combinations.
Instead, it uses complex algorithms to find an execution plan that has a cost reasonably
close to the minimum possible cost.

The SQL Server Query Optimizer does not choose only the execution plan with the
lowest resource cost; it chooses the plan that returns results to the user with a
reasonable cost in resources and that returns the results the fastest. For example,
processing a query in parallel typically uses more resources than processing it serially,
but completes the query faster. The SQL Server Query Optimizer will use a parallel
execution plan to return results if the load on the server will not be adversely affected.

The SQL Server Query Optimizer relies on distribution statistics when it estimates the
resource costs of different methods for extracting information from a table or index.
Distribution statistics are kept for columns and indexes, and hold information on the
density1 of the underlying data. This is used to indicate the selectivity of the values in a
particular index or column. For example, in a table representing cars, many cars have the
same manufacturer, but each car has a unique vehicle identification number (VIN). An
index on the VIN is more selective than an index on the manufacturer, because VIN has
lower density than manufacturer. If the index statistics are not current, the Query
Optimizer may not make the best choice for the current state of the table. For more
information about densities, see Statistics.

 Density defines the distribution of unique values that exist in the data, or the average
1

number of duplicate values for a given column. As density decreases, selectivity of a


value increases.

The SQL Server Query Optimizer is important because it enables the database server to
adjust dynamically to changing conditions in the database without requiring input from
a programmer or database administrator. This enables programmers to focus on
describing the final result of the query. They can trust that the SQL Server Query
Optimizer will build an efficient execution plan for the state of the database every time
the statement is run.

 Note

SQL Server Management Studio has three options to display execution plans:

 The Estimated Execution Plan, which is the compiled plan, as produced by the Query
Optimizer.
 The Actual Execution Plan, which is the same as the compiled plan plus its execution
context. This includes runtime information available after the execution completes, such as
execution warnings, or in newer versions of the Database Engine, the elapsed and CPU
time used during execution.
 The Live Query Statistics, which is the same as the compiled plan plus its execution
context. This includes runtime information during execution progress, and is updated
every second. Runtime information includes for example the actual number of rows
flowing through the operators.
Processing a SELECT Statement

The basic steps that SQL Server uses to process a single SELECT statement include the
following:

1. The parser scans the SELECT statement and breaks it into logical units such as keywords,
expressions, operators, and identifiers.
2. A query tree, sometimes referred to as a sequence tree, is built describing the logical steps
needed to transform the source data into the format required by the result set.
3. The Query Optimizer analyzes different ways the source tables can be accessed. It then
selects the series of steps that return the results fastest while using fewer resources. The
query tree is updated to record this exact series of steps. The final, optimized version of
the query tree is called the execution plan.
4. The relational engine starts executing the execution plan. As the steps that require data
from the base tables are processed, the relational engine requests that the storage engine
pass up data from the rowsets requested from the relational engine.
5. The relational engine processes the data returned from the storage engine into the format
defined for the result set and returns the result set to the client.
Constant Folding and Expression Evaluation

SQL Server evaluates some constant expressions early to improve query performance.
This is referred to as constant folding. A constant is a Transact-SQL literal, such
as 3, 'ABC', '2005-12-31', 1.0e3, or 0x12345678.

Foldable Expressions

SQL Server uses constant folding with the following types of expressions:

 Arithmetic expressions, such as 1+1, 5/3*2, that contain only constants.


 Logical expressions, such as 1=1 and 1>2 AND 3>4, that contain only constants.
 Built-in functions that are considered foldable by SQL Server, including CAST and CONVERT.
Generally, an intrinsic function is foldable if it is a function of its inputs only and not other
contextual information, such as SET options, language settings, database options, and
encryption keys. Nondeterministic functions are not foldable. Deterministic built-in
functions are foldable, with some exceptions.
 Deterministic methods of CLR user-defined types and deterministic scalar-valued CLR
user-defined functions (starting with SQL Server 2012 (11.x)). For more information,
see Constant Folding for CLR User-Defined Functions and Methods.
 Note

An exception is made for large object types. If the output type of the folding process is a
large object type (text,ntext, image, nvarchar(max), varchar(max), varbinary(max), or
XML), then SQL Server does not fold the expression.

Nonfoldable Expressions

All other expression types are not foldable. In particular, the following types of
expressions are not foldable:

 Nonconstant expressions such as an expression whose result depends on the value of a


column.
 Expressions whose results depend on a local variable or parameter, such as @x.
 Nondeterministic functions.
 User-defined Transact-SQL functions 1.
 Expressions whose results depend on language settings.
 Expressions whose results depend on SET options.
 Expressions whose results depend on server configuration options.

 Before SQL Server 2012 (11.x), deterministic scalar-valued CLR user-defined functions
1

and methods of CLR user-defined types were not foldable.

Examples of Foldable and Nonfoldable Constant Expressions

Consider the following query:

SQLCopy
SELECT *
FROM Sales.SalesOrderHeader AS s
INNER JOIN Sales.SalesOrderDetail AS d
ON s.SalesOrderID = d.SalesOrderID
WHERE TotalDue > 117.00 + 1000.00;

If the PARAMETERIZATION database option is not set to FORCED for this query, then the


expression 117.00 + 1000.00 is evaluated and replaced by its result, 1117.00, before the
query is compiled. Benefits of this constant folding include the following:

 The expression does not have to be evaluated repeatedly at run time.


 The value of the expression after it is evaluated is used by the Query Optimizer to estimate
the size of the result set of the portion of the query TotalDue > 117.00 + 1000.00.

On the other hand, if dbo.f is a scalar user-defined function, the expression dbo.f(100) is


not folded, because SQL Server does not fold expressions that involve user-defined
functions, even if they are deterministic. For more information on parameterization,
see Forced Parameterization later in this article.

Expression Evaluation

In addition, some expressions that are not constant folded but whose arguments are
known at compile time, whether the arguments are parameters or constants, are
evaluated by the result-set size (cardinality) estimator that is part of the optimizer
during optimization.

Specifically, the following built-in functions and special operators are evaluated at
compile time if all their inputs are known: UPPER, LOWER, RTRIM, DATEPART( YY
only ), GETDATE, CAST, and CONVERT. The following operators are also evaluated at compile
time if all their inputs are known:

 Arithmetic operators: +, -, *, /, unary -


 Logical Operators: AND, OR, NOT
 Comparison operators: <, >, <=, >=, <>, LIKE, IS NULL, IS NOT NULL

No other functions or operators are evaluated by the Query Optimizer during cardinality
estimation.

Examples of Compile-Time Expression Evaluation

Consider this stored procedure:

SQLCopy
USE AdventureWorks2014;
GO
CREATE PROCEDURE MyProc( @d datetime )
AS
SELECT COUNT(*)
FROM Sales.SalesOrderHeader
WHERE OrderDate > @d+1;

During optimization of the SELECT statement in the procedure, the Query Optimizer tries


to evaluate the expected cardinality of the result set for the condition OrderDate > @d+1.
The expression @d+1 is not constant-folded, because @d is a parameter. However, at
optimization time, the value of the parameter is known. This allows the Query Optimizer
to accurately estimate the size of the result set, which helps it select a good query plan.

Now consider an example similar to the previous one, except that a local
variable @d2 replaces @d+1 in the query and the expression is evaluated in a SET
statement instead of in the query.

SQLCopy
USE AdventureWorks2014;
GO
CREATE PROCEDURE MyProc2( @d datetime )
AS
BEGIN
DECLARE @d2 datetime
SET @d2 = @d+1
SELECT COUNT(*)
FROM Sales.SalesOrderHeader
WHERE OrderDate > @d2
END;

When the SELECT statement in MyProc2 is optimized in SQL Server, the value of @d2 is not
known. Therefore, the Query Optimizer uses a default estimate for the selectivity
of OrderDate > @d2, (in this case 30 percent).
Processing Other Statements

The basic steps described for processing a SELECT statement apply to other Transact-SQL
statements such as INSERT, UPDATE, and DELETE. UPDATE and DELETE statements both have
to target the set of rows to be modified or deleted. The process of identifying these
rows is the same process used to identify the source rows that contribute to the result
set of a SELECT statement. The UPDATE and INSERT statements may both contain
embedded SELECT statements that provide the data values to be updated or inserted.

Even Data Definition Language (DDL) statements, such as CREATE PROCEDURE or ALTER


TABLE, are ultimately resolved to a series of relational operations on the system catalog
tables and sometimes (such as ALTER TABLE ADD COLUMN) against the data tables.

Worktables

The Relational Engine may need to build a worktable to perform a logical operation
specified in an Transact-SQL statement. Worktables are internal tables that are used to
hold intermediate results. Worktables are generated for certain GROUP BY, ORDER BY,
or UNION queries. For example, if an ORDER BY clause references columns that are not
covered by any indexes, the Relational Engine may need to generate a worktable to sort
the result set into the order requested. Worktables are also sometimes used as spools
that temporarily hold the result of executing a part of a query plan. Worktables are built
in tempdb and are dropped automatically when they are no longer needed.

View Resolution

The SQL Server query processor treats indexed and nonindexed views differently:

 The rows of an indexed view are stored in the database in the same format as a table. If
the Query Optimizer decides to use an indexed view in a query plan, the indexed view is
treated the same way as a base table.
 Only the definition of a nonindexed view is stored, not the rows of the view. The Query
Optimizer incorporates the logic from the view definition into the execution plan it builds
for the Transact-SQL statement that references the nonindexed view.

The logic used by the SQL Server Query Optimizer to decide when to use an indexed
view is similar to the logic used to decide when to use an index on a table. If the data in
the indexed view covers all or part of the Transact-SQL statement, and the Query
Optimizer determines that an index on the view is the low-cost access path, the Query
Optimizer will choose the index regardless of whether the view is referenced by name in
the query.
When an Transact-SQL statement references a nonindexed view, the parser and Query
Optimizer analyze the source of both the Transact-SQL statement and the view and then
resolve them into a single execution plan. There is not one plan for the Transact-SQL
statement and a separate plan for the view.

For example, consider the following view:

SQLCopy
USE AdventureWorks2014;
GO
CREATE VIEW EmployeeName AS
SELECT h.BusinessEntityID, p.LastName, p.FirstName
FROM HumanResources.Employee AS h
JOIN Person.Person AS p
ON h.BusinessEntityID = p.BusinessEntityID;
GO

Based on this view, both of these Transact-SQL statements perform the same operations
on the base tables and produce the same results:

SQLCopy
/* SELECT referencing the EmployeeName view. */
SELECT LastName AS EmployeeLastName, SalesOrderID, OrderDate
FROM AdventureWorks2014.Sales.SalesOrderHeader AS soh
JOIN AdventureWorks2014.dbo.EmployeeName AS EmpN
ON (soh.SalesPersonID = EmpN.BusinessEntityID)
WHERE OrderDate > '20020531';

/* SELECT referencing the Person and Employee tables directly. */


SELECT LastName AS EmployeeLastName, SalesOrderID, OrderDate
FROM AdventureWorks2014.HumanResources.Employee AS e
JOIN AdventureWorks2014.Sales.SalesOrderHeader AS soh
ON soh.SalesPersonID = e.BusinessEntityID
JOIN AdventureWorks2014.Person.Person AS p
ON e.BusinessEntityID =p.BusinessEntityID
WHERE OrderDate > '20020531';

The SQL Server Management Studio Showplan feature shows that the relational engine
builds the same execution plan for both of these SELECT statements.

Using Hints with Views

Hints that are placed on views in a query may conflict with other hints that are
discovered when the view is expanded to access its base tables. When this occurs, the
query returns an error. For example, consider the following view that contains a table
hint in its definition:
SQLCopy
USE AdventureWorks2014;
GO
CREATE VIEW Person.AddrState WITH SCHEMABINDING AS
SELECT a.AddressID, a.AddressLine1,
s.StateProvinceCode, s.CountryRegionCode
FROM Person.Address a WITH (NOLOCK), Person.StateProvince s
WHERE a.StateProvinceID = s.StateProvinceID;

Now suppose you enter this query:

SQLCopy
SELECT AddressID, AddressLine1, StateProvinceCode, CountryRegionCode
FROM Person.AddrState WITH (SERIALIZABLE)
WHERE StateProvinceCode = 'WA';

The query fails, because the hint SERIALIZABLE that is applied on view Person.AddrState in


the query is propagated to both tables Person.Address and Person.StateProvince in the
view when it is expanded. However, expanding the view also reveals the NOLOCK hint
on Person.Address. Because the SERIALIZABLE and NOLOCK hints conflict, the resulting
query is incorrect.

The PAGLOCK, NOLOCK, ROWLOCK, TABLOCK, or TABLOCKX table hints conflict with each other, as


do the HOLDLOCK, NOLOCK, READCOMMITTED, REPEATABLEREAD, SERIALIZABLE table hints.

Hints can propagate through levels of nested views. For example, suppose a query
applies the HOLDLOCK hint on a view v1. When v1 is expanded, we find that view v2 is part
of its definition. v2's definition includes a NOLOCK hint on one of its base tables. But this
table also inherits the HOLDLOCK hint from the query on view v1. Because
the NOLOCK and HOLDLOCK hints conflict, the query fails.

When the FORCE ORDER hint is used in a query that contains a view, the join order of the
tables within the view is determined by the position of the view in the ordered construct.
For example, the following query selects from three tables and a view:

SQLCopy
SELECT * FROM Table1, Table2, View1, Table3
WHERE Table1.Col1 = Table2.Col1
AND Table2.Col1 = View1.Col1
AND View1.Col2 = Table3.Col2;
OPTION (FORCE ORDER);

And View1 is defined as shown in the following:


SQLCopy
CREATE VIEW View1 AS
SELECT Colx, Coly FROM TableA, TableB
WHERE TableA.ColZ = TableB.Colz;

The join order in the query plan is Table1, Table2, TableA, TableB, Table3.

Resolving Indexes on Views

As with any index, SQL Server chooses to use an indexed view in its query plan only if
the Query Optimizer determines it is beneficial to do so.

Indexed views can be created in any edition of SQL Server. In some editions of some
versions of SQL Server, the Query Optimizer automatically considers the indexed view. In
some editions of some versions of SQL Server, to use an indexed view,
the NOEXPAND table hint must be used. For clarification, see the documentation for each
version.

The SQL Server Query Optimizer uses an indexed view when the following conditions
are met:

 These session options are set to ON:


o ANSI_NULLS
o ANSI_PADDING
o ANSI_WARNINGS
o ARITHABORT
o CONCAT_NULL_YIELDS_NULL
o QUOTED_IDENTIFIER
 The NUMERIC_ROUNDABORT session option is set to OFF.
 The Query Optimizer finds a match between the view index columns and elements in the
query, such as the following:
o Search condition predicates in the WHERE clause
o Join operations
o Aggregate functions
o GROUP BY clauses
o Table references
 The estimated cost for using the index has the lowest cost of any access mechanisms
considered by the Query Optimizer.
 Every table referenced in the query (either directly, or by expanding a view to access its
underlying tables) that corresponds to a table reference in the indexed view must have the
same set of hints applied on it in the query.
 Note
The READCOMMITTED and READCOMMITTEDLOCK hints are always considered different hints in
this context, regardless of the current transaction isolation level.

Other than the requirements for the SET options and table hints, these are the same
rules that the Query Optimizer uses to determine whether a table index covers a query.
Nothing else has to be specified in the query for an indexed view to be used.

A query does not have to explicitly reference an indexed view in the FROM clause for the
Query Optimizer to use the indexed view. If the query contains references to columns in
the base tables that are also present in the indexed view, and the Query Optimizer
estimates that using the indexed view provides the lowest cost access mechanism, the
Query Optimizer chooses the indexed view, similar to the way it chooses base table
indexes when they are not directly referenced in a query. The Query Optimizer may
choose the view when it contains columns that are not referenced by the query, as long
as the view offers the lowest cost option for covering one or more of the columns
specified in the query.

The Query Optimizer treats an indexed view referenced in the FROM clause as a standard


view. The Query Optimizer expands the definition of the view into the query at the start
of the optimization process. Then, indexed view matching is performed. The indexed
view may be used in the final execution plan selected by the Query Optimizer, or
instead, the plan may materialize necessary data from the view by accessing the base
tables referenced by the view. The Query Optimizer chooses the lowest-cost alternative.

Using Hints with Indexed Views

You can prevent view indexes from being used for a query by using the EXPAND
VIEWS query hint, or you can use the NOEXPAND table hint to force the use of an index for
an indexed view specified in the FROM clause of a query. However, you should let the
Query Optimizer dynamically determine the best access methods to use for each query.
Limit your use of EXPAND and NOEXPAND to specific cases where testing has shown that
they improve performance significantly.

The EXPAND VIEWS option specifies that the Query Optimizer not use any view indexes for
the whole query.

When NOEXPAND is specified for a view, the Query Optimizer considers using any indexes
defined on the view. NOEXPAND specified with the optional INDEX() clause forces the Query
Optimizer to use the specified indexes. NOEXPAND can be specified only for an indexed
view and cannot be specified for a view not indexed.
When neither NOEXPAND nor EXPAND VIEWS is specified in a query that contains a view, the
view is expanded to access underlying tables. If the query that makes up the view
contains any table hints, these hints are propagated to the underlying tables. (This
process is explained in more detail in View Resolution.) As long as the set of hints that
exists on the underlying tables of the view are identical to each other, the query is
eligible to be matched with an indexed view. Most of the time, these hints will match
each other, because they are being inherited directly from the view. However, if the
query references tables instead of views, and the hints applied directly on these tables
are not identical, then such a query is not eligible for matching with an indexed view. If
the INDEX, PAGLOCK, ROWLOCK, TABLOCKX, UPDLOCK, or XLOCK hints apply to the tables
referenced in the query after view expansion, the query is not eligible for indexed view
matching.

If a table hint in the form of INDEX (index_val[ ,...n] ) references a view in a query and
you do not also specify the NOEXPAND hint, the index hint is ignored. To specify use of a
particular index, use NOEXPAND.

Generally, when the Query Optimizer matches an indexed view to a query, any hints
specified on the tables or views in the query are applied directly to the indexed view. If
the Query Optimizer chooses not to use an indexed view, any hints are propagated
directly to the tables referenced in the view. For more information, see View Resolution.
This propagation does not apply to join hints. They are applied only in their original
position in the query. Join hints are not considered by the Query Optimizer when
matching queries to indexed views. If a query plan uses an indexed view that matches
part of a query that contains a join hint, the join hint is not used in the plan.

Hints are not allowed in the definitions of indexed views. In compatibility mode 80 and
higher, SQL Server ignores hints inside indexed view definitions when maintaining them,
or when executing queries that use indexed views. Although using hints in indexed view
definitions will not produce a syntax error in 80 compatibility mode, they are ignored.

Resolving Distributed Partitioned Views

The SQL Server query processor optimizes the performance of distributed partitioned
views. The most important aspect of distributed partitioned view performance is
minimizing the amount of data transferred between member servers.

SQL Server builds intelligent, dynamic plans that make efficient use of distributed
queries to access data from remote member tables:
 The Query Processor first uses OLE DB to retrieve the check constraint definitions from
each member table. This allows the query processor to map the distribution of key values
across the member tables.
 The Query Processor compares the key ranges specified in an Transact-SQL
statement WHERE clause to the map that shows how the rows are distributed in the
member tables. The query processor then builds a query execution plan that uses
distributed queries to retrieve only those remote rows that are required to complete the
Transact-SQL statement. The execution plan is also built in such a way that any access to
remote member tables, for either data or metadata, are delayed until the information is
required.

For example, consider a system where a customers table is partitioned across Server1
(CustomerID from 1 through 3299999), Server2 (CustomerID from 3300000 through
6599999), and Server3 (CustomerID from 6600000 through 9999999).

Consider the execution plan built for this query executed on Server1:

SQLCopy
SELECT *
FROM CompanyData.dbo.Customers
WHERE CustomerID BETWEEN 3200000 AND 3400000;

The execution plan for this query extracts the rows with CustomerID key values from
3200000 through 3299999 from the local member table, and issues a distributed query
to retrieve the rows with key values from 3300000 through 3400000 from Server2.

The SQL Server Query Processor can also build dynamic logic into query execution plans
for Transact-SQL statements in which the key values are not known when the plan must
be built. For example, consider this stored procedure:

SQLCopy
CREATE PROCEDURE GetCustomer @CustomerIDParameter INT
AS
SELECT *
FROM CompanyData.dbo.Customers
WHERE CustomerID = @CustomerIDParameter;

SQL Server cannot predict what key value will be supplied by


the @CustomerIDParameter parameter every time the procedure is executed. Because the
key value cannot be predicted, the query processor also cannot predict which member
table will have to be accessed. To handle this case, SQL Server builds an execution plan
that has conditional logic, referred to as dynamic filters, to control which member table
is accessed, based on the input parameter value. Assuming the GetCustomer stored
procedure was executed on Server1, the execution plan logic can be represented as
shown in the following:

SQLCopy
IF @CustomerIDParameter BETWEEN 1 and 3299999
Retrieve row from local table CustomerData.dbo.Customer_33
ELSE IF @CustomerIDParameter BETWEEN 3300000 and 6599999
Retrieve row from linked table Server2.CustomerData.dbo.Customer_66
ELSE IF @CustomerIDParameter BETWEEN 6600000 and 9999999
Retrieve row from linked table Server3.CustomerData.dbo.Customer_99

SQL Server sometimes builds these types of dynamic execution plans even for queries
that are not parameterized. The Query Optimizer may parameterize a query so that the
execution plan can be reused. If the Query Optimizer parameterizes a query referencing
a partitioned view, the Query Optimizer can no longer assume the required rows will
come from a specified base table. It will then have to use dynamic filters in the
execution plan.

Stored Procedure and Trigger Execution


SQL Server stores only the source for stored procedures and triggers. When a stored
procedure or trigger is first executed, the source is compiled into an execution plan. If
the stored procedure or trigger is again executed before the execution plan is aged
from memory, the relational engine detects the existing plan and reuses it. If the plan
has aged out of memory, a new plan is built. This process is similar to the process SQL
Server follows for all Transact-SQL statements. The main performance advantage that
stored procedures and triggers have in SQL Server compared with batches of dynamic
Transact-SQL is that their Transact-SQL statements are always the same. Therefore, the
relational engine easily matches them with any existing execution plans. Stored
procedure and trigger plans are easily reused.

The execution plan for stored procedures and triggers is executed separately from the
execution plan for the batch calling the stored procedure or firing the trigger. This
allows for greater reuse of the stored procedure and trigger execution plans.

Execution Plan Caching and Reuse


SQL Server has a pool of memory that is used to store both execution plans and data
buffers. The percentage of the pool allocated to either execution plans or data buffers
fluctuates dynamically, depending on the state of the system. The part of the memory
pool that is used to store execution plans is referred to as the plan cache.
The plan cache has two stores for all compiled plans:

 The Object Plans cache store (OBJCP) used for plans related to persisted objects (stored
procedures, functions, and triggers).
 The SQL Plans cache store (SQLCP) used for plans related to autoparameterized, dynamic,
or prepared queries.

The query below provides information about memory usage for these two cache stores:

SQLCopy
SELECT * FROM sys.dm_os_memory_clerks
WHERE name LIKE '%plans%';
 Note

The plan cache has two additional stores that are not used for storing plans:

 The Bound Trees cache store (PHDR) used for data structures used during plan
compilation for views, constraints, and defaults. These structures are known as Bound
Trees or Algebrizer Trees.
 The Extended Stored Procedures cache store (XPROC) used for predefined system
procedures, like sp_executeSql or xp_cmdshell, that are defined using a DLL, not using
Transact-SQL statements. The cached structure contains only the function name and the
DLL name in which the procedure is implemented.

SQL Server execution plans have the following main components:

 Compiled Plan (or Query Plan)


The query plan produced by the compilation process is mostly a re-entrant, read-
only data structure used by any number of users. It stores information about:

o Physical operators which implement the operation described by logical


operators.

o The order of these operators, which determines the order in which data is
accessed, filtered, and aggregated.

o The number of estimated rows flowing through the operators.

 Note

In newer versions of the Database Engine, information about the statistics


objects that were used for Cardinality Estimation is also stored.
o What support objects must be created, such as worktables or workfiles in
tempdb. No user context or runtime information is stored in the query plan.
There are never more than one or two copies of the query plan in memory: one
copy for all serial executions and another for all parallel executions. The parallel
copy covers all parallel executions, regardless of their degree of parallelism.

 Execution Context
Each user that is currently executing the query has a data structure that holds the
data specific to their execution, such as parameter values. This data structure is
referred to as the execution context. The execution context data structures are
reused, but their content is not. If another user executes the same query, the data
structures are reinitialized with the context for the new user.

When any Transact-SQL statement is executed in SQL Server, the Database Engine first
looks through the plan cache to verify that an existing execution plan for the same
Transact-SQL statement exists. The Transact-SQL statement qualifies as existing if it
literally matches a previously executed Transact-SQL statement with a cached plan,
character per character. SQL Server reuses any existing plan it finds, saving the overhead
of recompiling the Transact-SQL statement. If no execution plan exists, SQL Server
generates a new execution plan for the query.

 Note

The execution plans for some Transact-SQL statements are not persisted in the plan
cache, such as bulk operation statements running on rowstore or statements containing
string literals larger than 8 KB in size. These plans only exist while the query is being
executed.

SQL Server has an efficient algorithm to find any existing execution plans for any specific
Transact-SQL statement. In most systems, the minimal resources that are used by this
scan are less than the resources that are saved by being able to reuse existing plans
instead of compiling every Transact-SQL statement.

The algorithms to match new Transact-SQL statements to existing, unused execution


plans in the plan cache require that all object references be fully qualified. For example,
assume that Person is the default schema for the user executing the
below SELECT statements. While in this example it is not required that the Person table is
fully qualified to execute, it means that the second statement is not matched with an
existing plan, but the third is matched:

SQLCopy
USE AdventureWorks2014;
GO
SELECT * FROM Person;
GO
SELECT * FROM Person.Person;
GO
SELECT * FROM Person.Person;
GO

Changing any of the following SET options for a given execution will affect the ability to
reuse plans, because the Database Engine performs constant folding and these options
affect the results of such expressions:

ANSI_NULL_DFLT_OFF

FORCEPLAN

ARITHABORT

DATEFIRST

ANSI_PADDING

NUMERIC_ROUNDABORT

ANSI_NULL_DFLT_ON

LANGUAGE

CONCAT_NULL_YIELDS_NULL

DATEFORMAT

ANSI_WARNINGS
QUOTED_IDENTIFIER

ANSI_NULLS

NO_BROWSETABLE

ANSI_DEFAULTS

Caching multiple plans for the same query

Queries and execution plans are uniquely identifiable in the Database Engine, much like
a fingerprint:

 The query plan hash is a binary hash value calculated on the execution plan for a given
query, and used to uniquely identify similar execution plans.
 The query hash is a binary hash value calculated on the Transact-SQL text of a query, and
is used to uniquely identify queries.

A compiled plan can be retrieved from the plan cache using a Plan Handle, which is a
transient identifier that remains constant only while the plan remains in the cache. The
plan handle is a hash value derived from the compiled plan of the entire batch. The plan
handle for a compiled plan remains the same even if one or more statements in the
batch get recompiled.

 Note

If a plan was compiled for a batch instead of a single statement, the plan for individual
statements in the batch can be retrieved using the plan handle and statement offsets.
The sys.dm_exec_requests DMV contains
the statement_start_offset and statement_end_offset columns for each record, which
refer to the currently executing statement of a currently executing batch or persisted
object. For more information, see sys.dm_exec_requests (Transact-SQL).
The sys.dm_exec_query_stats DMV also contains these columns for each record, which
refer to the position of a statement within a batch or persisted object. For more
information, see sys.dm_exec_query_stats (Transact-SQL).

The actual Transact-SQL text of a batch is stored in a separate memory space from the
plan cache, called the SQL Manager cache (SQLMGR). The Transact-SQL text for a
compiled plan can be retrieved from the sql manager cache using a SQL Handle, which
is a transient identifier that remains constant only while at least one plan that references
it remains in the plan cache. The sql handle is a hash value derived from the entire batch
text and is guaranteed to be unique for every batch.
 Note

Like a compiled plan, the Transact-SQL text is stored per batch, including the comments.
The sql handle contains the MD5 hash of the entire batch text and is guaranteed to be
unique for every batch.

The query below provides information about memory usage for the sql manager cache:

SQLCopy
SELECT * FROM sys.dm_os_memory_objects
WHERE type = 'MEMOBJ_SQLMGR';

There is a 1:N relation between a sql handle and plan handles. Such a condition occurs
when the cache key for the compiled plans is different. This may occur due to change in
SET options between two executions of the same batch.

Consider the following stored procedure:

SQLCopy
USE WideWorldImporters;
GO
CREATE PROCEDURE usp_SalesByCustomer @CID int
AS
SELECT * FROM Sales.Customers
WHERE CustomerID = @CID
GO

SET ANSI_DEFAULTS ON
GO

EXEC usp_SalesByCustomer 10
GO

Verify what can be found in the plan cache using the query below:

SQLCopy
SELECT cp.memory_object_address, cp.objtype, refcounts, usecounts,
qs.query_plan_hash, qs.query_hash,
qs.plan_handle, qs.sql_handle
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_sql_text (cp.plan_handle)
CROSS APPLY sys.dm_exec_query_plan (cp.plan_handle)
INNER JOIN sys.dm_exec_query_stats AS qs ON qs.plan_handle = cp.plan_handle
WHERE text LIKE '%usp_SalesByCustomer%'
GO
Here is the result set.

Copy
memory_object_address objtype refcounts usecounts query_plan_hash
query_hash
--------------------- ------- --------- --------- ------------------
------------------
0x000001CC6C534060 Proc 2 1 0x3B4303441A1D7E6D
0xA05D5197DA1EAC2D

plan_handle
-------------------------------------------------------------------------------------
-----
0x0500130095555D02D022F111CD010000010000000000000000000000000000000000000000000000000
00000

sql_handle
-------------------------------------------------------------------------------------
-----
0x0300130095555D02C864C10061AB0000010000000000000000000000000000000000000000000000000
00000

Now execute the stored procedure with a different parameter, but no other changes to
execution context:

SQLCopy
EXEC usp_SalesByCustomer 8
GO

Verify again what can be found in the plan cache. Here is the result set.

Copy
memory_object_address objtype refcounts usecounts query_plan_hash
query_hash
--------------------- ------- --------- --------- ------------------
------------------
0x000001CC6C534060 Proc 2 2 0x3B4303441A1D7E6D
0xA05D5197DA1EAC2D

plan_handle
-------------------------------------------------------------------------------------
-----
0x0500130095555D02D022F111CD010000010000000000000000000000000000000000000000000000000
00000

sql_handle
-------------------------------------------------------------------------------------
-----
0x0300130095555D02C864C10061AB0000010000000000000000000000000000000000000000000000000
00000

Notice the usecounts has increased to 2, which means the same cached plan was re-used
as-is, because the execution context data structures were reused. Now change the SET
ANSI_DEFAULTS option and execute the stored procedure using the same parameter.

SQLCopy
SET ANSI_DEFAULTS OFF
GO

EXEC usp_SalesByCustomer 8
GO

Verify again what can be found in the plan cache. Here is the result set.

Copy
memory_object_address objtype refcounts usecounts query_plan_hash
query_hash
--------------------- ------- --------- --------- ------------------
------------------
0x000001CD01DEC060 Proc 2 1 0x3B4303441A1D7E6D
0xA05D5197DA1EAC2D
0x000001CC6C534060 Proc 2 2 0x3B4303441A1D7E6D
0xA05D5197DA1EAC2D

plan_handle
-------------------------------------------------------------------------------------
-----
0x0500130095555D02B031F111CD010000010000000000000000000000000000000000000000000000000
00000
0x0500130095555D02D022F111CD010000010000000000000000000000000000000000000000000000000
00000

sql_handle
-------------------------------------------------------------------------------------
-----
0x0300130095555D02C864C10061AB0000010000000000000000000000000000000000000000000000000
00000
0x0300130095555D02C864C10061AB0000010000000000000000000000000000000000000000000000000
00000

Notice there are now two entries in the sys.dm_exec_cached_plans DMV output:

 The usecounts column shows the value 1 in the first record which is the plan executed
once with SET ANSI_DEFAULTS OFF.
 The usecounts column shows the value 2 in the second record which is the plan executed
with SET ANSI_DEFAULTS ON, because it was executed twice.
 The different memory_object_address refers to a different execution plan entry in the plan
cache. However, the sql_handle value is the same for both entries because they refer to
the same batch.
o The execution with ANSI_DEFAULTS set to OFF has a new plan_handle, and it's available
for reuse for calls that have the same set of SET options. The new plan handle is
necessary because the execution context was reinitialized due to changed SET options.
But that doesn't trigger a recompile: both entries refer to the same plan and query, as
evidenced by the same query_plan_hash and query_hash values.

What this effectively means is that we have two plan entries in the cache corresponding
to the same batch, and it underscores the importance of making sure that the plan
cache affecting SET options are the same, when the same queries are executed
repeatedly, to optimize for plan reuse and keep plan cache size to its required minimum.

 Tip

A common pitfall is that different clients may have different default values for the SET
options. For example, a connection made through SQL Server Management Studio
automatically sets QUOTED_IDENTIFIER to ON, while SQLCMD sets QUOTED_IDENTIFIER to
OFF. Executing the same queries from these two clients will result in multiple plans (as
described in the example above).

Removing execution plans from the Plan Cache

Execution plans remain in the plan cache as long as there is enough memory to store
them. When memory pressure exists, the SQL Server Database Engine uses a cost-based
approach to determine which execution plans to remove from the plan cache. To make
a cost-based decision, the SQL Server Database Engine increases and decreases a
current cost variable for each execution plan according to the following factors.

When a user process inserts an execution plan into the cache, the user process sets the
current cost equal to the original query compile cost; for ad-hoc execution plans, the
user process sets the current cost to zero. Thereafter, each time a user process
references an execution plan, it resets the current cost to the original compile cost; for
ad-hoc execution plans the user process increases the current cost. For all plans, the
maximum value for the current cost is the original compile cost.

When memory pressure exists, the SQL Server Database Engine responds by removing
execution plans from the plan cache. To determine which plans to remove, the SQL
Server Database Engine repeatedly examines the state of each execution plan and
removes plans when their current cost is zero. An execution plan with zero current cost
is not removed automatically when memory pressure exists; it is removed only when the
SQL Server Database Engine examines the plan and the current cost is zero. When
examining an execution plan, the SQL Server Database Engine pushes the current cost
towards zero by decreasing the current cost if a query is not currently using the plan.

The SQL Server Database Engine repeatedly examines the execution plans until enough
have been removed to satisfy memory requirements. While memory pressure exists, an
execution plan may have its cost increased and decreased more than once. When
memory pressure no longer exists, the SQL Server Database Engine stops decreasing the
current cost of unused execution plans and all execution plans remain in the plan cache,
even if their cost is zero.

The SQL Server Database Engine uses the resource monitor and user worker threads to
free memory from the plan cache in response to memory pressure. The resource
monitor and user worker threads can examine plans run concurrently to decrease the
current cost for each unused execution plan. The resource monitor removes execution
plans from the plan cache when global memory pressure exists. It frees memory to
enforce policies for system memory, process memory, resource pool memory, and
maximum size for all caches.

The maximum size for all caches is a function of the buffer pool size and cannot exceed
the maximum server memory. For more information on configuring the maximum server
memory, see the max server memory setting in sp_configure.

The user worker threads remove execution plans from the plan cache when single cache
memory pressure exists. They enforce policies for maximum single cache size and
maximum single cache entries.

The following examples illustrate which execution plans get removed from the plan
cache:

 An execution plan is frequently referenced so that its cost never goes to zero. The plan
remains in the plan cache and is not removed unless there is memory pressure and the
current cost is zero.
 An ad-hoc execution plan is inserted and is not referenced again before memory pressure
exists. Since ad-hoc plans are initialized with a current cost of zero, when the SQL Server
Database Engine examines the execution plan, it will see the zero current cost and remove
the plan from the plan cache. The ad-hoc execution plan remains in the plan cache with a
zero current cost when memory pressure does not exist.

To manually remove a single plan or all plans from the cache, use DBCC
FREEPROCCACHE. DBCC FREESYSTEMCACHE can also be used to clear any cache,
including plan cache. Starting with SQL Server 2016 (13.x), the ALTER DATABASE SCOPED
CONFIGURATION CLEAR PROCEDURE_CACHE  to clear the procedure (plan) cache for the
database in scope. A change in some configuration settings
via sp_configure and reconfigure will also cause plans to be removed from plan cache.
You can find the list of these configuration settings in the Remarks section of the DBCC
FREEPROCCACHE article. A configuration change like this will log the following
informational message in the error log:

SQL Server has encountered %d occurrence(s) of cachestore flush for the '%s'
cachestore (part of plan cache) due to some database maintenance or reconfigure
operations.

Recompiling Execution Plans

Certain changes in a database can cause an execution plan to be either inefficient or


invalid, based on the new state of the database. SQL Server detects the changes that
invalidate an execution plan and marks the plan as not valid. A new plan must then be
recompiled for the next connection that executes the query. The conditions that
invalidate a plan include the following:

 Changes made to a table or view referenced by the query (ALTER TABLE and ALTER VIEW).
 Changes made to a single procedure, which would drop all plans for that procedure from
the cache (ALTER PROCEDURE).
 Changes to any indexes used by the execution plan.
 Updates on statistics used by the execution plan, generated either explicitly from a
statement, such as UPDATE STATISTICS, or generated automatically.
 Dropping an index used by the execution plan.
 An explicit call to sp_recompile.
 Large numbers of changes to keys (generated by INSERT or DELETE statements from other
users that modify a table referenced by the query).
 For tables with triggers, if the number of rows in the inserted or deleted tables grows
significantly.
 Executing a stored procedure using the WITH RECOMPILE option.

Most recompilations are required either for statement correctness or to obtain


potentially faster query execution plans.

In SQL Server versions prior to 2005, whenever a statement within a batch causes
recompilation, the entire batch, whether submitted through a stored procedure, trigger,
ad-hoc batch, or prepared statement, was recompiled. Starting with SQL Server 2005
(9.x), only the statement inside the batch that triggers recompilation is recompiled. Also,
there are additional types of recompilations in SQL Server 2005 (9.x) and later because
of its expanded feature set.
Statement-level recompilation benefits performance because, in most cases, a small
number of statements causes recompilations and their associated penalties, in terms of
CPU time and locks. These penalties are therefore avoided for the other statements in
the batch that do not have to be recompiled.

The sql_statement_recompile extended event (xEvent) reports statement-level


recompilations. This xEvent occurs when a statement-level recompilation is required by
any kind of batch. This includes stored procedures, triggers, ad hoc batches and queries.
Batches may be submitted through several interfaces, including sp_executesql, dynamic
SQL, Prepare methods or Execute methods. The recompile_cause column
of sql_statement_recompile xEvent contains an integer code that indicates the reason for
the recompilation. The following table contains the possible reasons:

Schema changed

Statistics changed

Deferred compile

SET option changed

Temporary table changed

Remote rowset changed

FOR BROWSE permission changed

Query notification environment changed

Partitioned view changed

Cursor options changed

OPTION (RECOMPILE) requested

Parameterized plan flushed

Plan affecting database version changed

Query Store plan forcing policy changed

Query Store plan forcing failed

Query Store missing the plan


 Note

In SQL Server versions where xEvents are not available, then the SQL Server
Profiler SP:Recompile trace event can be used for the same purpose of reporting
statement-level recompilations. The trace event SQL:StmtRecompile also reports
statement-level recompilations, and this trace event can also be used to track and
debug recompilations. Whereas SP:Recompile generates only for stored procedures and
triggers, SQL:StmtRecompile generates for stored procedures, triggers, ad-hoc batches,
batches that are executed by using sp_executesql, prepared queries, and dynamic SQL.
The EventSubClass column of SP:Recompile and SQL:StmtRecompile contains an integer
code that indicates the reason for the recompilation. The codes are described here.

 Note

When the AUTO_UPDATE_STATISTICS database option is set to ON, queries are recompiled


when they target tables or indexed views whose statistics have been updated or whose
cardinalities have changed significantly since the last execution. This behavior applies to
standard user-defined tables, temporary tables, and the inserted and deleted tables
created by DML triggers. If query performance is affected by excessive recompilations,
consider changing this setting to OFF. When the AUTO_UPDATE_STATISTICS database option
is set to OFF, no recompilations occur based on statistics or cardinality changes, with the
exception of the inserted and deleted tables that are created by DML INSTEAD
OF triggers. Because these tables are created in tempdb, the recompilation of queries
that access them depends on the setting of AUTO_UPDATE_STATISTICS in tempdb. Note that
in SQL Server prior to 2005, queries continue to recompile based on cardinality changes
to the DML trigger inserted and deleted tables, even when this setting is OFF.

Parameters and Execution Plan Reuse

The use of parameters, including parameter markers in ADO, OLE DB, and ODBC
applications, can increase the reuse of execution plans.

 Warning

Using parameters or parameter markers to hold values that are typed by end users is
more secure than concatenating the values into a string that is then executed by using
either a data access API method, the EXECUTE statement, or the sp_executesql stored
procedure.

The only difference between the following two SELECT statements is the values that are
compared in the WHERE clause:
SQLCopy
SELECT *
FROM AdventureWorks2014.Production.Product
WHERE ProductSubcategoryID = 1;
SQLCopy
SELECT *
FROM AdventureWorks2014.Production.Product
WHERE ProductSubcategoryID = 4;

The only difference between the execution plans for these queries is the value stored for
the comparison against the ProductSubcategoryID column. While the goal is for SQL
Server to always recognize that the statements generate essentially the same plan and
reuse the plans, SQL Server sometimes does not detect this in complex Transact-SQL
statements.

Separating constants from the Transact-SQL statement by using parameters helps the
relational engine recognize duplicate plans. You can use parameters in the following
ways:

 In Transact-SQL , use sp_executesql:

SQLCopy
DECLARE @MyIntParm INT
SET @MyIntParm = 1
EXEC sp_executesql
N'SELECT *
FROM AdventureWorks2014.Production.Product
WHERE ProductSubcategoryID = @Parm',
N'@Parm INT',
@MyIntParm

This method is recommended for Transact-SQL scripts, stored procedures, or


triggers that generate SQL statements dynamically.

 ADO, OLE DB, and ODBC use parameter markers. Parameter markers are question
marks (?) that replace a constant in an SQL statement and are bound to a program
variable. For example, you would do the following in an ODBC application:

o Use SQLBindParameter to bind an integer variable to the first parameter marker in an


SQL statement.
o Put the integer value in the variable.
o Execute the statement, specifying the parameter marker (?):
Copy
SQLExecDirect(hstmt,
"SELECT *
FROM AdventureWorks2014.Production.Product
WHERE ProductSubcategoryID = ?",
SQL_NTS);

The SQL Server Native Client OLE DB Provider and the SQL Server Native Client
ODBC driver included with SQL Server use sp_executesql to send statements to
SQL Server when parameter markers are used in applications.

 To design stored procedures, which use parameters by design.

If you do not explicitly build parameters into the design of your applications, you can
also rely on the SQL Server Query Optimizer to automatically parameterize certain
queries by using the default behavior of simple parameterization. Alternatively, you can
force the Query Optimizer to consider parameterizing all queries in the database by
setting the PARAMETERIZATION option of the ALTER DATABASE statement to FORCED.

When forced parameterization is enabled, simple parameterization can still occur. For
example, the following query cannot be parameterized according to the rules of forced
parameterization:

SQLCopy
SELECT * FROM Person.Address
WHERE AddressID = 1 + 2;

However, it can be parameterized according to simple parameterization rules. When


forced parameterization is tried but fails, simple parameterization is still subsequently
tried.

Simple Parameterization

In SQL Server, using parameters or parameter markers in Transact-SQL statements


increases the ability of the relational engine to match new Transact-SQL statements with
existing, previously-compiled execution plans.

 Warning

Using parameters or parameter markers to hold values typed by end users is more
secure than concatenating the values into a string that is then executed using either a
data access API method, the EXECUTE statement, or the sp_executesql stored procedure.
If a Transact-SQL statement is executed without parameters, SQL Server parameterizes
the statement internally to increase the possibility of matching it against an existing
execution plan. This process is called simple parameterization. In SQL Server versions
prior to 2005, the process was referred to as auto-parameterization.

Consider this statement:

SQLCopy
SELECT * FROM AdventureWorks2014.Production.Product
WHERE ProductSubcategoryID = 1;

The value 1 at the end of the statement can be specified as a parameter. The relational
engine builds the execution plan for this batch as if a parameter had been specified in
place of the value 1. Because of this simple parameterization, SQL Server recognizes that
the following two statements generate essentially the same execution plan and reuses
the first plan for the second statement:

SQLCopy
SELECT * FROM AdventureWorks2014.Production.Product
WHERE ProductSubcategoryID = 1;
SQLCopy
SELECT * FROM AdventureWorks2014.Production.Product
WHERE ProductSubcategoryID = 4;

When processing complex Transact-SQL statements, the relational engine may have
difficulty determining which expressions can be parameterized. To increase the ability of
the relational engine to match complex Transact-SQL statements to existing, unused
execution plans, explicitly specify the parameters using either sp_executesql or
parameter markers.

 Note

When the +, -, *, /, or % arithmetic operators are used to perform implicit or explicit


conversion of int, smallint, tinyint, or bigint constant values to the float, real, decimal or
numeric data types, SQL Server applies specific rules to calculate the type and precision
of the expression results. However, these rules differ, depending on whether the query is
parameterized or not. Therefore, similar expressions in queries can, in some cases,
produce differing results.

Under the default behavior of simple parameterization, SQL Server parameterizes a


relatively small class of queries. However, you can specify that all queries in a database
be parameterized, subject to certain limitations, by setting the PARAMETERIZATION option
of the ALTER DATABASE command to FORCED. Doing so may improve the performance of
databases that experience high volumes of concurrent queries by reducing the
frequency of query compilations.

Alternatively, you can specify that a single query, and any others that are syntactically
equivalent but differ only in their parameter values, be parameterized.

 Tip

When using an Object-Relational Mapping (ORM) solution such as Entity Framework


(EF), application queries like manual LINQ query trees or certain raw SQL queries may
not be parameterized, which impacts plan re-use and the ability to track queries in the
Query Store. For more information, see EF Query caching and
parameterization and EF Raw SQL Queries.

Forced Parameterization

You can override the default simple parameterization behavior of SQL Server by
specifying that all SELECT, INSERT, UPDATE, and DELETE statements in a database be
parameterized, subject to certain limitations. Forced parameterization is enabled by
setting the PARAMETERIZATION option to FORCED in the ALTER DATABASE statement. Forced
parameterization may improve the performance of certain databases by reducing the
frequency of query compilations and recompilations. Databases that may benefit from
forced parameterization are generally those that experience high volumes of concurrent
queries from sources such as point-of-sale applications.

When the PARAMETERIZATION option is set to FORCED, any literal value that appears in


a SELECT, INSERT, UPDATE, or DELETE statement, submitted in any form, is converted to a
parameter during query compilation. The exceptions are literals that appear in the
following query constructs:

 INSERT...EXECUTE statements.
 Statements inside the bodies of stored procedures, triggers, or user-defined functions.
SQL Server already reuses query plans for these routines.
 Prepared statements that have already been parameterized on the client-side application.
 Statements that contain XQuery method calls, where the method appears in a context
where its arguments would typically be parameterized, such as a WHERE clause. If the
method appears in a context where its arguments would not be parameterized, the rest of
the statement is parameterized.
 Statements inside a Transact-SQL cursor. (SELECT statements inside API cursors are
parameterized.)
 Deprecated query constructs.
 Any statement that is run in the context of ANSI_PADDING or ANSI_NULLS set to OFF.
 Statements that contain more than 2,097 literals that are eligible for parameterization.
 Statements that reference variables, such as WHERE T.col2 >= @bb.
 Statements that contain the RECOMPILE query hint.
 Statements that contain a COMPUTE clause.
 Statements that contain a WHERE CURRENT OF clause.

Additionally, the following query clauses are not parameterized. Note that in these
cases, only the clauses are not parameterized. Other clauses within the same query may
be eligible for forced parameterization.

 The <select_list> of any SELECT statement. This includes SELECT lists of subqueries


and SELECT lists inside INSERT statements.
 Subquery SELECT statements that appear inside an IF statement.
 The TOP, TABLESAMPLE, HAVING, GROUP BY, ORDER BY, OUTPUT...INTO, or FOR XML clauses of a
query.
 Arguments, either direct or as subexpressions,
to OPENROWSET, OPENQUERY, OPENDATASOURCE, OPENXML, or any FULLTEXT operator.
 The pattern and escape_character arguments of a LIKE clause.
 The style argument of a CONVERT clause.
 Integer constants inside an IDENTITY clause.
 Constants specified by using ODBC extension syntax.
 Constant-foldable expressions that are arguments of the +, -, *, /, and % operators. When
considering eligibility for forced parameterization, SQL Server considers an expression to
be constant-foldable when either of the following conditions is true:
o No columns, variables, or subqueries appear in the expression.
o The expression contains a CASE clause.
 Arguments to query hint clauses. These include the number_of_rows argument of
the FAST query hint, the number_of_processors argument of the MAXDOP query hint, and
the number argument of the MAXRECURSION query hint.

Parameterization occurs at the level of individual Transact-SQL statements. In other


words, individual statements in a batch are parameterized. After compiling, a
parameterized query is executed in the context of the batch in which it was originally
submitted. If an execution plan for a query is cached, you can determine whether the
query was parameterized by referencing the sql column of the sys.syscacheobjects
dynamic management view. If a query is parameterized, the names and data types of
parameters come before the text of the submitted batch in this column, such as (@1
tinyint).

 Note
Parameter names are arbitrary. Users or applications should not rely on a particular
naming order. Also, the following can change between versions of SQL Server and
Service Pack upgrades: Parameter names, the choice of literals that are parameterized,
and the spacing in the parameterized text.

Data Types of Parameters

When SQL Server parameterizes literals, the parameters are converted to the following
data types:

 Integer literals whose size would otherwise fit within the int data type parameterize to int.
Larger integer literals that are parts of predicates that involve any comparison operator
(includes <, <=, =, !=, >, >=, , !<, !>, <>, ALL, ANY, SOME, BETWEEN, and IN) parameterize to
numeric(38,0). Larger literals that are not parts of predicates that involve comparison
operators parameterize to numeric whose precision is just large enough to support its size
and whose scale is 0.
 Fixed-point numeric literals that are parts of predicates that involve comparison operators
parameterize to numeric whose precision is 38 and whose scale is just large enough to
support its size. Fixed-point numeric literals that are not parts of predicates that involve
comparison operators parameterize to numeric whose precision and scale are just large
enough to support its size.
 Floating point numeric literals parameterize to float(53).
 Non-Unicode string literals parameterize to varchar(8000) if the literal fits within 8,000
characters, and to varchar(max) if it is larger than 8,000 characters.
 Unicode string literals parameterize to nvarchar(4000) if the literal fits within 4,000
Unicode characters, and to nvarchar(max) if the literal is larger than 4,000 characters.
 Binary literals parameterize to varbinary(8000) if the literal fits within 8,000 bytes. If it is
larger than 8,000 bytes, it is converted to varbinary(max).
 Money type literals parameterize to money.
Guidelines for Using Forced Parameterization

Consider the following when you set the PARAMETERIZATION option to FORCED:

 Forced parameterization, in effect, changes the literal constants in a query to parameters


when compiling a query. Therefore, the Query Optimizer might choose suboptimal plans
for queries. In particular, the Query Optimizer is less likely to match the query to an
indexed view or an index on a computed column. It may also choose suboptimal plans for
queries posed on partitioned tables and distributed partitioned views. Forced
parameterization should not be used for environments that rely heavily on indexed views
and indexes on computed columns. Generally, the PARAMETERIZATION FORCED option
should only be used by experienced database administrators after determining that doing
this does not adversely affect performance.
 Distributed queries that reference more than one database are eligible for forced
parameterization as long as the PARAMETERIZATION option is set to FORCED in the database
whose context the query is running.
 Setting the PARAMETERIZATION option to FORCED flushes all query plans from the plan cache
of a database, except those that currently are compiling, recompiling, or running. Plans for
queries that are compiling or running during the setting change are parameterized the
next time the query is executed.
 Setting the PARAMETERIZATION option is an online operation that it requires no database-
level exclusive locks.
 The current setting of the PARAMETERIZATION option is preserved when reattaching or
restoring a database.

You can override the behavior of forced parameterization by specifying that simple
parameterization be attempted on a single query, and any others that are syntactically
equivalent but differ only in their parameter values. Conversely, you can specify that
forced parameterization be attempted on only a set of syntactically equivalent queries,
even if forced parameterization is disabled in the database. Plan guides are used for this
purpose.

 Note

When the PARAMETERIZATION option is set to FORCED, the reporting of error messages may


differ from when the PARAMETERIZATION option is set to SIMPLE: multiple error messages
may be reported under forced parameterization, where fewer messages would be
reported under simple parameterization, and the line numbers in which errors occur
may be reported incorrectly.

Preparing SQL Statements

The SQL Server relational engine introduces full support for preparing Transact-SQL
statements before they are executed. If an application has to execute an Transact-SQL
statement several times, it can use the database API to do the following:

 Prepare the statement once. This compiles the Transact-SQL statement into an execution
plan.
 Execute the precompiled execution plan every time it has to execute the statement. This
prevents having to recompile the Transact-SQL statement on each execution after the first
time.
Preparing and executing statements is controlled by API functions and methods. It is not
part of the Transact-SQL language. The prepare/execute model of executing Transact-SQL
statements is supported by the SQL Server Native Client OLE DB Provider and the SQL
Server Native Client ODBC driver. On a prepare request, either the provider or the driver
sends the statement to SQL Server with a request to prepare the statement. SQL Server
compiles an execution plan and returns a handle for that plan to the provider or driver. On
an execute request, either the provider or the driver sends the server a request to execute
the plan that is associated with the handle.

Prepared statements cannot be used to create temporary objects on SQL Server.


Prepared statements cannot reference system stored procedures that create temporary
objects, such as temporary tables. These procedures must be executed directly.

Excess use of the prepare/execute model can degrade performance. If a statement is


executed only once, a direct execution requires only one network round-trip to the
server. Preparing and executing an Transact-SQL statement executed only one time
requires an extra network round-trip; one trip to prepare the statement and one trip to
execute it.

Preparing a statement is more effective if parameter markers are used. For example,
assume that an application is occasionally asked to retrieve product information from
the AdventureWorks sample database. There are two ways the application can do this.

Using the first way, the application can execute a separate query for each product
requested:

SQLCopy
SELECT * FROM AdventureWorks2014.Production.Product
WHERE ProductID = 63;

Using the second way, the application does the following:

1. Prepares a statement that contains a parameter marker (?):


SQLCopy
SELECT * FROM AdventureWorks2014.Production.Product
WHERE ProductID = ?;
2. Binds a program variable to the parameter marker.
3. Each time product information is needed, fills the bound variable with the key value and
executes the statement.

The second way is more efficient when the statement is executed more than three times.

In SQL Server, the prepare/execute model has no significant performance advantage


over direct execution, because of the way SQL Server reuses execution plans. SQL Server
has efficient algorithms for matching current Transact-SQL statements with execution
plans that are generated for prior executions of the same Transact-SQL statement. If an
application executes a Transact-SQL statement with parameter markers multiple times,
SQL Server will reuse the execution plan from the first execution for the second and
subsequent executions (unless the plan ages from the plan cache). The prepare/execute
model still has these benefits:

 Finding an execution plan by an identifying handle is more efficient than the algorithms
used to match an Transact-SQL statement to existing execution plans.
 The application can control when the execution plan is created and when it is reused.
 The prepare/execute model is portable to other databases, including earlier versions of
SQL Server.
Parameter Sensitivity

Parameter sensitivity, also known as "parameter sniffing", refers to a process whereby


SQL Server "sniffs" the current parameter values during compilation or recompilation,
and passes it along to the Query Optimizer so that they can be used to generate
potentially more efficient query execution plans.

Parameter values are sniffed during compilation or recompilation for the following types
of batches:

 Stored procedures
 Queries submitted via sp_executesql
 Prepared queries

For more information on troubleshooting bad parameter sniffing issues,


see Troubleshoot queries with parameter-sensitive query execution plan issues.

 Note

For queries using the RECOMPILE hint, both parameter values and current values of local
variables are sniffed. The values sniffed (of parameters and local variables) are those that
exist at the place in the batch just before the statement with the RECOMPILE hint. In
particular, for parameters, the values that came along with the batch invocation call are
not sniffed.

Parallel Query Processing


SQL Server provides parallel queries to optimize query execution and index operations
for computers that have more than one microprocessor (CPU). Because SQL Server can
perform a query or index operation in parallel by using several operating system worker
threads, the operation can be completed quickly and efficiently.
During query optimization, SQL Server looks for queries or index operations that might
benefit from parallel execution. For these queries, SQL Server inserts exchange operators
into the query execution plan to prepare the query for parallel execution. An exchange
operator is an operator in a query execution plan that provides process management,
data redistribution, and flow control. The exchange operator includes the Distribute
Streams, Repartition Streams, and Gather Streams logical operators as subtypes, one or
more of which can appear in the Showplan output of a query plan for a parallel query.

 Important

Certain constructs inhibit SQL Server's ability to leverage parallelism on the entire
execution plan, or parts or the execution plan.

Constructs that inhibit parallelism include:

 Scalar UDFs
For more information on scalar user-defined functions, see Create User-defined
Functions. Starting with SQL Server 2019 (15.x), the SQL Server Database Engine
has the ability to inline these functions, and unlock use of parallelism during query
processing. For more information on scalar UDF inlining, see Intelligent query
processing in SQL databases.

 Remote Query
For more information on Remote Query, see Showplan Logical and Physical
Operators Reference.

 Dynamic cursors
For more information on cursors, see DECLARE CURSOR.

 Recursive queries
For more information on recursion, see Guidelines for Defining and Using
Recursive Common Table Expressions and Recursion in T-SQL.

 Multi-statement table-valued functions (MSTVFs)


For more information on MSTVFs, see Create User-defined Functions (Database
Engine).

 TOP keyword
For more information, see TOP (Transact-SQL).
A query execution plan may contain the NonParallelPlanReason attribute in
the QueryPlan element which describes why parallelism was not used. Values for this
attribute include:

TABLE 1

NonParallelPlanReason Value Description

MaxDOPSetToOne Maximum degree of parallelism set to


1.

EstimatedDOPIsOne Estimated degree of parallelism is 1.

NoParallelWithRemoteQuery Parallelism is not supported for remote


queries.

NoParallelDynamicCursor Parallel plans not supported for


dynamic cursors.

NoParallelFastForwardCursor Parallel plans not supported for fast


forward cursors.

NoParallelCursorFetchByBookmark Parallel plans not supported for cursors


that fetch by bookmark.

NoParallelCreateIndexInNonEnterpriseEdition Parallel index creation not supported


for non-Enterprise edition.

NoParallelPlansInDesktopOrExpressEdition Parallel plans not supported for


Desktop and Express edition.

NonParallelizableIntrinsicFunction Query is referencing a non-


parallelizable intrinsic function.

CLRUserDefinedFunctionRequiresDataAccess Parallelism not supported for a CLR


UDF that requires data access.

TSQLUserDefinedFunctionsNotParallelizable Query is referencing a T-SQL User


Defined Function that was not
parallelizable.

TableVariableTransactionsDoNotSupportParallelNestedTransaction Table variable transactions do not


support parallel nested transactions.

DMLQueryReturnsOutputToClient DML query returns output to client and


TABLE 1

NonParallelPlanReason Value Description

is not parallelizable.

MixedSerialAndParallelOnlineIndexBuildNotSupported Unsupported mix of serial and parallel


plans for a single online index build.

CouldNotGenerateValidParallelPlan Verifying parallel plan failed, failing


back to serial.

NoParallelForMemoryOptimizedTables Parallelism not supported for


referenced In-Memory OLTP tables.

NoParallelForDmlOnMemoryOptimizedTable Parallelism not supported for DML on


an In-Memory OLTP table.

NoParallelForNativelyCompiledModule Parallelism not supported for


referenced natively compiled modules.

NoRangesResumableCreate Range generation failed for a


resumable create operation.

After exchange operators are inserted, the result is a parallel-query execution plan. A
parallel-query execution plan can use more than one worker thread. A serial execution
plan, used by a non-parallel (serial) query, uses only one worker thread for its execution.
The actual number of worker threads used by a parallel query is determined at query
plan execution initialization and is determined by the complexity of the plan and the
degree of parallelism.

Degree of parallelism (DOP) determines the maximum number of CPUs that are being
used; it does not mean the number of worker threads that are being used. The DOP limit
is set per task. It is not a per request or per query limit. This means that during a parallel
query execution, a single request can spawn multiple tasks which are assigned to
a scheduler. More processors than specified by the MAXDOP may be used concurrently
at any given point of query execution, when different tasks are executed concurrently.
For more information, see the Thread and Task Architecture Guide.

The SQL Server Query Optimizer does not use a parallel execution plan for a query if any
one of the following conditions is true:
 The serial execution cost of the query is not high enough to consider an alternative,
parallel execution plan.
 A serial execution plan is considered faster than any possible parallel execution plan for
the particular query.
 The query contains scalar or relational operators that cannot be run in parallel. Certain
operators can cause a section of the query plan to run in serial mode, or the whole plan to
run in serial mode.
Degree of Parallelism

SQL Server automatically detects the best degree of parallelism for each instance of a
parallel query execution or index data definition language (DDL) operation. It does this
based on the following criteria:

1. Whether SQL Server is running on a computer that has more than one
microprocessor or CPU, such as a symmetric multiprocessing computer (SMP).
Only computers that have more than one CPU can use parallel queries.

2. Whether sufficient worker threads are available. Each query or index operation


requires a certain number of worker threads to execute. Executing a parallel plan
requires more worker threads than a serial plan, and the number of required
worker threads increases with the degree of parallelism. When the worker thread
requirement of the parallel plan for a specific degree of parallelism cannot be
satisfied, the SQL Server Database Engine decreases the degree of parallelism
automatically or completely abandons the parallel plan in the specified workload
context. It then executes the serial plan (one worker thread).

3. The type of query or index operation executed. Index operations that create or


rebuild an index, or drop a clustered index and queries that use CPU cycles heavily
are the best candidates for a parallel plan. For example, joins of large tables, large
aggregations, and sorting of large result sets are good candidates. Simple queries,
frequently found in transaction processing applications, find the additional
coordination required to execute a query in parallel outweigh the potential
performance boost. To distinguish between queries that benefit from parallelism
and those that do not benefit, the SQL Server Database Engine compares the
estimated cost of executing the query or index operation with the cost threshold
for parallelism value. Users can change the default value of 5 using sp_configure if
proper testing found that a different value is better suited for the running
workload.

4. Whether there are a sufficient number of rows to process. If the Query


Optimizer determines that the number of rows is too low, it does not introduce
exchange operators to distribute the rows. Consequently, the operators are
executed serially. Executing the operators in a serial plan avoids scenarios when
the startup, distribution, and coordination costs exceed the gains achieved by
parallel operator execution.

5. Whether current distribution statistics are available. If the highest degree of


parallelism is not possible, lower degrees are considered before the parallel plan is
abandoned. For example, when you create a clustered index on a view, distribution
statistics cannot be evaluated, because the clustered index does not yet exist. In
this case, the SQL Server Database Engine cannot provide the highest degree of
parallelism for the index operation. However, some operators, such as sorting and
scanning, can still benefit from parallel execution.

 Note

Parallel index operations are only available in SQL Server Enterprise, Developer, and
Evaluation editions.

At execution time, the SQL Server Database Engine determines whether the current
system workload and configuration information previously described allow for parallel
execution. If parallel execution is warranted, the SQL Server Database Engine determines
the optimal number of worker threads and spreads the execution of the parallel plan
across those worker threads. When a query or index operation starts executing on
multiple worker threads for parallel execution, the same number of worker threads is
used until the operation is completed. The SQL Server Database Engine re-examines the
optimal number of worker thread decisions every time an execution plan is retrieved
from the plan cache. For example, one execution of a query can result in the use of a
serial plan, a later execution of the same query can result in a parallel plan using three
worker threads, and a third execution can result in a parallel plan using four worker
threads.

The update and delete operators in a parallel query execution plan are executed serially,
but the WHERE clause of an UPDATE or a DELETE statement may be executed in parallel.
The actual data changes are then serially applied to the database.

Up to SQL Server 2012 (11.x), the insert operator is also executed serially. However, the
SELECT part of an INSERT statement may be executed in parallel. The actual data
changes are then serially applied to the database.
Starting with SQL Server 2014 (12.x) and database compatibility level 110, the SELECT …
INTO statement can be executed in parallel. Other forms of insert operators work the
same way as described for SQL Server 2012 (11.x).

Starting with SQL Server 2016 (13.x) and database compatibility level 130, the INSERT …
SELECT statement can be executed in parallel when inserting into heaps or clustered
columnstore indexes (CCI), and using the TABLOCK hint. Inserts into local temporary
tables (identified by the # prefix) and global temporary tables (identified by ## prefixes)
are also enabled for parallelism using the TABLOCK hint. For more information,
see INSERT (Transact-SQL).

Static and keyset-driven cursors can be populated by parallel execution plans. However,
the behavior of dynamic cursors can be provided only by serial execution. The Query
Optimizer always generates a serial execution plan for a query that is part of a dynamic
cursor.

Overriding Degrees of Parallelism

The degree of parallelism sets the number of processors to use in parallel plan
execution. This configuration can be set at various levels:

1. Server level, using the max degree of parallelism (MAXDOP) server


configuration option.
Applies to: SQL Server

 Note

SQL Server 2019 (15.x) introduces automatic recommendations for setting the
MAXDOP server configuration option during the installation process. The setup
user interface allows you to either accept the recommended settings or enter your
own value. For more information, see Database Engine Configuration - MaxDOP
page.

2. Workload level, using the MAX_DOP Resource Governor workload group


configuration option.
Applies to: SQL Server

3. Database level, using the MAXDOP database scoped configuration.


Applies to: SQL Server and Azure SQL Database
4. Query or index statement level, using the MAXDOP query hint or MAXDOP index
option. For example, you can use the MAXDOP option to control, by increasing or
reducing, the number of processors dedicated to an online index operation. In this
way, you can balance the resources used by an index operation with those of the
concurrent users.
Applies to: SQL Server and Azure SQL Database

Setting the max degree of parallelism option to 0 (default) enables SQL Server to use all
available processors up to a maximum of 64 processors in a parallel plan execution.
Although SQL Server sets a runtime target of 64 logical processors when MAXDOP
option is set to 0, a different value can be manually set if needed. Setting MAXDOP to 0
for queries and indexes allows SQL Server to use all available processors up to a
maximum of 64 processors for the given queries or indexes in a parallel plan execution.
MAXDOP is not an enforced value for all parallel queries, but rather a tentative target for
all queries eligible for parallelism. This means that if not enough worker threads are
available at runtime, a query may execute with a lower degree of parallelism than the
MAXDOP server configuration option.

 Tip

Refer to this documentation page for guidelines on configuring MAXDOP.

Parallel Query Example

The following query counts the number of orders placed in a specific quarter, starting
on April 1, 2000, and in which at least one line item of the order was received by the
customer later than the committed date. This query lists the count of such orders
grouped by each order priority and sorted in ascending priority order.

This example uses theoretical table and column names.

SQLCopy
SELECT o_orderpriority, COUNT(*) AS Order_Count
FROM orders
WHERE o_orderdate >= '2000/04/01'
AND o_orderdate < DATEADD (mm, 3, '2000/04/01')
AND EXISTS
(
SELECT *
FROM lineitem
WHERE l_orderkey = o_orderkey
AND l_commitdate < l_receiptdate
)
GROUP BY o_orderpriority
ORDER BY o_orderpriority

Assume the following indexes are defined on the lineitem and orders tables:

SQLCopy
CREATE INDEX l_order_dates_idx
ON lineitem
(l_orderkey, l_receiptdate, l_commitdate, l_shipdate)

CREATE UNIQUE INDEX o_datkeyopr_idx


ON ORDERS
(o_orderdate, o_orderkey, o_custkey, o_orderpriority)

Here is one possible parallel plan generated for the query previously shown:

Copy
|--Stream Aggregate(GROUP BY:([ORDERS].[o_orderpriority])
DEFINE:([Expr1005]=COUNT(*)))
|--Parallelism(Gather Streams, ORDER BY:
([ORDERS].[o_orderpriority] ASC))
|--Stream Aggregate(GROUP BY:
([ORDERS].[o_orderpriority])
DEFINE:([Expr1005]=Count(*)))
|--Sort(ORDER BY:([ORDERS].[o_orderpriority] ASC))
|--Merge Join(Left Semi Join, MERGE:
([ORDERS].[o_orderkey])=
([LINEITEM].[l_orderkey]),
RESIDUAL:([ORDERS].[o_orderkey]=
[LINEITEM].[l_orderkey]))
|--Sort(ORDER BY:([ORDERS].[o_orderkey] ASC))
| |--Parallelism(Repartition Streams,
PARTITION COLUMNS:
([ORDERS].[o_orderkey]))
| |--Index Seek(OBJECT:
([tpcd1G].[dbo].[ORDERS].[O_DATKEYOPR_IDX]),
SEEK:([ORDERS].[o_orderdate] >=
Apr 1 2000 12:00AM AND
[ORDERS].[o_orderdate] <
Jul 1 2000 12:00AM) ORDERED)
|--Parallelism(Repartition Streams,
PARTITION COLUMNS:
([LINEITEM].[l_orderkey]),
ORDER BY:([LINEITEM].[l_orderkey] ASC))
|--Filter(WHERE:
([LINEITEM].[l_commitdate]<
[LINEITEM].[l_receiptdate]))
|--Index Scan(OBJECT:
([tpcd1G].[dbo].[LINEITEM].[L_ORDER_DATES_IDX]), ORDERED)
The illustration below shows a query plan executed with a degree of parallelism equal to
4 and involving a two-table join.

The parallel plan contains three parallelism operators. Both the Index Seek operator of
the o_datkey_ptr index and the Index Scan operator of the l_order_dates_idx index are
performed in parallel. This produces several exclusive streams. This can be determined
from the nearest Parallelism operators above the Index Scan and Index Seek operators,
respectively. Both are repartitioning the type of exchange. That is, they are just
reshuffling data among the streams and producing the same number of streams on
their output as they have on their input. This number of streams is equal to the degree
of parallelism.

The parallelism operator above the l_order_dates_idx Index Scan operator is


repartitioning its input streams using the value of L_ORDERKEY as a key. In this way, the
same values of L_ORDERKEY end up in the same output stream. At the same time, output
streams maintain the order on the L_ORDERKEY column to meet the input requirement of
the Merge Join operator.

The parallelism operator above the Index Seek operator is repartitioning its input
streams using the value of O_ORDERKEY. Because its input is not sorted on
the O_ORDERKEY column values and this is the join column in the Merge Join operator, the
Sort operator between the parallelism and Merge Join operators make sure that the
input is sorted for the Merge Join operator on the join columns. The Sort operator, like
the Merge Join operator, is performed in parallel.

The topmost parallelism operator gathers results from several streams into a single
stream. Partial aggregations performed by the Stream Aggregate operator below the
parallelism operator are then accumulated into a single SUM value for each different
value of the O_ORDERPRIORITY in the Stream Aggregate operator above the parallelism
operator. Because this plan has two exchange segments, with degree of parallelism
equal to 4, it uses eight worker threads.

For more information on the operators used in this example, refer to the Showplan
Logical and Physical Operators Reference.

Parallel Index Operations

The query plans built for the index operations that create or rebuild an index, or drop a
clustered index, allow for parallel, multi-worker threaded operations on computers that
have multiple microprocessors.

 Note

Parallel index operations are only available in Enterprise Edition, starting with SQL Server
2008.

SQL Server uses the same algorithms to determine the degree of parallelism (the total
number of separate worker threads to run) for index operations as it does for other
queries. The maximum degree of parallelism for an index operation is subject to the max
degree of parallelism server configuration option. You can override the max degree of
parallelism value for individual index operations by setting the MAXDOP index option in
the CREATE INDEX, ALTER INDEX, DROP INDEX, and ALTER TABLE statements.

When the SQL Server Database Engine builds an index execution plan, the number of
parallel operations is set to the lowest value from among the following:

 The number of microprocessors, or CPUs in the computer.


 The number specified in the max degree of parallelism server configuration option.
 The number of CPUs not already over a threshold of work performed for SQL Server
worker threads.
For example, on a computer that has eight CPUs, but where max degree of parallelism is
set to 6, no more than six parallel worker threads are generated for an index operation.
If five of the CPUs in the computer exceed the threshold of SQL Server work when an
index execution plan is built, the execution plan specifies only three parallel worker
threads.

The main phases of a parallel index operation include the following:

 A coordinating worker thread quickly and randomly scans the table to estimate the
distribution of the index keys. The coordinating worker thread establishes the key
boundaries that will create a number of key ranges equal to the degree of parallel
operations, where each key range is estimated to cover similar numbers of rows. For
example, if there are four million rows in the table and the degree of parallelism is 4, the
coordinating worker thread will determine the key values that delimit four sets of rows
with 1 million rows in each set. If enough key ranges cannot be established to use all
CPUs, the degree of parallelism is reduced accordingly.
 The coordinating worker thread dispatches a number of worker threads equal to the
degree of parallel operations and waits for these worker threads to complete their work.
Each worker thread scans the base table using a filter that retrieves only rows with key
values within the range assigned to the worker thread. Each worker thread builds an index
structure for the rows in its key range. In the case of a partitioned index, each worker
thread builds a specified number of partitions. Partitions are not shared among worker
threads.
 After all the parallel worker threads have completed, the coordinating worker thread
connects the index subunits into a single index. This phase applies only to offline index
operations.

Individual CREATE TABLE or ALTER TABLE statements can have multiple constraints that


require that an index be created. These multiple index creation operations are
performed in series, although each individual index creation operation may be a parallel
operation on a computer that has multiple CPUs.

Distributed Query Architecture


Microsoft SQL Server supports two methods for referencing heterogeneous OLE DB data
sources in Transact-SQL statements:

 Linked server names


The system stored procedures sp_addlinkedserver and sp_addlinkedsrvlogin are
used to give a server name to an OLE DB data source. Objects in these linked
servers can be referenced in Transact-SQL statements using four-part names. For
example, if a linked server name of DeptSQLSrvr is defined against another instance
of SQL Server, the following statement references a table on that server:

SQLCopy
SELECT JobTitle, HireDate
FROM DeptSQLSrvr.AdventureWorks2014.HumanResources.Employee;

The linked server name can also be specified in an OPENQUERY statement to open a


rowset from the OLE DB data source. This rowset can then be referenced like a
table in Transact-SQL statements.

 Ad hoc connector names


For infrequent references to a data source,
the OPENROWSET or OPENDATASOURCE functions are specified with the information
needed to connect to the linked server. The rowset can then be referenced the
same way a table is referenced in Transact-SQL statements:

SQLCopy
SELECT *
FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0',
'c:\MSOffice\Access\Samples\Northwind.mdb';'Admin';'';
Employees);

SQL Server uses OLE DB to communicate between the relational engine and the storage
engine. The relational engine breaks down each Transact-SQL statement into a series of
operations on simple OLE DB rowsets opened by the storage engine from the base
tables. This means the relational engine can also open simple OLE DB rowsets on any
OLE DB data source.
The relational engine uses the OLE DB application programming interface (API) to open
the rowsets on linked servers, fetch the rows, and manage transactions.

For each OLE DB data source accessed as a linked server, an OLE DB provider must be
present on the server running SQL Server. The set of Transact-SQL operations that can
be used against a specific OLE DB data source depends on the capabilities of the OLE
DB provider.

For each instance of SQL Server, members of the sysadmin fixed server role can enable or
disable the use of ad-hoc connector names for an OLE DB provider using the SQL
Server DisallowAdhocAccess property. When ad-hoc access is enabled, any user logged
on to that instance can execute Transact-SQL statements containing ad-hoc connector
names, referencing any data source on the network that can be accessed using that OLE
DB provider. To control access to data sources, members of the sysadmin role can disable
ad-hoc access for that OLE DB provider, thereby limiting users to only those data
sources referenced by linked server names defined by the administrators. By default, ad-
hoc access is enabled for the SQL Server OLE DB provider, and disabled for all other OLE
DB providers.

Distributed queries can allow users to access another data source (for example, files,
non-relational data sources such as Active Directory, and so on) using the security
context of the Microsoft Windows account under which the SQL Server service is
running. SQL Server impersonates the login appropriately for Windows logins; however,
that is not possible for SQL Server logins. This can potentially allow a distributed query
user to access another data source for which they do not have permissions, but the
account under which the SQL Server service is running does have permissions.
Use sp_addlinkedsrvlogin to define the specific logins that are authorized to access the
corresponding linked server. This control is not available for ad-hoc names, so use
caution in enabling an OLE DB provider for ad-hoc access.

When possible, SQL Server pushes relational operations such as joins, restrictions,
projections, sorts, and group by operations to the OLE DB data source. SQL Server does
not default to scanning the base table into SQL Server and performing the relational
operations itself. SQL Server queries the OLE DB provider to determine the level of SQL
grammar it supports, and, based on that information, pushes as many relational
operations as possible to the provider.

SQL Server specifies a mechanism for an OLE DB provider to return statistics indicating
how key values are distributed within the OLE DB data source. This lets the SQL Server
Query Optimizer better analyze the pattern of data in the data source against the
requirements of each Transact-SQL statement, increasing the ability of the Query
Optimizer to generate optimal execution plans.

Query Processing Enhancements on Partitioned Tables


and Indexes
SQL Server 2008 improved query processing performance on partitioned tables for
many parallel plans, changes the way parallel and serial plans are represented, and
enhanced the partitioning information provided in both compile-time and run-time
execution plans. This topic describes these improvements, provides guidance on how to
interpret the query execution plans of partitioned tables and indexes, and provides best
practices for improving query performance on partitioned objects.

 Note

Until SQL Server 2014 (12.x), partitioned tables and indexes are supported only in the
SQL Server Enterprise, Developer, and Evaluation editions.
Starting with SQL Server 2016 (13.x) SP1, partitioned tables and indexes are also
supported in SQL Server Standard edition.

New Partition-Aware Seek Operation

In SQL Server, the internal representation of a partitioned table is changed so that the
table appears to the query processor to be a multicolumn index with PartitionID as the
leading column. PartitionID is a hidden computed column used internally to represent
the ID of the partition containing a specific row. For example, assume the table T,
defined as T(a, b, c), is partitioned on column a, and has a clustered index on column
b. In SQL Server, this partitioned table is treated internally as a nonpartitioned table with
the schema T(PartitionID, a, b, c) and a clustered index on the composite
key (PartitionID, b). This allows the Query Optimizer to perform seek operations based
on PartitionID on any partitioned table or index.

Partition elimination is now done in this seek operation.

In addition, the Query Optimizer is extended so that a seek or scan operation with one
condition can be done on PartitionID (as the logical leading column) and possibly other
index key columns, and then a second-level seek, with a different condition, can be done
on one or more additional columns, for each distinct value that meets the qualification
for the first-level seek operation. That is, this operation, called a skip scan, allows the
Query Optimizer to perform a seek or scan operation based on one condition to
determine the partitions to be accessed and a second-level index seek operation within
that operator to return rows from these partitions that meet a different condition. For
example, consider the following query.

SQLCopy
SELECT * FROM T WHERE a < 10 and b = 2;

For this example, assume that table T, defined as T(a, b, c), is partitioned on column a,
and has a clustered index on column b. The partition boundaries for table T are defined
by the following partition function:

SQLCopy
CREATE PARTITION FUNCTION myRangePF1 (int) AS RANGE LEFT FOR VALUES (3, 7, 10);

To solve the query, the query processor performs a first-level seek operation to find
every partition that contains rows that meet the condition T.a < 10. This identifies the
partitions to be accessed. Within each partition identified, the processor then performs a
second-level seek into the clustered index on column b to find the rows that meet the
condition T.b = 2 and T.a < 10.

The following illustration is a logical representation of the skip scan operation. It shows
table T with data in columns a and b. The partitions are numbered 1 through 4 with the
partition boundaries shown by dashed vertical lines. A first-level seek operation to the
partitions (not shown in the illustration) has determined that partitions 1, 2, and 3 meet
the seek condition implied by the partitioning defined for the table and the predicate on
column a. That is, T.a < 10. The path traversed by the second-level seek portion of the
skip scan operation is illustrated by the curved line. Essentially, the skip scan operation
seeks into each of these partitions for rows that meet the condition b = 2. The total cost
of the skip scan operation is the same as that of three separate index seeks.

Displaying Partitioning Information in Query Execution Plans

The execution plans of queries on partitioned tables and indexes can be examined by
using the Transact-SQL SET statements SET SHOWPLAN_XML or SET STATISTICS XML, or by
using the graphical execution plan output in SQL Server Management Studio. For
example, you can display the compile-time execution plan by clicking Display Estimated
Execution Plan on the Query Editor toolbar and the run-time plan by clicking Include
Actual Execution Plan.

Using these tools, you can ascertain the following information:

 The operations such as scans, seeks, inserts, updates, merges, and deletes that access


partitioned tables or indexes.
 The partitions accessed by the query. For example, the total count of partitions accessed
and the ranges of contiguous partitions that are accessed are available in run-time
execution plans.
 When the skip scan operation is used in a seek or scan operation to retrieve data from one
or more partitions.
Partition Information Enhancements

SQL Server provides enhanced partitioning information for both compile-time and run-
time execution plans. Execution plans now provide the following information:

 An optional Partitioned attribute that indicates that an operator, such as


a seek, scan, insert, update, merge, or delete, is performed on a partitioned table.
 A new SeekPredicateNew element with a SeekKeys subelement that
includes PartitionID as the leading index key column and filter conditions that specify
range seeks on PartitionID. The presence of two SeekKeys subelements indicates that a
skip scan operation on PartitionID is used.
 Summary information that provides a total count of the partitions accessed. This
information is available only in run-time plans.
To demonstrate how this information is displayed in both the graphical execution plan
output and the XML Showplan output, consider the following query on the partitioned
table fact_sales. This query updates data in two partitions.

SQLCopy
UPDATE fact_sales
SET quantity = quantity * 2
WHERE date_id BETWEEN 20080802 AND 20080902;

The following illustration shows the properties of the Clustered Index Seek operator in


the runtime execution plan for this query. To view the definition of the fact_sales table
and the partition definition, see "Example" in this topic.
Partitioned Attribute

When an operator such as an Index Seek is executed on a partitioned table or index,


the Partitioned attribute appears in the compile-time and run-time plan and is set
to True (1). The attribute does not display when it is set to False (0).

The Partitioned attribute can appear in the following physical and logical operators:


 Table Scan
 Index Scan
 Index Seek
 Insert
 Update
 Delete
 Merge

As shown in the previous illustration, this attribute is displayed in the properties of the
operator in which it is defined. In the XML Showplan output, this attribute appears
as Partitioned="1" in the RelOp node of the operator in which it is defined.

New Seek Predicate

In XML Showplan output, the SeekPredicateNew element appears in the operator in which


it is defined. It can contain up to two occurrences of the SeekKeys sub-element. The
first SeekKeys item specifies the first-level seek operation at the partition ID level of the
logical index. That is, this seek determines the partitions that must be accessed to satisfy
the conditions of the query. The second SeekKeys item specifies the second-level seek
portion of the skip scan operation that occurs within each partition identified in the first-
level seek.

Partition Summary Information

In run-time execution plans, partition summary information provides a count of the


partitions accessed and the identity of the actual partitions accessed. You can use this
information to verify that the correct partitions are accessed in the query and that all
other partitions are eliminated from consideration.

The following information is provided: Actual Partition Count, and Partitions Accessed.

Actual Partition Count is the total number of partitions accessed by the query.

Partitions Accessed, in XML Showplan output, is the partition summary information that
appears in the new RuntimePartitionSummary element in RelOp node of the operator in
which it is defined. The following example shows the contents of
the RuntimePartitionSummary element, indicating that two total partitions are accessed
(partitions 2 and 3).

Copy
<RunTimePartitionSummary>
<PartitionsAccessed PartitionCount="2" >

<PartitionRange Start="2" End="3" />

</PartitionsAccessed>

</RunTimePartitionSummary>
Displaying Partition Information by Using Other Showplan Methods

The Showplan methods SHOWPLAN_ALL, SHOWPLAN_TEXT, and STATISTICS PROFILE do not


report the partition information described in this topic, with the following exception. As
part of the SEEK predicate, the partitions to be accessed are identified by a range
predicate on the computed column representing the partition ID. The following example
shows the SEEK predicate for a Clustered Index Seek operator. Partitions 2 and 3 are
accessed, and the seek operator filters on the rows that meet the condition date_id
BETWEEN 20080802 AND 20080902.

Copy
|--Clustered Index Seek(OBJECT:([db_sales_test].[dbo].[fact_sales].[ci]),

SEEK:([PtnId1000] >= (2) AND [PtnId1000] \<= (3)

AND [db_sales_test].[dbo].[fact_sales].[date_id] >= (20080802)

AND [db_sales_test].[dbo].[fact_sales].[date_id] <= (20080902))

ORDERED FORWARD)
Interpreting Execution Plans for Partitioned Heaps

A partitioned heap is treated as a logical index on the partition ID. Partition elimination
on a partitioned heap is represented in an execution plan as a Table Scan operator with
a SEEK predicate on partition ID. The following example shows the Showplan information
provided:

Copy
|-- Table Scan (OBJECT: ([db].[dbo].[T]), SEEK: ([PtnId1001]=[Expr1011]) ORDERED
FORWARD)
Interpreting Execution Plans for Collocated Joins

Join collocation can occur when two tables are partitioned using the same or equivalent
partitioning function and the partitioning columns from both sides of the join are
specified in the join condition of the query. The Query Optimizer can generate a plan
where the partitions of each table that have equal partition IDs are joined separately.
Collocated joins can be faster than non-collocated joins because they can require less
memory and processing time. The Query Optimizer chooses a non-collocated plan or a
collocated plan based on cost estimates.

In a collocated plan, the Nested Loops join reads one or more joined table or index
partitions from the inner side. The numbers within the Constant Scan operators represent
the partition numbers.

When parallel plans for collocated joins are generated for partitioned tables or indexes,
a Parallelism operator appears between the Constant Scan and the Nested Loops join
operators. In this case, multiple worker threads on the outer side of the join each read
and work on a different partition.

The following illustration demonstrates a parallel query plan for a collocated join.

Parallel Query Execution Strategy for Partitioned Objects

The query processor uses a parallel execution strategy for queries that select from
partitioned objects. As part of the execution strategy, the query processor determines
the table partitions required for the query and the proportion of worker threads to
allocate to each partition. In most cases, the query processor allocates an equal or
almost equal number of worker threads to each partition, and then executes the query
in parallel across the partitions. The following paragraphs explain worker thread
allocation in greater detail.
If the number of worker threads is less than the number of partitions, the query
processor assigns each worker thread to a different partition, initially leaving one or
more partitions without an assigned worker thread. When a worker thread finishes
executing on a partition, the query processor assigns it to the next partition until each
partition has been assigned a single worker thread. This is the only case in which the
query processor reallocates worker threads to other partitions.
Shows worker thread reassigned after it finishes. If the number of worker threads is
equal to the number of partitions, the query processor assigns one worker thread to
each partition. When a worker thread finishes, it is not reallocated to another partition.

If the number of worker threads is greater than the number of partitions, the query
processor allocates an equal number of worker threads to each partition. If the number
of worker threads is not an exact multiple of the number of partitions, the query
processor allocates one additional worker thread to some partitions in order to use all of
the available worker threads. Note that if there is only one partition, all worker threads
will be assigned to that partition. In the diagram below, there are four partitions and 14
worker threads. Each partition has 3 worker threads assigned, and two partitions have an
additional worker thread, for a total of 14 worker thread assignments. When a worker
thread finishes, it is not reassigned to another partition.

Although the above examples suggest a straightforward way to allocate worker threads,
the actual strategy is more complex and accounts for other variables that occur during
query execution. For example, if the table is partitioned and has a clustered index on
column A and a query has the predicate clause WHERE A IN (13, 17, 25), the query
processor will allocate one or more worker threads to each of these three seek values
(A=13, A=17, and A=25) instead of each table partition. It is only necessary to execute
the query in the partitions that contain these values, and if all of these seek predicates
happen to be in the same table partition, all of the worker threads will be assigned to
the same table partition.

To take another example, suppose that the table has four partitions on column A with
boundary points (10, 20, 30), an index on column B, and the query has a predicate
clause WHERE B IN (50, 100, 150). Because the table partitions are based on the values
of A, the values of B can occur in any of the table partitions. Thus, the query processor
will seek for each of the three values of B (50, 100, 150) in each of the four table
partitions. The query processor will assign worker threads proportionately so that it can
execute each of these 12 query scans in parallel.

TABLE 2

Table partitions based on column A Seeks for column B in each table partition

Table Partition 1: A < 10 B=50, B=100, B=150

Table Partition 2: A >= 10 AND A < 20 B=50, B=100, B=150

Table Partition 3: A >= 20 AND A < 30 B=50, B=100, B=150

Table Partition 4: A >= 30 B=50, B=100, B=150

Best Practices

To improve the performance of queries that access a large amount of data from large
partitioned tables and indexes, we recommend the following best practices:

 Stripe each partition across many disks. This is especially relevant when using spinning
disks.
 When possible, use a server with enough main memory to fit frequently accessed
partitions or all partitions in memory to reduce I/O cost.
 If the data you query will not fit in memory, compress the tables and indexes. This will
reduce I/O cost.
 Use a server with fast processors and as many processor cores as you can afford, to take
advantage of parallel query processing capability.
 Ensure the server has sufficient I/O controller bandwidth.
 Create a clustered index on every large partitioned table to take advantage of B-tree
scanning optimizations.
 Follow the best practice recommendations in the white paper, The Data Loading
Performance Guide, when bulk loading data into partitioned tables.
Example

The following example creates a test database containing a single table with seven
partitions. Use the tools described previously when executing the queries in this
example to view partitioning information for both compile-time and run-time plans.

 Note

This example inserts more than 1 million rows into the table. Running this example may
take several minutes depending on your hardware. Before executing this example, verify
that you have more than 1.5 GB of disk space available.

SQLCopy
USE master;
GO
IF DB_ID (N'db_sales_test') IS NOT NULL
DROP DATABASE db_sales_test;
GO
CREATE DATABASE db_sales_test;
GO
USE db_sales_test;
GO
CREATE PARTITION FUNCTION [pf_range_fact](int) AS RANGE RIGHT FOR VALUES
(20080801, 20080901, 20081001, 20081101, 20081201, 20090101);
GO
CREATE PARTITION SCHEME [ps_fact_sales] AS PARTITION [pf_range_fact]
ALL TO ([PRIMARY]);
GO
CREATE TABLE fact_sales(date_id int, product_id int, store_id int,
quantity int, unit_price numeric(7,2), other_data char(1000))
ON ps_fact_sales(date_id);
GO
CREATE CLUSTERED INDEX ci ON fact_sales(date_id);
GO
PRINT 'Loading...';
SET NOCOUNT ON;
DECLARE @i int;
SET @i = 1;
WHILE (@i<1000000)
BEGIN
INSERT INTO fact_sales VALUES(20080800 + (@i%30) + 1, @i%10000, @i%200, RAND() *
25, (@i%3) + 1, '');
SET @i += 1;
END;
GO
DECLARE @i int;
SET @i = 1;
WHILE (@i<10000)
BEGIN
INSERT INTO fact_sales VALUES(20080900 + (@i%30) + 1, @i%10000, @i%200, RAND() *
25, (@i%3) + 1, '');
SET @i += 1;
END;
PRINT 'Done.';
GO
-- Two-partition query.
SET STATISTICS XML ON;
GO
SELECT date_id, SUM(quantity*unit_price) AS total_price
FROM fact_sales
WHERE date_id BETWEEN 20080802 AND 20080902
GROUP BY date_id ;
GO
SET STATISTICS XML OFF;
GO
-- Single-partition query.
SET STATISTICS XML ON;
GO
SELECT date_id, SUM(quantity*unit_price) AS total_price
FROM fact_sales
WHERE date_id BETWEEN 20080801 AND 20080831
GROUP BY date_id;
GO
SET STATISTICS XML OFF;
GO
Triggers in SQL Server with Examples
1. What are Triggers in SQL Server?
2. Types of Triggers in SQL Server
3. What are DML Triggers in SQL Server?
4. Examples to understand DML Trigger
5. Where the Triggers are Created in SQL Server?
6. Why do we need DML Triggers in SQL Server?
7. Multiple examples to understand the above concepts
What are Triggers in SQL Server?
Triggers are nothing but they are logic’s like stored procedures that can be executed
automatically before the Insert, Update or Delete happens in a table or after the Insert,
Update, or Delete happens in a table. In simple words, we can say that, if you want to
execute some pre-processing or post-processing logic before or after the Insert, Update, or
Delete in a table then you need to use Triggers in SQL Server.

There are two types of triggers. They are as follows:


1. Instead of Triggers: The Instead Of triggers are going to be executed instead of the
corresponding DML operations. That means instead of the DML operations such as
Insert, Update, and Delete, the Instead Of triggers are going to be executed.
2. After Triggers: The After Triggers fires in SQL Server execute after the triggering
action. That means once the DML statement (such as Insert, Update, and Delete)
completes its execution, this trigger is going to be fired.
Types of Triggers in SQL Server:
There are four types of triggers available in SQL Server. They are as follows:
1. DML Triggers – Data Manipulation Language Triggers.
2. DDL Triggers – Data Definition Language Triggers
3. CLR triggers – Common Language Runtime Triggers
4. Logon triggers
In this article, we are going to discuss the DML triggers and the rest are going to discuss in
our upcoming articles.
What are DML Triggers in SQL Server?
As we know DML Stands for Data Manipulation Language and it provides Insert, Update
and Delete statements to perform the respective operation on the database tables or view
which will modify the data. The triggers which are executed automatically in response to
DML events (such as Insert, Update, and Delete statements) are called DML Triggers.
The syntax for creating a DML Triggers in SQL Server:
You can create a DML Trigger in SQL Server by using the following syntax.

Let us understand the syntax in detail.


1. ON TableName or ViewName: It refers to the table or view name on which we are
defining the trigger.
2. For/After/InsteadOf:  The For/After option specifies that the trigger fires only after the
SQL statements are executed whereas the InsteadOf option specifies that the trigger is
executed on behalf of the triggering SQL statement. You cannot create After Trigger on
views.
3. INSERT, UPDATE, DELETE: The INSERT, UPDATE, DELETE specify which SQL
statement will fire this trigger and we need to use at least one option or the combination
of multiple options is also accepted.
Note: The Insert, Update and Delete statements are also known as Triggering SQL
statements as these statements are responsible for the trigger to fire.
Examples to understand DML Trigger in SQL Server:
In this article, we are going to discuss some simple examples to understand the triggers and
from our next article, we will see the real-time usages of Triggers.  Here, we are going to
use the following Employee table to understand the Triggers.

Please use the below SQL Script to create and populate the Employee table.
-- Create Employee table
CREATE TABLE Employee

Id int Primary Key,

Name nvarchar(30),

Salary int,

Gender nvarchar(10),

DepartmentId int

GO

-- Insert data into Employee table

INSERT INTO Employee VALUES (1,'Pranaya', 5000, 'Male', 3)

INSERT INTO Employee VALUES (2,'Priyanka', 5400, 'Female', 2)

INSERT INTO Employee VALUES (3,'Anurag', 6500, 'male', 1)

INSERT INTO Employee VALUES (4,'sambit', 4700, 'Male', 2)

INSERT INTO Employee VALUES (5,'Hina', 6600, 'Female', 3)


Example: For/After Insert DML Trigger in SQL Server
So, basically what we want is, we want to create a For/After DML Trigger which should fire
after the INSERT DML operation when performing on the Employee table. The trigger
should restrict the INSERT operation on the Employee table. To do so, please execute the
below query. As you can see in the below query, this trigger is created for the Employee
table. We also specify that is a FOR trigger for the INSERT DML operation and as part of
the trigger body, we are simply rollbacking the transaction which will roll back the insert
statement.
CREATE TRIGGER trInsertEmployee

ON Employee

FOR INSERT

AS

BEGIN
PRINT 'YOU CANNOT PERFORM INSERT OPERATION'

ROLLBACK TRANSACTION

END
Let’s try to insert the following record into the employee table.
INSERT INTO Employee VALUES (6, ‘Saroj’, 7600, ‘Male’, 1)
When you try to execute the above Insert statement it gives you the below error. First, the
INSERT statement is executed, and then immediately this trigger fired and roll back the
INSERT operation as well as print the message.

Example: For/After Update DML Trigger in SQL Server


So, basically what we want is, we want to create a For/After DML Trigger which should fire
after the UPDATE DML operation when performing on the Employee table. The trigger
should restrict the UPDATE operation on the Employee table. To do so, please execute the
below query. As you can see in the below query, this trigger is created for the Employee
table. We also specify that is a FOR trigger for the UPDATE DML operation and as part of
the trigger body, we are simply rollbacking the transaction which will roll back the UPDATE
statement and print a message.
CREATE TRIGGER trUpdateEmployee

ON Employee

FOR UPDATE

AS

BEGIn

PRINT 'YOU CANNOT PERFORM UPDATE OPERATION'

ROLLBACK TRANSACTION

END
Let’s try to update one record in the Employee table
UPDATE Employee SET Salary = 90000 WHERE Id = 1
When you try to execute the above Update statement it will give you the following error.
First, the UPDATE statement is executed, and then immediately this trigger fired and roll
back the UPDATE operation as well as print the message.
Example3: For/After Delete DML Triggers in SQL Server
So, basically what we want is, we want to create a For/After DML Trigger which should fire
after the DELETE DML operation when performing on the Employee table. The trigger
should restrict the DELETE operation on the Employee table. To do so, please execute the
below query. As you can see in the below query, this trigger is created for
the Employee table. We also specify that is a FOR trigger for the DELETE DML operation
and as part of the trigger body, we are simply rollbacking the transaction which will roll back
the DELETE operation and print a message.
CREATE TRIGGER trDeleteEmployee

ON Employee

FOR DELETE

AS

BEGIN

PRINT 'YOU CANNOT PERFORM DELETE OPERATION'

ROLLBACK TRANSACTION

END
Let’s try to delete one record from the Employee table
DELETE FROM Employee WHERE Id = 1
When we try to execute the above Delete statement, it gives us the below error. First, the
DELETE statement is executed, and then immediately this trigger fired and roll back the
DELETE operation as well as print the message.

Where the Triggers are Created in SQL Server?


In SQL Server, the Triggers are created within the Trigger folder which you can find when
you expand the table as shown in the below image.
Example4: For Insert/Update/Delete DML Trigger in SQL Server
So, basically what we want is, we want to create a For/After DML Trigger which should fire
after any DML operation (INSERT, UPDATE, and DELETE) when performing on the
Employee table. The trigger should restrict all the DML operations on the Employee table.
As you can see in the below query, this trigger is created for the Employee table. We also
specify that is a FOR trigger for the INSERT, UPDATE, DELETE DML operation and as part
of the trigger body, we are simply rollbacking the transaction which will roll back all DML
operation and print a message.
First, delete all the triggers that we already created on the Employee table. To delete you
can the below syntax.
DROP Trigger TrggerName
Example:
DROP TRIGGER trDeleteEmployee
DROP TRIGGER trInsertEmployee
DROP TRIGGER trUpdateEmployee
Now execute the below query to create a trigger that will restrict all the DML Operations
on the Employee table.
CREATE TRIGGER trAllDMLOperationsOnEmployee

ON Employee

FOR INSERT, UPDATE, DELETE

AS

BEGIN

PRINT 'YOU CANNOT PERFORM DML OPERATION'


ROLLBACK TRANSACTION

END
Now, you cannot perform any DML operations on the Employee table because those
operations are restricted by a trigger called trAllDMLOperationsOnEmployee.
Example5:
Create a Trigger that will restrict all the DML operations on the Employee table on
MONDAY only.
1. SUN DAY = 1
2. MON DAY = 2
3. TUE DAY = 3
4. WED DAY = 4
5. THU DAY = 5
6. FRI DAY = 6
7. SAT DAY = 7
ALTER TRIGGER trAllDMLOperationsOnEmployee

ON Employee

FOR INSERT, UPDATE, DELETE

AS

BEGIN

IF DATEPART(DW,GETDATE())= 2

BEGIN

PRINT 'DML OPERATIONS ARE RESTRICTED ON MONDAY'

ROLLBACK TRANSACTION

END

END
Example6:
Create a Trigger that will restrict all the DML operations on the Employee table before
1 pm.
ALTER TRIGGER trAllDMLOperationsOnEmployee

ON Employee

FOR INSERT, UPDATE, DELETE


AS

BEGIN

IF DATEPART(HH,GETDATE()) < 13

BEGIN

PRINT 'INVALID TIME'

ROLLBACK TRANSACTION

END

END
Why do we need DML Triggers in SQL Server?
DML Triggers are used to enforce business rules and data integrity. These triggers are very
much similar to constraints in the way they enforce integrity. So, with the help of DML
Triggers, we can enforce data integrity which cannot be done with the help of constraints
that is comparing values with values of another table, etc.
Inserted and Deleted Tables in SQL Server
1. What are Inserted and Deleted Tables in SQL Server?
2. Understanding the Inserted and Deleted Tables with Examples
3. What is Deleted Table in SQL Server?
4. How to view the updating data in a table?
5. What will happen if we update multiple records at a time?
What are Inserted and Deleted Tables in SQL Server?
Inserted and Deleted tables are temporary tables that are created by SQL Server in the
context of a trigger. That means these two tables can only be available as part of a trigger.
If you try to access these tables outside of a trigger, then you will get an error. The table
structure of both inserted and deleted tables will be exactly the same as the table structure
of the table on which the trigger is created.
Whenever you fire any INSERT, UPDATE, and DELETE statement on a table, all the new
records are actually going to the inserted table i.e. all the updated and new records are
present in the inserted table. On the other hand, all the old values are present in the deleted
table.

Understanding the Inserted and Deleted Tables with Examples in SQL Server.
Let us understand how we can use Inserted and Deleted tables within a trigger with
examples in SQL Server. We are going to use the following Employee table to understand
the Inserted and Deleted Tables in the SQL server.
Please use the below SQL Script to create and populate the Employee table with
sample data.
-- Create Employee table

CREATE TABLE Employee

Id int Primary Key,

Name nvarchar(30),

Salary int,

Gender nvarchar(10),

DepartmentId int

GO

-- Insert data into Employee table

INSERT INTO Employee VALUES (1,'Pranaya', 5000, 'Male', 3)

INSERT INTO Employee VALUES (2,'Priyanka', 5400, 'Female', 2)

INSERT INTO Employee VALUES (3,'Anurag', 6500, 'male', 1)

INSERT INTO Employee VALUES (4,'sambit', 4700, 'Male', 2)

INSERT INTO Employee VALUES (5,'Hina', 6600, 'Female', 3)


Understanding the INSERTED Table in SQL Server:
The Inserted table is created by SQL Server when we perform an INSERT operation and
this table has access to the values being inserted into the table. So, whenever we insert the
values into a table those values we can see in the inserted magic table. Let us see an
example for a better understanding. The following is an example Insert trigger. So, when we
insert a record into the Employee table, this trigger is going to be fired after the record
inserted. And the inserted data is also stored in the INSERTED magic table and those data
stored in the INSERTED magic table are displaying as part of the trigger body.
CREATE TRIGGER trInsertEmployee

ON Employee

FOR INSERT

AS

BEGIN

SELECT * FROM INSERTED

END
Let’s Insert one record into the Employee table
INSERT INTO Employee VALUES (6, ‘Saroj’, 7700, ‘Male’, 2)
So when we execute the above insert statement, the data is inserted as expected in the
Employee table along with a copy of the inserted new data also available in the Inserted
table. So, we get the following output. Please note, the structure of the Inserted table is
exactly the same as the structure of the Employee table.

When we add a new row into the Employee table a copy of the row will also be made into
the INSERTED table which only a trigger can access. We cannot access this table outside
the context of the trigger. 
What is Deleted Table in SQL Server?
The Deleted table is created by SQL Server when we perform a delete operation on the
table and this table has access to the record being deleted. So, in simple words, we can say
that, whenever we delete a record from a table the deleted record information we can view
with the help of the deleted table as part of a trigger in SQL Server. Let us understand this
with an example. The following is an example delete trigger. So, when we delete a record
from the Employee table, this trigger is going to be fired.
CREATE TRIGGER trDeleteEmployee

ON Employee

FOR DELETE

AS

BEGIN

SELECT * FROM DELETED


END
Let’s Delete one record from the Employee table
DELETE FROM Employee WHERE Id = 6
When we execute the above Delete statement, the data gets deleted from the Employee
table whose Id is 6 along with it also displays the following deleted data as part of the
deleted table.

This proofs that the delete table holds the data that is being deleted from the table. When
we delete a row from the Employee table, a copy of the deleted row will be made available
in the DELETED table, which only a trigger can access. Just like the INSERTED table, the
DELETED table cannot be accessed outside the context of a trigger and the structure of the
DELETED table will be identical to the structure of the Employee table.
How to view the updating data in a table?
When we perform an update operation, then we will be having both the inserted and deleted
tables. The inserted table will hold the new values being inserted and the deleted table will
hold the old values of the table. So in a simple word, we can say that, whenever we update
a record in the table, then we can view the new data in the inserted table and the old data in
the deleted table. Let us understand this with an example. The following is an example
update trigger. So, when we update a record from the Employee table, this trigger is going
to be fired.
-- Create Update Trigger

CREATE TRIGGER trUpdateEmployee

ON Employee

FOR UPDATE

AS

BEGIN

SELECT * FROM DELETED

SELECT * FROM INSERTED

END
Let’s Update one record in the Employee table by executing the following update statement.
UPDATE Employee SET Name = ‘Sharma’, Salary = 8000 WHERE Id = 5
So when we execute the above update statement, the data is updated in the table as
expected along with it, it also gives us the following output. So this proves that whenever we
perform an update operation then the Deleted Table will hold the OLD Data whereas the
InsertedTable will hold the new Data.
What will happen if we update multiple records at a time?
Currently, the employee table holds the below data

Here you can see that the Employee table having three employees whose Gender is Male.
Let’s update the Salary of All the Male Employees to 20000 by executing the following SQL
query.
UPDATE Employee SET Salary = 20000 WHERE Gender = ‘Male’
So when we execute the above code the data is updated in the Employee table as expected
as well as we will also get the following output. As you can see in the below image, all the
old records are stored in the Deleted Table whereas all the updated data are stored in the
Inserted table.
DML Trigger Real-Time Examples in SQL Server
Real-time Examples of DML Trigger in SQL Server:
Here, we are going to implement the audit example using the DML Trigger in SQL Server.
So, whenever we INSERT, UPDATE, or DELETE any data from a table, then we need to
make an entry into the audit table as well. We are going to use the following Employee table
to understand this concept.

Please use the following SQL Script to create and populate the Employee table.
-- Create Employee table

CREATE TABLE Employee

Id int Primary Key,

Name nvarchar(30),

Salary int,

Gender nvarchar(10),

DepartmentId int

GO

-- Insert data into Employee table

INSERT INTO Employee VALUES (1,'Pranaya', 5000, 'Male', 3)

INSERT INTO Employee VALUES (2,'Priyanka', 5400, 'Female', 2)

INSERT INTO Employee VALUES (3,'Anurag', 6500, 'male', 1)

INSERT INTO Employee VALUES (4,'sambit', 4700, 'Male', 2)

INSERT INTO Employee VALUES (5,'Hina', 6600, 'Female', 3)


Along with the above Employee table, we are also going to use the
following EmployeeAudit table. Please use the below SQL Script to create
the EmployeeAudit table.
-- Create EmployeeAudit Table

CREATE TABLE EmployeeAudit

ID INT IDENTITY(1,1) PRIMARY KEY,

AuditData VARCHAR(MAX),

AuditDate DATETIME

)
Our business requirement is that whenever a new employee is added to the Employee table
we want to capture the ID and name of the Employee and store the data into
the EmployeeAudit table. The simplest way to achieve this is by using an AFTER
TRIGGER for the INSERT event.
Example: AFTER TRIGGER for INSERT Event in SQL Server
The following is an example of AFTER TRIGGER for INSERT event on Employee table to
store the inserted employee data on the EmployeeAudit table:
CREATE TRIGGER tr_Employee_For_Insert

ON Employee

FOR INSERT

AS

BEGIN

-- Declare a variable to hold the ID Value

DECLARE @ID INT

-- Declare a variable to hold the Name value

DECLARE @Name VARCHAR(100)

-- Declare a variable to hold the Audit data

DECLARE @AuditData VARCHAR(100)

-- Get the ID and Name from the INSERTED Magic table


SELECT @ID = ID, @Name = Name FROM INSERTED

-- Set the AuditData to be stored in the EmployeeAudit table

SET @AuditData = 'New employee Added with ID = ' + Cast(@ID AS VARCHAR(10)) + '
and Name ' + @Name

-- Insert the data into the EmployeeAudit table

INSERT INTO EmployeeAudit (AuditData, AuditDate) VALUES(@AuditData,


GETDATE())

END
Once you created the above DML AFTER INSERT Trigger. Now let us insert a record into
the Employee table by executing the below INSERT statement.
INSERT INTO Employee VALUES (6, ‘Saroj’, 3300, ‘Male’, 2)
When we execute the above INSERT statement. Immediately after inserting the record into
the Employee table, the insert trigger gets fired automatically, and a record is also inserted
into the EmployeeAudit table. To verify this, issue a select query against the EmployeeAudit
table as shown below
SELECT * FROM EmployeeAudit
Once you execute the above query, it will give you the following output.

Example: AFTER TRIGGER for DELETE Event in SQL Server


Next, we are going to capture the audit information when an employee is deleted from the
Employee table. The following is an example for AFTER TRIGGER for DELETE event on
the Employee table in SQL Server to store the deleted employee data on
the EmployeeAudit table.
CREATE TRIGGER tr_Employee_For_Delete

ON Employee

FOR DELETE

AS

BEGIN

-- Declare a variable to hold the ID Value

DECLARE @ID INT

-- Declare a variable to hold the Name value


DECLARE @Name VARCHAR(100)

-- Declare a variable to hold the Audit data

DECLARE @AuditData VARCHAR(100)

-- Get the ID and Name from the DELETED table

SELECT @ID = ID, @Name = Name FROM DELETED

-- Set the AuditData to be stored in the EmployeeAudit table

SET @AuditData = 'An employee is deleted with ID = ' + Cast(@ID AS VARCHAR(10)) +


' and Name = ' + @Name

-- Insert the data into the EmployeeAudit table

INSERT INTO EmployeeAudit (AuditData, AuditDate)VALUES(@AuditData,


GETDATE())

END
Once you created the above DML AFTER DELETE Trigger. Now let us DELETE a record
from the Employee table by executing the below DELETE SQL statement.
DELETE FROM Employee WHERE ID = 6
So when we execute the above DELETE statement. Immediately after Deleting the row
from the Employee table, the delete trigger gets fired automatically, and a row into
EmployeeAudit is also inserted. To verify this, issue a select query against the
EmployeeAudit table as shown below
SELECT * FROM EmployeeAudit
Once you execute the above query, it will give you the following output.

As we already see, triggers make use of 2 special tables INSERTED and DELETED. The
inserted table contains the updated data or new data whereas the deleted table contains the
old data or deleted data. The After trigger for the UPDATE event makes use of both the
inserted and deleted magic tables. 
Example: AFTER TRIGGER for UPDATE Event in SQL Server
Now we are going to capture the audit information when an employee is updated from the
Employee table. The following is an example of AFTER TRIGGER for UPDATE event on
Employee table to store the updated employee data on the EmployeeAudit table.
CREATE TRIGGER tr_Employee_For_Update

ON Employee
FOR Update

AS

BEGIN

-- Declare the variables to hold old and updated data

DECLARE @ID INT

DECLARE @Old_Name VARCHAR(200), @New_Name VARCHAR(200)

DECLARE @Old_Salary INT, @New_Salary INT

DECLARE @Old_Gender VARCHAR(200), @New_Gender VARCHAR(200)

DECLARE @Old_DepartmenttId INT, @New_DepartmenttId INT

-- Declare Variable to build the audit string

DECLARE @AuditData VARCHAR(MAX)

-- Store the updated data into a temporary table

SELECT *

INTO #UpdatedDataTempTable

FROM INSERTED

-- Loop thru the records in the UpdatedDataTempTable temp table

WHILE(Exists(SELECT ID FROM #UpdatedDataTempTable))

BEGIN

--Initialize the audit string to empty string

SET @AuditData = ''

-- Select first row data from temp table

SELECT TOP 1 @ID = ID,

@New_Name = Name,

@New_Gender = Gender,
@New_Salary = Salary,

@New_DepartmenttId = DepartmentId

FROM #UpdatedDataTempTable

-- Select the corresponding row from deleted table

SELECT @Old_Name = Name,

@Old_Gender = Gender,

@Old_Salary = Salary,

@Old_DepartmenttId = DepartmentId

FROM DELETED WHERE ID = @ID

-- Build the audit data dynamically

Set @AuditData = 'Employee with Id = ' + CAST(@ID AS VARCHAR(6)) + ' changed'

-- If old name and new name are not same, then its changed

IF(@Old_Name <> @New_Name)

BEGIN

Set @AuditData = @AuditData + ' Name from ' + @Old_Name + ' to ' + @New_Name

END

-- If old Gender and new gender are not same, then its changed

IF(@Old_Gender <> @New_Gender)

BEGIN

Set @AuditData = @AuditData + ' Gender from ' + @Old_Gender + ' to ' + @New_Gender

END

-- If old Salary and new Salary are not same, then its changed

IF(@Old_Salary <> @New_Salary)

BEGIN
Set @AuditData = @AuditData + ' Salary from ' + Cast(@Old_Salary AS VARCHAR(10))
+ ' to '

+ Cast(@New_Salary AS VARCHAR(10))

END

-- If old Department ID and new Department ID are not same, then its changed

IF(@Old_DepartmenttId <> @New_DepartmenttId)

BEGIN

Set @AuditData = @AuditData + ' DepartmentId from ' + Cast(@Old_DepartmenttId AS


VARCHAR(10))+ ' to '

+ Cast(@New_DepartmenttId AS VARCHAR(10))

END

-- Then Insert the audit data into the EmployeeAudit table

INSERT INTO EmployeeAudit(AuditData, AuditDate) VALUES(@AuditData,


GETDATE())

-- Delete the current row from temp table, so we can move to the next row

DELETE FROM #UpdatedDataTempTable WHERE ID = @ID

End

End
Now perform the update operation on the Employee table and you will see everything is
working as expected.
Instead Of Trigger in SQL Server with Examples
1. What is Instead Of Triggers in SQL Server?
2. Example to understand Instead of Triggers in SQL Server
3. INSTEAD OF INSERT trigger in SQL Server
4. Instead Of Update Trigger in SQL Server
5. Instead Of Delete Trigger in SQL Server
What is Instead Of Triggers in SQL Server?
The INSTEAD OF triggers are the DML triggers that are fired instead of the triggering event
such as the INSERT, UPDATE or DELETE events. So, when you fire any DML statements
such as Insert, Update, and Delete, then on behalf of the DML statement, the instead of
trigger is going to execute. In real-time applications, Instead Of Triggers are used to
correctly update a complex view.
Example to understand Instead of Triggers in SQL Server:
We are going to use the following Department and Employee table to understand the
complex views in SQL Server.

Please use the below SQL Script to create and populate the Department and
Employee table with sample data.
-- Create Department Table

CREATE TABLE Department

ID INT PRIMARY KEY,

Name VARCHAR(50)

GO

-- Populate the Department Table with test data


INSERT INTO Department VALUES(1, 'IT')

INSERT INTO Department VALUES(2, 'HR')

INSERT INTO Department VALUES(3, 'Sales')

-- Create Employee Table

CREATE TABLE Employee

ID INT PRIMARY KEY,

Name VARCHAR(50),

Gender VARCHAR(50),

DOB DATETIME,

Salary DECIMAL(18,2),

DeptID INT

GO

-- Populate the Employee Table with test data

INSERT INTO Employee VALUES(1, 'Pranaya', 'Male','1996-02-29 10:53:27.060', 25000,


1)

INSERT INTO Employee VALUES(2, 'Priyanka', 'Female','1995-05-25 10:53:27.060',


30000, 2)

INSERT INTO Employee VALUES(3, 'Anurag', 'Male','1995-04-19 10:53:27.060',40000,


2)

INSERT INTO Employee VALUES(4, 'Preety', 'Female','1996-03-17 10:53:27.060', 35000,


3)

INSERT INTO Employee VALUES(5, 'Sambit', 'Male','1997-01-15 10:53:27.060', 27000,


1)
INSERT INTO Employee VALUES(6, 'Hina', 'Female','1995-07-12 10:53:27.060', 33000,
2)

GO
We want to retrieve the following data from the Employee and Department table.

So, let’s create a view that will return the above results.
CREATE VIEW vwEmployeeDetails

AS

SELECT emp.ID, emp.Name, Gender, Salary, dept.Name AS Department

FROM Employee emp

INNER JOIN Department dept

ON emp.DeptID = dept.ID
Now let’s try to insert a record into the view vwEmployeeDetails by executing the following
query.
INSERT INTO vwEmployeeDetails VALUES(7, ‘Saroj’, ‘Male’, 50000, ‘IT’)
When we execute the above query it gives us the error as ‘View or function
vwEmployeeDetails is not updatable because the modification affects multiple base
tables.‘
How to overcome the above problem?
By using Instead of Insert Trigger.
INSTEAD OF INSERT Trigger in SQL Server:
Here you can see that inserting a record into a view that is based on multiple tables gives
us an error. Now let’s understand how the INSTEAD OF TRIGGERS can help us in
situations like this. As we are getting an error when we are trying to insert a record into the
view, let’s create an INSTEAD OF INSERT trigger on the view vwEmployeeDetails to
correctly insert the records into the appropriate table.
CREATE TRIGGER tr_vwEmployeeDetails_InsteadOfInsert

ON vwEmployeeDetails
INSTEAD OF INSERT

AS

BEGIN

DECLARE @DepartmenttId int

-- First Check if there is a valid DepartmentId in the Department Table

-- for the given Department Name

SELECT @DepartmenttId = dept.ID

FROM Department dept

INNER JOIN INSERTED inst

on inst.Department = dept.Name

--If the DepartmentId is null then throw an error

IF(@DepartmenttId is null)

BEGIN

RAISERROR('Invalid Department Name. Statement terminated', 16, 1)

RETURN

END

-- Finally insert the data into the Employee table

INSERT INTO Employee(ID, Name, Gender, Salary, DeptID)

SELECT ID, Name, Gender, Salary, @DepartmenttId

FROM INSERTED

End
Now, let’s execute the below Insert statement.
INSERT INTO vwEmployeeDetails VALUES(7, ‘Saroj’, ‘Male’, 50000, ‘IT’)
Instead Of Trigger inserts the row correctly into the Employee table as expected. Since we
are inserting a row, the inserted magic table will contain the newly added row whereas the
deleted table will be empty. Now check the data by issuing a select query against the
Employee Table or the vwEmployeeDetails view.
SELECT * FROM vwEmployeeDetails will give us the below result.

As you can see from the above image, the record is inserted as expected into the Employee
table.
Instead Of Update Trigger in SQL Server:
The INSTEAD OF UPDATE Trigger in SQL server gets fired instead of the UPDATE event
on a table or a view. For example, let’s say we have an INSTEAD OF UPDATE trigger on a
view or on a table, and when we try to update a record (or records) from that view or table,
instead of the actual UPDATE event, the trigger gets fired automatically. The Instead Of
Update Trigger in SQL Server is usually used to correctly update the records from a
view that is based on multiple tables.
Please update the Department and Employee table as shown below to understand this
concept.

We want to retrieve the following data from the Employee and Department table.
So, let’s create a view that will return the above results.
CREATE VIEW vwEmployeeDetails

AS

SELECT emp.ID, emp.Name, Gender, Salary, dept.Name AS Department

FROM Employee emp

INNER JOIN Department dept

ON emp.DeptID = dept.ID
Now let’s try to update the view vwEmployeeDetails in such a way that it affects both the
underlying tables such as Employee and Department table and see if we get any error.
The following UPDATE statement changes the Name and Salary column from the
Employee table and the Department Name column from the Department table.
UPDATE vwEmployeeDetails

SET Name = 'Kumar',

Salary = 45000,

Department = 'HR'

WHERE Id = 1
When we execute the above update query, we get the error as “View or function
‘vwEmployeeDetails’ is not updatable because the modification affects multiple base
tables.”
Note: When the view is based on multiple tables and if the update statement affects more
than one table then the update failed.
Now let’s try to change only the department of Pranaya from IT to HR. The following
UPDATE query affects only one base table that is the Department table. So, the update
query should succeed. But before executing the query please note that employee Sambit is
also in the IT department.
UPDATE vwEmployeeDetails

SET Department = 'HR'

WHERE Id = 1
Once we execute the above query, then select the data from the view and notice
that Sambit’s Department is also changed to HR.
SELECT * FROM vwEmployeeDetails
We intended to just change Pranaya’s Department Name but it also changes the
Department Name of Sambit. So the UPDATE didn’t work as expected. This is because the
UPDATE query updated the Department Name from IT to HR in the Department table.
SELECT * FROM Department

As you can see the Record with Id = 1, has the Department Name changed from IT to
HR. So, the conclusion is that, if a view is based on multiple tables, and if we want to
update the view, the UPDATE may not always work as expected. To correctly update the
underlying base tables, through a view, the Instead Of Update Trigger in SQL Server can be
used.
Before we create the trigger, let’s update the Department Name to IT for record with Id = 1.
UPDATE Department SET Name = ‘IT’ WHERE ID = 1
Please use the below SQL Script to create the Instead Of Update Trigger in SQL Server
CREATE TRIGGER tr_vwEmployeeDetails_InsteadOfUpdate

ON vwEmployeeDetails

INSTEAD OF UPDATE

AS

BEGIN

-- if EmployeeId is updated

IF(UPDATE(ID))

BEGIN

RAISERROR('Id cannot be changed', 16, 1)

RETURN
END

-- If Department Name is updated

IF(UPDATE(Department))

BEGIN

DECLARE @DepartmentID INT

SELECT @DepartmentID = dept.ID

FROM Department dept

INNER JOIN INSERTED inst

ON dept.Name = inst.Department

IF(@DepartmentID is NULL )

BEGIN

RAISERROR('Invalid Department Name', 16, 1)

RETURN

END

UPDATE Employee set DeptID = @DepartmentID

FROM INSERTED

INNER JOIN Employee

on Employee.ID = inserted.ID

End

-- If gender is updated

IF(UPDATE(Gender))

BEGIN

UPDATE Employee SET Gender = inserted.Gender

FROM INSERTED
INNER JOIN Employee

ON Employee.ID = INSERTED.ID

END

-- If Salary is updated

IF(UPDATE(Salary))

BEGIN

UPDATE Employee SET Salary = inserted.Salary

FROM INSERTED

INNER JOIN Employee

ON Employee.ID = INSERTED.ID

END

-- If Name is updated

IF(UPDATE(Name))

BEGIN

UPDATE Employee SET Name = inserted.Name

FROM INSERTED

INNER JOIN Employee

ON Employee.ID = INSERTED.ID

END

END
Note: The Update() function used in the trigger returns true, even if we update with the
same value. For this reason, I recommended comparing values between inserted and
deleted tables, rather than relying on the Update() function. The Update() function does not
operate on a per-row basis but across all rows.
Now, let’s try to update Pranaya’s Department to HR. 
UPDATE vwEmployeeDetails SET Department = ‘HR’ WHERE Id = 1
The UPDATE query works as expected. The INSTEAD OF UPDATE trigger, correctly
updates, Pranaya’s DeptId to 2 in Employee table.
Now, let’s try to update Name, Gender, Salary and Department Name. The UPDATE query,
works as expected, without raising the error – ‘View or function vwEmployeeDetails is
not updatable because the modification affects multiple base tables.‘
UPDATE vwEmployeeDetails

SET Name = 'Preety',

Gender = 'Female',

Salary = 44000,

Department = 'IT'

WHERE Id = 1
Instead Of Delete Trigger in SQL Server:
The INSTEAD OF DELETE Trigger in SQL server gets fired instead of the DELETE event
on a table or a view. For example, let’s say we have an INSTEAD OF DELETE trigger on a
view or on a table, and when we try to delete a row from that view or table, instead of the
actual DELETE event, the trigger gets fired automatically. INSTEAD OF DELETE
TRIGGERS are usually used to delete the records from a view that is based on multiple
tables.
Please update the Department and Employee table as shown below to understand this
concept.

We want to retrieve the following data from the Employee and Department table.
So, let’s create a view that will return the above results.
CREATE VIEW vwEmployeeDetails

AS

SELECT emp.ID, emp.Name, Gender, Salary, dept.Name AS Department

FROM Employee emp

INNER JOIN Department dept

ON emp.DeptID = dept.ID
Now let’s try to delete a record from the view vwEmployeeDetails by executing the following
query.
DELETE FROM vwEmployeeDetails WHERE ID = 1
When we execute the above query it gives us the error as ‘View or function
vwEmployeeDetails is not updatable because the modification affects multiple base
tables.‘
Here we can see that deleting a record from a view that is based on multiple tables gives us
an error. Now let’s understand how the INSTEAD OF TRIGGERS can help us in a situation
like this. As we are getting an error when we are trying to Delete a record from the view,
let’s create an INSTEAD OF DELETE trigger on the view vwEmployeeDetails to correctly
delete the data.
CREATE TRIGGER tr_vwEmployeeDetails_InsteadOfDelete

ON vwEmployeeDetails

INSTEAD OF DELETE

AS

BEGIN

-- Using Inner Join

DELETE FROM Employee

FROM Employee emp

INNER JOIN DELETED del

ON emp.ID = del.ID

-- Using the Subquery

-- DELETE FROM Employee


-- WHERE ID IN (SELECT ID FROM DELETED)

END
Now, let’s execute the below delete statement.
DELETE FROM vwEmployeeDetails WHERE ID = 1
The Instead Of Trigger deletes the row correctly from the Employee table as expected.
Since we are deleting a row, the deleted magic table will contain all the rows that we want to
delete whereas the inserted table will be empty. Now check the data by issuing a select
query against the Employee Table or the vwEmployeeDetails view.
SELECT * FROM vwEmployeeDetails will give us the below result.

As you can see from the above image, the record is deleted as expected from the
Employee table.
DDL Triggers in SQL Server with Examples
1. What are DDL TRIGGERS in SQL Server?
2. Types of DDL triggers?
3. Database Scoped DDL Triggers in SQL Server
4. How to Create a Database-Scoped DDL Trigger?
5. Where can I find the Database-scoped DDL triggers?
6. How to drop, enable and disable a Database Scoped DDL trigger?
7. Server-scoped DDL Triggers in SQL Server
8. How to Create a Server-Scoped DDL Trigger?
9. Where can I find the Server-scoped DDL triggers?
10. How to drop, enable and disable a Server Scoped DDL trigger?
11. A real-time example of DDL Trigger
What are DDL TRIGGERS in SQL Server?
The DDL triggers in SQL Server are fired in response to a variety of data definition
language (DDL) events such as Create, Alter, Drop, Grant, Denay, and Revoke (Table,
Function, Index, Stored Procedure, etc…). That means DDL triggers in SQL Server are
working on a database.
DDL triggers are introduced from SQL Server 2005 version which will be used to restrict the
DDL operations such as CREATE, ALTER and DROP commands.
We can think of a DDL trigger as a special kind of stored procedure that executes in
response to a server scoped or database scoped events. We will discuss the examples of
both server scoped and database scoped.
The point to remember is that the DDL triggers fire only after the DDL statements execute
so we cannot use the “Instead Of Triggers” here and moreover the DDL triggers will not fire
in response to events that affect the local temporary tables.
Syntax:

Here Event_Type refers to the event that will fire the trigger which can be anything
like Create_Table, Drop_Table, Alter_Table, etc.
Types of DDL triggers in SQL Server?
There are two types of DDLs triggers available in SQL Server. They are as follows:
1. Database Scoped DDL Trigger
2. Server Scoped DDL Trigger
The DDL triggers can be created in a specific database or at the server level. If we set the
scope to server-level then it is applied to all the databases of that server. Here, in this
article, first, we will discuss the database scoped triggers, and then, we will discuss server
scoped triggers.
Database Scoped DDL Triggers in SQL Server
First Create a database with the name SQL_TESTING_DB
Example1: Create a trigger that will restrict creating a new table on a specific
database.
USE SQL_TESTING_DB

GO

CREATE TRIGGER trRestrictCreateTable

ON DATABASE

FOR CREATE_TABLE

AS

BEGIN

PRINT 'YOU CANNOT CREATE A TABLE IN THIS DATABASE'

ROLLBACK TRANSACTION

END
Where can I find the Database-Scoped DDL triggers?
In the Object Explorer window expand the SQL_TESTING_DB database by clicking on the
plus symbol. Expand the Programmability folder. Then Expand the Database
Triggers folder as shown below
Note: If you can’t find the trigger that you just created in the SQL_TESTING_DB database,
make sure to refresh the Database Triggers folder. When you execute the following code to
create a table, the trigger will automatically fire and will print the message – YOU CANNOT
CREATE A TABLE IN THIS DATABASE
CREATE TABLE tblTest (ID INT)
Example2: Create a trigger that will restrict ALTER operations on a specific database table.
CREATE TRIGGER trRestrictAlterTable

ON DATABASE

FOR ALTER_TABLE

AS

BEGIN

PRINT 'YOU CANNOT ALTER TABLES'

ROLLBACK TRANSACTION

END
Example3: Create a trigger that will restrict dropping the tables from a specific
database.
CREATE TRIGGER trRestrictDropTable

ON DATABASE

FOR DROP_TABLE
AS

BEGIN

PRINT 'YOU CANNOT DROP TABLES'

ROLLBACK TRANSACTION

END
Note: We cannot implement business logic in DDL Trigger. To be able to create, alter or
drop a table we either have to disable or delete the trigger.
How to drop a Database Scoped DDL trigger in SQL Server?
Right-click on the trigger in object explorer and select “Delete” from the context menu. We
can also drop the trigger using the following T-SQL command
DROP TRIGGER trRestrictCreateTable ON DATABASE

DROP TRIGGER trRestrictAlterTable ON DATABASE

DROP TRIGGER trRestrictDropTable ON DATABASE


Let us see an example of how to prevent users from creating, altering, or dropping tables
from a specific database using a single trigger.
CREATE TRIGGER trRestrictDDLEvents

ON DATABASE

FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE

AS

BEGIN

PRINT 'You cannot create, alter or drop a table'

ROLLBACK TRANSACTION

END
How to disable a Database Scoped DDL trigger in SQL Server?
Right-click on the trigger in object explorer and select “Disable” from the context menu. We
can also disable the trigger using the following T-SQL command
DISABLE TRIGGER trRestrictDDLEvents ON DATABASE
How to enable a Database Scoped DDL trigger in SQL Server?
Right-click on the trigger in object explorer and select “Enable” from the context menu. We
can also enable the trigger using the following T-SQL command
ENABLE TRIGGER trRestrictDDLEvents ON DATABASE
Certain system stored procedures that perform DDL-like operations can also fire DDL
triggers. The following trigger will be fired whenever we rename a database object
using the sp_rename system stored procedure.
CREATE TRIGGER trRenameTable

ON DATABASE

FOR RENAME

AS

BEGIN

PRINT 'You just renamed something'

END
Let’s create a table and test this.
First, disable the trRestrictDDLEvents trigger: DISABLE TRIGGER trRestrictDDLEvents
ON DATABASE
Then create a table using the command: CREATE TABLE tblTest (ID INT)
The following code changes the name of the table tblTest to tblTestChanged. When this
code is executed, it will fire the trigger trRenameTable automatically.
sp_rename ‘tblTest’, ‘tblTestChanged’
When we execute the above code, it will display the below output.

Server-scoped DDL Triggers in SQL Server:


Let’s understand the need for a Server-Scoped DDL Trigger with an example. We already
created the following trigger.
CREATE TRIGGER trRestrictDDLEvents

ON DATABASE

FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE

AS

BEGIN

PRINT 'You cannot create, alter or drop a table'

ROLLBACK TRANSACTION

END
The above trigger is an example of a Database Scoped DDL Trigger. This trigger will
prevent the users from creating, altering, or dropping tables only from the database on
which it is created. 
But, if we have another database on the same server, then the users will be able to create,
alter or drop tables in that database. So, if we want to prevent the users from creating,
altering, or dropping tables from that database then we need to create the trigger again in
that particular database.
Think of a situation, where we have 50 databases on a particular server and we want to
prevent the users from creating, altering, or dropping tables from all those 50 databases.
Creating the same trigger again and again for all those 50 databases is bad for the following
two reasons.
1. It is a tedious process as well as error-prone
2. Maintainability is a nightmare. If for some reason we have to change the trigger, then
we will have to do it in all the 50 databases, which again is a tedious process as well as
error-prone.
This is the ideal situation where the Server-Scoped DDL triggers come into the picture.
When we create a server-scoped DDL trigger, then it will be fired in response to the DDL
events happening in all of the databases on that particular server.
How to Create a Server-Scoped DDL Trigger in SQL Server?
Creating a server scoped DDL trigger in SQL Server is very much similar to creating a
database scoped DDL trigger, except that we will have to change the scope to ALL Server
as shown in the below example.
CREATE TRIGGER trServerScopedDDLTrigger

ON ALL SERVER

FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE

AS

BEGIN

PRINT 'You cannot create, alter or drop a table in any database of this server'

ROLLBACK TRANSACTION

END
Now if you will try to create, alter or drop a table in any of the databases on that particular
server, then the above Server-Scoped DDL trigger will be fired.
Where can I find the Server-scoped DDL triggers?
To find the Server-Scoped DDL Triggers in SQL Server Follow the below steps
1. In the Object Explorer window, expand the “Server Objects” folder
2. Then Expand the Triggers folder as shown in the below image
How to disable Server-Scoped DDL trigger in SQL Server?
Right-click on the trigger in object explorer and select “Disable” from the context menu. We
can also disable the trigger using the SQL Command
as DISABLE TRIGGER trServerScopedDDLTrigger ON ALL SERVER
How to enable Server-Scoped DDL trigger in SQL Server?
Right-click on the trigger in object explorer and select “Enable” from the context menu. We
can also enable the trigger using the T-SQL command
as ENABLE TRIGGER trServerScopedDDLTrigger ON ALL SERVER 
How to drop Server-scoped DDL trigger in SQL Server?
Right-click on the trigger in object explorer and select “Delete” from the context menu. We
can also drop the trigger using the SQL command
as DROP TRIGGER trServerScopedDDLTrigger ON ALL SERVER
DDL Trigger Real-Time Example in SQL Server:
The DDL triggers in SQL Server will be very much handy to audit and control the DDL
changes in a database. Below are such real-time scenarios:
1. To Track the DLL changes
2. Track the DDL statement which is fired
3. Who has fired the DDL statements? For example, we may be interested in identifying
who has dropped the table or who has modified the table.
4. When the DDL statement is fired.
5. Block the user from doing some DDL changes like DROP TABLE, DROP
PROCEDURE, etc.
6. Allow the DDL changes only during a specified window (i.e. only during particular
hours of the day)
So let us discuss how to audit table changes in SQL Server using a DDL
trigger.
-- Create the TableAudit table
CREATE TABLE TableAudit

DatabaseName nvarchar(250),

TableName nvarchar(250),

EventType nvarchar(250),

LoginName nvarchar(250),

SQLCommand nvarchar(2500),

AuditDateTime datetime

Go

-- The following trigger audits all table changes in all databases on a particular Server

CREATE TRIGGER tr_AuditTableChangesInAllDatabases

ON ALL SERVER

FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE

AS

BEGIN

DECLARE @EventData XML

SELECT @EventData = EVENTDATA()

INSERT INTO SQL_TESTING_DB.dbo.TableAudit

(DatabaseName, TableName, EventType, LoginName,

SQLCommand, AuditDateTime)

VALUES

@EventData.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'varchar(250)'),
@EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'varchar(250)'),

@EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(250)'),

@EventData.value('(/EVENT_INSTANCE/LoginName)[1]', 'varchar(250)'),

@EventData.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(2500)'),

GetDate()

END
In the above example, we are using the EventData() function which will returns event the
data in XML format. The following XML is returned by the EventData() function when I
created a table with name = MyTestTable in SQL_TESTING_DB database.
CREATE TABLE MyTestTable

Id INT,

Name VARCHAR(50),

Gender VARCHAR(50),

Salary INT

)
Once you create the above table then select the TableAudit as shown below
SELECT * FROM TableAudit will give us the below output.

The EVENTDATA() function give us the data in below XML Format


<EVENT_INSTANCE>

<EventType>CREATE_TABLE</EventType>

<PostTime> 2018-10-10 22:05:37.453 </PostTime>

<SPID>58</SPID>

<ServerName> LAPTOP-2HN3PT8T </ServerName>


<LoginName>LAPTOP-2HN3PT8T\Pranaya</LoginName>

<UserName>dbo</UserName>

<DatabaseName>SQL_TESTING_DB</DatabaseName>

<SchemaName>dbo</SchemaName>

<ObjectName>MyTestTable</ObjectName>

<ObjectType>TABLE</ObjectType>

<TSQLCommand>

<SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON"

ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON"

ENCRYPTED="FALSE" />

<CommandText>

CREATE TABLE MyTestTable

Id INT,

Name VARCHAR(50),

Gender VARCHAR(50),

Salary INT

</CommandText>

</TSQLCommand>

</EVENT_INSTANCE>
Views in SQL Server with Examples
1. What is a View?
2. What are the differences between a table and a view?
3. Examples to understand Views in SQL Server
4. How many types of views are there in SQL Server?
5. What is a simple view or an Updatable view?
6. What is a complex view?
7. Can we drop a table that has dependent views on it?
8. Can we create a view based on other views?
9. Can we update the views?
What is a View in SQL Server?
The views in SQL Server are nothing more than a compiled SQL query. We can also
consider the Views as virtual tables. As a virtual table, the Views do not store any data
physically by default. But if you want then you can change this default behavior that we will
discuss in our Indexed Views in SQL Server article. So when we query a view it actually
gets the data from the underlying database tables as shown in the below image.

Simply we can say that the views in SQL Server act as an interface between the Table(s)
and the user.
What are the differences between a table and a view in SQL Server?
When we compared a view with a table we have the following differences.
1. The table is physical whereas the view is logical or virtual.
2. A Table is an independent object whereas a view is a dependent object that is a view
depends on the table or tables from which it is loading the data.
3. When a new table is created from an existing table the new and old tables are
independent themselves that is the changes of one table will not be reflected into the
other table whereas if a view is created based on a table any changes that are
performed on the table reflects into the view and any changes performed on the view
reflected in the table also.
Examples to understand Views in SQL Server
We are going to use the following Department and Employee table to understand the Views
in SQL Server.
Please use the below SQL Script to create and populate the Department and
Employee table with sample data.
-- Create Department Table

CREATE TABLE Department

ID INT PRIMARY KEY,

Name VARCHAR(50)

GO

-- Populate the Department Table with test data

INSERT INTO Department VALUES(1, 'IT')

INSERT INTO Department VALUES(2, 'HR')

INSERT INTO Department VALUES(3, 'Sales')

-- Create Employee Table

CREATE TABLE Employee

ID INT PRIMARY KEY,

Name VARCHAR(50),
Gender VARCHAR(50),

DOB DATETIME,

DeptID INT

GO

-- Populate the Employee Table with test data

INSERT INTO Employee VALUES(1, 'Pranaya', 'Male','1996-02-29 10:53:27.060', 1)

INSERT INTO Employee VALUES(2, 'Priyanka', 'Female','1995-05-25 10:53:27.060', 2)

INSERT INTO Employee VALUES(3, 'Anurag', 'Male','1995-04-19 10:53:27.060', 2)

INSERT INTO Employee VALUES(4, 'Preety', 'Female','1996-03-17 10:53:27.060', 3)

INSERT INTO Employee VALUES(5, 'Sambit', 'Male','1997-01-15 10:53:27.060', 1)

INSERT INTO Employee VALUES(6, 'Hina', 'Female','1995-07-12 10:53:27.060', 2)

GO
The syntax for creating a View in SQL Server: 
The View will be created by using the CREATE View ViewName statement followed by the
select statement as shown in the below image.

How many types of views are there in SQL Server?


There are two types of views in SQL Server, they are
1. Simple Views
2. Complex Views
What is a simple view or an Updatable view in SQL Server?
The view that is created based on the columns of a single table, then it is known as a simple
view. We can perform all the DML operations on a simple view. This is the reason why a
simple view can also be called an updatable view or dynamic view.
Let us understand the simple view in SQL with an example. Please have a look at the below
SQL statements which creates two simple views. Further, if you notice, both the Views are
created based on a single table i.e. Employee. In the first view i.e. vwAllEmployees1, we
are retrieving all the columns of the Employee table by using * and in the second view i.e.
vwAllEmployees2, we are specifying the column names explicitly.
-- View with all columns

CREATE VIEW vwAllEmployees1

AS

SELECT * FROM Employee

-- View with specific columns

CREATE VIEW vwAllEmployees2

AS

SELECT ID, Name, Gender, DOB, DeptID

FROM Employee
The above two views are examples of Simple or Updatable Views in SQL Server. And, we
can perform all the DML operations (INSERT, UPDATE, and DELETE) on the above views
including the SELECT operations.
DML Operations on the Simple Views in SQL Server:
Let us see how to perform the SELECT, INSERT, UPDATE, and DELETE operations on the
above-created simple views i.e. either vwAllEmployees1 or vwAllEmployees2. To select
data from a view, we need to use the SELECT statement, in the same way, we used to
select the data from a table as shown below.
SELECT * from vwAllEmployees1
Once you execute the above query, you will get the following output.

Insert Operation on Simple Views in SQL Server


On a simple view in SQL Server, we can perform the INSERT operation. Let us see this
with an example. Please execute the following INSERT statement to Insert an employee
into the Employee table using the vwAllEmployees1view.
INSERT INTO vwAllEmployees1 (ID, Name, Gender, DOB, DeptID) VALUES(7, ‘Rohit’,
‘Male’, ‘1995-04-19 10:53:27.060’, 3)
When you execute the above insert statement, it will successfully insert the record into the
Employee table. To check issue a select query against the Employee table as shown below/
SELECT * FROM Employee 
Once you execute the above query, you will get the following output.

As you can see in the above image, the new record is inserted into the Employee table.
Update Operation on View:
On a simple view, we can also perform the UPDATE operation. Let us understand this with
an example. Let’s try to update the details of the employee whose id is 7 using the
vwAllEmployees1 view. Please execute the following UPDATE statement which will
UPDATE employee details using the view. 
UPDATE vwAllEmployees1 SET

Name = 'Rohit Kumar',

DOB = '1996-02-29 10:53:27.060',

DeptID = 1

WHERE ID = 7
Once you execute the above UPDATE SQL Query, it will update the data in the Employee
table. To check whether the data is updated successfully or not make a select query against
the Employee table as shown below.
SELECT * FROM Employee 
Once you execute the above SELECT statement, you will get the following output.
As you can see the Employee whose ID is 7 is successfully updated as expected.
Delete Operation on View:
On a simple view, it is also possible to perform the UPDATE DML operation. Let us
understand this with an example. Let’s try to Delete the Employee whose ID is 7 using the
vwAllEmployees1 view. Please execute the following DELETE statement which will
DELETE the employee record from the table Employee.  
DELETE FROM vwAllEmployees1 where ID = 7
Once we execute the above delete operation then check the Employee table and you will
see that the Employee whose ID is 7 is deleted as expected from the Employee table. So
this proofs that we can perform ALL the DML operations on a Simple View which is also
called Updatable View in SQL Server.
What is a complex view in SQL Server?
When we create a view based on more than 1 table then it is known as a complex view and
on a complex view, we may or may not perform the DML (INSERT, UPDATE, and DELETE)
operations and more ever the complex view may not update the data correctly on the
underlying database tables.
Creating Complex Views
Please have a look at the following code. Here, we created the view by using joining the
Employee and Department tables, and hence it is a complex view. Further, if you notice,
here we are selecting the columns from both tables.
CREATE VIEW vwAllEmployees3

AS

SELECT emp.ID, emp.Name, emp.Gender, emp.DOB,

dep.Name as DepartmentName

FROM Employee emp

INNER JOIN Department dep


on emp.DeptID = dep.ID
A view that is created based on a single table will also be considered as a complex view
provided if the query contains Distinct. Aggregate Function, Group By Clause, having
Clause, calculated columns, and set operations. For better understanding, please have a
look at the following query. Here, we are using a single table as well as we are also using
the Count aggregate function which makes the view a complex view and also restricts us to
perform any DML operations.
CREATE VIEW vwAllEmployees4

AS

SELECT Gender, Count(*) as TotalEmployee

FROM Employee Group BY Gender


In our upcoming article, we will discuss Complex Views in SQL Server in detail with Real-
time Examples.
Can we drop a table that has dependent views on it?
Yes, you can drop a table even if any dependent views are associated with it, but the views
that are associated with it will not be dropped. They will still execute in the database only
with the status as inactive object and all those views become active and start functioning
provided the table is recreated.
Can we create a view based on other views?
Yes, It is possible in SQL Server to create a view based on other views. 
Can we update the views in SQL Server?
Yes, in SQL server views can be updated. However, updating a view that is based on
multiple tables, may not update the base tables correctly. To correctly update a view that is
based on multiple tables we can make use of INSTEAD OF triggers in SQL Server.
Advantages and Disadvantages of Views in SQL
Server
1. Advantages of views in SQL Server
2. Hiding the complexity of a Complex SQL Query
3. Implementing Row Level Security
4. Implementing Column Level Security
5. Presenting the Aggregated data by Hiding Detailed data
6. Limitations and Dis-Advantages of Views in SQL Server
7. We cannot pass parameters to SQL Server views
8. Cannot use Order By clause with views without specifying FOR XML, OFFSET, or
TOP
9. The Views cannot be created based on Temporary Tables in SQL Server
10. We cannot associate Rules and Defaults with SQL Server views
Let us understand the Advantages and Disadvantages of Views in SQL
Server.
We are going to use the following Department and Employee table to understand the
advantages of using views in SQL Server.

Please use below the SQL Script to create and populate the Department and
Employee table with required sample data.
-- Create Department Table

CREATE TABLE Department

ID INT PRIMARY KEY,


Name VARCHAR(50)

GO

-- Populate the Department Table with test data

INSERT INTO Department VALUES(1, 'IT')

INSERT INTO Department VALUES(2, 'HR')

INSERT INTO Department VALUES(3, 'Sales')

-- Create Employee Table

CREATE TABLE Employee

ID INT PRIMARY KEY,

Name VARCHAR(50),

Gender VARCHAR(50),

DOB DATETIME,

Salary DECIMAL(18,2),

DeptID INT

GO

-- Populate the Employee Table with test data

INSERT INTO Employee VALUES(1, 'Pranaya', 'Male','1996-02-29 10:53:27.060', 25000,


1)

INSERT INTO Employee VALUES(2, 'Priyanka', 'Female','1995-05-25 10:53:27.060',


30000, 2)

INSERT INTO Employee VALUES(3, 'Anurag', 'Male','1995-04-19 10:53:27.060',40000,


2)
INSERT INTO Employee VALUES(4, 'Preety', 'Female','1996-03-17 10:53:27.060', 35000,
3)

INSERT INTO Employee VALUES(5, 'Sambit', 'Male','1997-01-15 10:53:27.060', 27000,


1)

INSERT INTO Employee VALUES(6, 'Hina', 'Female','1995-07-12 10:53:27.060', 33000,


2)

GO
Advantages of Views in SQL Server
We are getting the following advantages of using Views in SQL Server.
1. Hiding the complexity
2. Implementing Row and Column Level Security.
3. Presenting the aggregated data by hiding the detailed data.
Hiding the Complexity of a Complex SQL Query using Views in SQL Server
In SQL Server, we can use the Views to reduce the complexity of the database
schema for non-IT users. Let’s create a view that will retrieve the data from both the
Department and Employee table as shown below.
CREATE VIEW vwEmployeesByDepartment

AS

SELECT emp.ID,

emp.Name,

emp.Salary,

emp.Gender,

dept.Name AS DepartmentName

FROM Employee emp

INNER JOIN Department dept

ON emp.DeptID = dept.ID
The above vwEmployeesByDepartment view hides the complexity of SQL Server Joins.
Now, the Non-IT users find this view easy to query rather than writing the complex joins.
Implementing Row Level Security using Views in SQL Server:
Let us see how to implement Row Level Security using Views in SQL Server. For example,
you want an end-user to access only the IT Department employees. If you grant access to
the underlying Employee and Department tables, then the end-user will be able to see the
information of all the department employees. To restrict this, what you can do is, you will
create a view, which will return only the IT Department employees, and grant the end-user
to access the view rather than the underlying database tables. Let’s create a view that only
returns the IT department employees by joining Employee and Department table by
executing the below query.
CREATE VIEW vwITDepartmentEmployees

AS

SELECT emp.ID,

emp.Name,

emp.Salary,

emp.Gender,

dept.Name AS DepartmentName

FROM Employee emp

INNER JOIN Department dept

ON emp.DeptID = dept.ID

WHERE dept.Name = 'IT'


Now execute the vwITDepartmentEmployees as shown below. 
SELECT * FROM vwITDepartmentEmployees
Once you execute the above SELECT statement, it will only display the IT Department
employees as shown in the below image.

Implementing Column Level Security using Views in SQL Server:


Let us understand how to implement Column Level Security using Views in SQL Server. As
you know the Salary is confidential information for any organization and I want to prevent
access to that column. If we provide access to the Employee and Department table, then
the user can view the salary of any employee. To restrict this, what we can do is, we will
create a view, which excludes the Salary column and then grant the end-user access to the
view rather than the database tables. Let’s create a view that will return all columns except
the Salary column by executing the below SQL statements.
CREATE VIEW vwEmployeesByDept

AS

SELECT emp.ID,

emp.Name,

emp.Gender,

DOB,

dept.Name AS DepartmentName

FROM Employee emp

INNER JOIN Department dept

ON emp.DeptID = dept.ID
Now execute the vwITDepartmentEmployees as shown below. 
SELECT * FROM vwEmployeesByDept
Once you execute the above SELECT statement, it will only display all the columns data
except the salary column as shown in the below image.

Presenting the Aggregated data by Hiding Detailed data using Views in SQL
Server:
The Views in SQL Server can also be used to present only the aggregated data and hide
the detailed data. Let’s create a view that will return the total number of employees by
department.
CREATE VIEW vwEmployeesCountByDepartment

AS

SELECT dept.Name AS DepartmentName,


COUNT(*) AS TotalEmployees

FROM Employee emp

INNER JOIN Department dept

ON emp.DeptID = dept.ID

GROUP By dept.Name
Now execute the vwEmployeesCountByDepartment as shown below. 
SELECT * FROM vwEmployeesCountByDepartment
Once you execute the above SELECT statement, it will display only the aggregated data as
shown in the below image.

So, in short, We Need Views in SQL Server


1. To protect the data. If we have a table containing sensitive data in certain columns,
we might wish to hide those columns from certain groups of users. For instance,
customer names, addresses, and social security numbers might all be stored in the
same table; however, for lower-level employees like shipping clerks, you can create
a view that only displays customer names and addresses. You can grant
permissions to a view without allowing users to query the original tables.
2. A view is a logical table but what it stores internally is a select statement that is used
for creating the view. So that whenever a user performs an operation on the view like
select, insert, update or delete internally the view performs those operations on a
table.
3. Simply we can say that view will act as an interface between the data provider
(Table) and the User.
4. The view is created based on a table, any changes that are performed on the table
reflect into the view any changes performed on the view reflect on the table also.
Limitations and Dis-Advantages of Views in SQL Server:
Let us discuss the limitations and Disadvantages of views in SQL Server. We are going to
use the following Employee table to understand the limitations and Disadvantages of views
in SQL Server.
Please use the below SQL Script to create and populate the Employee table with
sample data.
-- Create Employee Table

CREATE TABLE Employee

ID INT PRIMARY KEY,

Name VARCHAR(50),

Gender VARCHAR(50),

DOB DATETIME,

Salary DECIMAL(18,2),

DeptID INT

GO

-- Populate the Employee Table with test data

INSERT INTO Employee VALUES(1, 'Pranaya', 'Male','1996-02-29 10:53:27.060', 25000,


1)

INSERT INTO Employee VALUES(2, 'Priyanka', 'Female','1995-05-25 10:53:27.060',


30000, 2)
INSERT INTO Employee VALUES(3, 'Anurag', 'Male','1995-04-19 10:53:27.060',40000,
2)

INSERT INTO Employee VALUES(4, 'Preety', 'Female','1996-03-17 10:53:27.060', 35000,


3)

INSERT INTO Employee VALUES(5, 'Sambit', 'Male','1997-01-15 10:53:27.060', 27000,


1)

INSERT INTO Employee VALUES(6, 'Hina', 'Female','1995-07-12 10:53:27.060', 33000,


2)

GO
Limitations of Views in SQL Server:
In SQL Server, it is not possible to pass parameters to a view. The Inline Table-Valued
Functions in SQL Server are an excellent replacement for the parameterized views.
-- Error: Cannot pass Parameters to a View

CREATE VIEW vwEmployeeDetailsByGender

@Gender varchar(20)

AS

SELECT Id, Name, Gender, DOB, Salary, DeptID

FROM Employee

WHERE Gender = @Gender


The Table-Valued functions in SQL Server can be used as a replacement for parameterized
views.
CREATE FUNCTION fnEmployeeDetailsByGender

@Gender VARCHAR(20)

RETURNS Table

AS
RETURN

(SELECT Id, Name, Gender, DOB, Salary, DeptID

FROM Employee

WHERE Gender = @Gender)


Now you can call the function from a select class as shown below.
SELECT * FROM dbo.fnEmployeeDetailsByGender(‘Male’)
Once you execute the above function you will get the following output.

The ORDER BY clause is invalid in views unless TOP, OFFSET, or FOR XML is also
specified.
CREATE VIEW vwEmployeeDetailsSortedByName

AS

SELECT Id, Name, Gender, DOB, Salary, DeptID

FROM Employee

ORDER BY Name
If we use the ORDER BY clause, we will get an error stating – The ORDER BY clause is
invalid in views, inline functions, derived tables, subqueries, and common table
expressions unless TOP, OFFSET, or FOR XML is also specified.
Let’s see how to use TOP Clause to support Order By clause in SQL Server.
CREATE VIEW vwEmployeeDetailsSortedByName

AS

SELECT TOP 100 PERCENT Id, Name, Gender, DOB, Salary, DeptID

FROM Employee

ORDER BY Name
To learn more about TOP along with PERCENT please find the below article.
https://dotnettutorials.net/lesson/top-n-clause-sql-server/
Views cannot be created based on temporary tables.
Let’s see an example to understand this.
Create Table ##TestTempTable(Id int, Name nvarchar(20), Gender nvarchar(10))

Insert into ##TestTempTable values(101, ABC, 'Male')

Insert into ##TestTempTable values(102, PQR, 'Female')

Insert into ##TestTempTable values(103, XYZ, 'Female')

-- Error: Cannot create a view on Temp Tables

Create View vwOnTempTable

as

Select Id, Name, Gender

from ##TestTempTable
So, in short, the following are the limitations and Dis-Advantages of Views in
SQL Server
1. We cannot pass parameters to SQL Server views
2. We cannot use an Order By clause with views without specifying FOR XML,
OFFSET, or TOP
3. The Views cannot be created based on Temporary Tables in SQL Server
4. We cannot associate Rules and Defaults with SQL Server views
Complex Views in SQL Server with Examples
What is a complex view in SQL Server?
When the view is created based on multiple tables then it is known as a complex view in
SQL Server. The most important point that we need to remember is, on a complex view in
SQL Server, we may or may not perform the DML operations and more ever the complex
view may not update the data correctly on the underlying database tables.
Example: Complex Views in SQL Server:
We are going to use the following Department and Employee table to understand the
complex views in SQL Server.

Please use the below SQL Script to create and populate the Department and Employee
table with sample data.
-- Create Department Table

CREATE TABLE Department

ID INT PRIMARY KEY,

Name VARCHAR(50)

GO

-- Populate the Department Table with test data

INSERT INTO Department VALUES(1, 'IT')

INSERT INTO Department VALUES(2, 'HR')


INSERT INTO Department VALUES(3, 'Sales')

-- Create Employee Table

CREATE TABLE Employee

ID INT PRIMARY KEY,

Name VARCHAR(50),

Gender VARCHAR(50),

DOB DATETIME,

Salary DECIMAL(18,2),

DeptID INT

GO

-- Populate the Employee Table with test data

INSERT INTO Employee VALUES(1, 'Pranaya', 'Male','1996-02-29 10:53:27.060', 25000,


1)

INSERT INTO Employee VALUES(2, 'Priyanka', 'Female','1995-05-25 10:53:27.060',


30000, 2)

INSERT INTO Employee VALUES(3, 'Anurag', 'Male','1995-04-19 10:53:27.060',40000,


2)

INSERT INTO Employee VALUES(4, 'Preety', 'Female','1996-03-17 10:53:27.060', 35000,


3)

INSERT INTO Employee VALUES(5, 'Sambit', 'Male','1997-01-15 10:53:27.060', 27000,


1)

INSERT INTO Employee VALUES(6, 'Hina', 'Female','1995-07-12 10:53:27.060', 33000,


2)
GO
Business Requirement:
Our business requirement is to create a complex view in SQL Server that will return the
following result set by joining the Department and Employee table.

Let’s create a complex view in SQL Server that will return the above data. Please execute
the below SQL Statement to create the View which will return the above data. As you can
see, the below view is created based on two tables i.e. Department and Employee, we call
this a Complex View in SQL Server.
CREATE VIEW vwEmployeesByDepartment

AS

SELECT emp.ID,

emp.Name,

emp.Salary,

CAST(emp.DOB AS Date) AS DOB,

emp.Gender,

dept.Name AS DepartmentName

FROM Employee emp

INNER JOIN Department dept

ON emp.DeptID = dept.ID
Now execute the following query which will return the data as expected.
SELECT * FROM vwEmployeesByDepartment
UPDATE DML Operation on Complex View in SQL Server:
Now, let’s update, Pranaya’s department, from IT to HR. As you can see in our Employee
table, at the moment, there are 2 employees (Pranaya and Sambit) in the IT department.
Please execute the following UPDATE statement to update the Department Name from IT
to HR of the Employee whose name is Pranaya.
Update vwEmployeesByDepartment SET DepartmentName =’HR’ where Name =
‘Pranaya’
Once you execute the above UPDATE statement, then make a select query on the view as
shown below
SELECT * FROM vwEmployeesByDepartment
Once you execute the above query, you will get the following output.

Notice that along with Pranaya, Sambit’s department is also changed from IT to HR. To
understand the reasons for incorrect UPDATE, select the Data from Department and
Employee base tables and you will get the following data.

If you notice, the UPDATE statement actually updated the Department Name from IT to HR
in the Department table, instead of updating the DeptID column in the Employee table. So,
the conclusion is – If a view is based on multiple tables, and if we update the view, it may
not update the underlying base tables correctly. To correctly update a complex view, that is
based on multiple tables INSTEAD OF Triggers are used which we will discuss in a later
article.
Example: Updating two tables data using Complex View in SQL Server
Let us see what happens when we try to update two different tables record by using a
complex view in SQL Server. First correct the department table, change the department
name to IT where ID is 1 in the Department table by executing the below UPDATE
statement.
UPDATE Department SET Name = ‘IT’ WHERE ID = 1
Let’s update the Salary and department name of Pranaya by executing the below UPDATE
statement.
UPDATE vwEmployeesByDepartment SET

DepartmentName ='HR',

Salary = 50000

WHERE Name = 'Pranaya'


When we execute the above update statement it gives us the error as View or function
‘vwEmployeesByDepartment’ is not updatable because the modification affects
multiple base tables.
It clearly says that we cannot update a view if it affects more than one base table. In our
update statement, we are trying to update the Department Name from the Department table
and salary from the Employee table and hence the update statement failed and we got the
above error.
Example:
Now we want to update the Salary of Pranaya from 25000 to 50000 by executing the below
update statement.
UPDATE vwEmployeesByDepartment SET Salary = 50000 WHERE Name = ‘Pranaya’
When we execute the above update statement it updates the Salary as expected. To check,
retrieve the data from the Employee table by executing the below SELECT statement.
SELECT * FROM Employee
Once you execute the above select statement, you will give the following results. You can
see it updates the Salary correctly in the Employee table.
Conclusion:
1. In a Complex View, if your update statement affects one base table, then the update
succeeded but it may or may not update the data correctly.
2. if your update statement affects more than one table, then the update failed and we
will get an error message stating “View or function ‘vwEmployeesByDepartment’
is not updatable because the modification affects multiple base tables”.
Stored Procedure in SQL Server with Examples
1. Why do we need a Procedure in SQL Server?
2. What is a Stored Procedure in SQL Server?
3. How can we create a Stored Procedure?
4. How to call a Stored Procedure in SQL Server?
5. Multiple Examples to understand SQL Server Stored Procedure.
6. How to view the text of a Procedure in SQL Server?
7. How to Drop and Alter a Procedure in SQL Server?
8. Different Types of Parameters in SQL Server Stored Procedure
9. Understanding the Input Parameters in SQL Server Procedure
10. Understanding the SQL Server Stored Procedure Output Parameters
11. What are the advantages of using a Stored Procedure?
12. What is an execution plan?
Why do we need a Stored Procedure in SQL Server?
Before going to understand why we need a Stored Procedure, let us first understand what
happens when we execute a simple SQL statement in SQL Server. When any SQL
Statements are fired on SQL Server, then three steps are happening in order which is
shown in the below image.

1. Syntax Checked: This step ensures that the syntaxes are correct and there is no
error and the query is ready for executing on SQL Server.
2. Plan Selected: Once the syntaxes are checked, the second step is to select a plan.
The SQL Query must be using some tables. It will go and check what types of indexes
are exists on these tables, it will also check can use these indexes or a table scan is
fine. So, the second step is to select a proper execution plan to execute the query.
3. Query Execution: Once the plan is selected, the final step is to execute the query and
the output is seen by the end-user.
So, any SQL Statement fire on SQL Server should go through these three steps.
Now somehow, if we ensure that the first two steps (i.e. Syntax Checked and Plan
Selected) are executed only once, would not it be great. In other words, the first time the
SQL is executed, the syntaxes are checked, the execution plan is selected and the
execution plan is cached in memory. So, if the same SQL statements are fired again, then
these two steps are not going to be executed, rather the execution plan is taken from the
cache and executed and that will definitely increase the performance of the application
which is shown in the below image.
This is what exactly the stored procedure does in SQL Server. When we create a stored
procedure, the syntaxes are checked while creating the procedure or we can say at the
design. When we execute the procedure for the first time, the best execution plan is
selected and is cached in memory. And after that whenever we call the stored procedure,
the query execution plan is taken from the cache rather than creating again and again and
executed.
There are also other advantages of using stored procedures which we will discuss in our
upcoming articles. With this keep in mind, let us proceed and understand the SQL Server
Stored Procedure in detail.
What is a Stored Procedure in SQL Server?
A SQL Server Stored Procedure is a database object which contains pre-compiled queries
(a group of T-SQL Statements). In other words, we can say that the Stored Procedures are
a block of code designed to perform a task whenever we called.
How can we create a Stored Procedure in SQL Server?
In SQL Server, you can create a stored procedure by using the CREATE
PROCEDURE or CREATE PROC statement. Again, you can create a procedure with or
without parameters. Please have a look at the below image for the Syntax of Stored
Procedure.
A stored procedure is very much similar to a function in C, C++ languages or a method in
Java or C# languages. The procedure definitions contain two parts in it
1. Procedure header
2. Procedure body
The content above “AS” is known as the procedure header and the content below the
“AS” is known as the procedure body. If required we can pass the parameter to a
procedure to make the procedure more dynamic.
How to call a Stored Procedure in SQL Server?
Once we create the stored procedure, then it is physically stored on the server as a
“database object” which can be called from anywhere connecting to the server. We can
call the procedure from anywhere that is from a new query window or from any application
that is developed using java or .net language also in three different ways as shown in the
below image.

Note: Another way to execute a stored procedure is to right-click on the procedure name in


object explorer and select “Execute Stored Procedure”.
Stored Procedure in SQL Server Without Parameter
The following stored procedure simply print a welcome message on the screen
CREATE PROCEDURE spDisplayWelcome

AS

BEGIN

PRINT 'WELCOME TO PROCEDURE in SQL Server'

END
Calling a Stored Procedure:
EXECUTE spDisplayWelcome

Or

EXEC spDisplayWelcome

Or

spDisplayWelcome
Output: WELCOME TO PROCEDURE in SQL Server
Note: While naming the user-defined stored procedures we should not have to
use “sp_” as a prefix as it is recommended by Microsoft. The reason is all the system-
defined procedures in SQL Server are prefixed with “sp_”. So to avoid the ambiguity
between the user-defined stored procedure and the system stored procedures and for any
conflicts with some future coming system procedure we should not use sp_ as a prefix to
our user-defined stored procedure.
Let’s see another example where we will fetch the data from a database table.
We are going to use the following Employee table.

Please use the following SQL Script to create and populate the Employee table with
the required sample data.
-- Create Employee Table

CREATE TABLE Employee


(

ID INT PRIMARY KEY,

Name VARCHAR(50),

Gender VARCHAR(50),

DOB DATETIME,

DeptID INT

GO

-- Populate the Employee Table with test data

INSERT INTO Employee VALUES(1, 'Pranaya', 'Male','1996-02-29 10:53:27.060', 1)

INSERT INTO Employee VALUES(2, 'Priyanka', 'Female','1995-05-25 10:53:27.060', 2)

INSERT INTO Employee VALUES(3, 'Anurag', 'Male','1995-04-19 10:53:27.060', 2)

INSERT INTO Employee VALUES(4, 'Preety', 'Female','1996-03-17 10:53:27.060', 3)

INSERT INTO Employee VALUES(5, 'Sambit', 'Male','1997-01-15 10:53:27.060', 1)

INSERT INTO Employee VALUES(6, 'Hina', 'Female','1995-07-12 10:53:27.060', 2)

GO
Create a stored procedure to get the names, gender, and the dob of all employees from the
table Employee table.
CREATE PROCEDURE spGetEmployee

AS

BEGIN

Select Name, Gender, DOB from Employee

END

-- To Execute the Procedure

EXEC spGetEmployee
When we execute the above statement it will give us the below output.

How to View the text of a Stored Procedure in SQL Server?


Once you created the stored procedure and later if you want to view the text of the stored
procedure then you need to use the sp_helptext system-defined stored procedure by
supplying the procedure name as a parameter as shown below
Example: sp_helptext spGetEmployee
Else just right click on the stored procedure in object explorer Script procedure as Create To
new query editor window
How to change the name and body of a stored procedure in SQL Server?
CREATE PROCEDURE spGetEmployee

As

BEGIN

SELECT Name,Gender, DOB FROM Employee

END

-- How to change the body of a stored procedure

-- User Alter procedure to change the body

ALTER PROCEDURE spGetEmployee

AS

BEGIN

SELECT Name, Gender, DOB

FROM Employee
ORDER BY Name

END

-- To change the procedure name from spGetEmployee to spGetEmployee1

-- Use sp_rename system defined stored procedure

EXEC sp_rename 'spGetEmployee', 'spGetEmployee1'


How to Drop a Stored Procedure?
In order to drop a stored procedure, all you need to use the following syntax
DROP PROCEDURE ProcedureName
Example: Drop proc spGetEmployee1 or Drop Procedure spGetEmployee1
Different Types of Parameters in SQL Server Stored Procedure.
The parameters of a Stored Procedure in SQL Server can be of two types
1. Input parameters
2. Output parameters
The Input Parameters in SQL Server Stored Procedure are used for bringing the values into
the procedure for execution. On the other hand, the Output Parameters are used to carrying
a value out of the procedure after execution.
When a parameter is declared with the output keyword, then we only require assigning a
value to the parameter inside the procedure so that the procedure will send that value out at
the end of procedure execution.
Understanding the Input Parameters in SQL Server Procedure:
Let us understand the Input Parameters in SQL Server with an example. Let’s create a
procedure that will take two input integer parameters and then perform the sum operation
and finally print the result.
Example: Stored Procedure for adding two variables value
-- Create a Procedure

ALTER PROCEDURE spAddTwoNumbers(@no1 INT, @no2 INT)

AS

BEGIN

DECLARE @Result INT

SET @Result = @no1 + @no2

PRINT 'RESULT IS: '+ CAST(@Result AS VARCHAR)

END

GO
-- Calling the procedure:

EXECUTE spAddTwoNumbers 10, 20

-- OR

EXECUTE spAddTwoNumbers @no1=10, @no2=20

-- OR calling the procedure by declaring two variables as shown below

DECLARE @no1 INT, @no2 INt

SET @no1 = 10

SET @no2 = 20

EXECUTE spAddTwoNumbers @no1, @no2


Note: The Parameters and variables that we created must have an @ prefix in their name.
Example: Create a Procedure to get the employee information bypassing the employee
gender and department id from the Employee table
CREATE PROCEDURE spGetEmployeesByGenderAndDepartment

@Gender VARCHAR(20),

@DeptID INT

AS

BEGIN

SELECT Name, Gender, DOB, DeptID

FROM Employee

WHERE Gender = @Gender AND DeptID = @DeptID

END

GO
In order to invoke the above SQL Server Stored Procedure, we need to pass the value
for @Gender and @DeptID input parameters. If we don’t specify the name of the
parameters we have to first pass the value for the @Gender parameter and then for
the @DeptID parameter as shown below.
EXECUTE spGetEmployeesByGenderAndDepartment ‘Male’, 1
On the other hand, if we change the order, then we will get an error stating “Error
converting data type varchar to int.” This is because the value of “Male” is passed into
the @DeptID parameter. Since @DeptID is an integer, we get the type conversion error.
EXEC spGetEmployeesByGenderAndDepartment 1, ‘Male’
When we specify the names of the parameters when executing the stored procedure the
order doesn’t matter. The example is given below.
EXEC spGetEmployeesByGenderAndDepartment @DeptID=1, @Gender=’Male’
Note: While we are calling the stored procedure passing the values in order is very
important. In the order they are declared in the procedure we need to pass the values in the
same order. You can also pass the value in any order, but at that time you have to specify
the variable name before the value to which you are passing the values.
Example: Create a procedure to update the Employee details in the Employee table based
on the Employee id.
-- Create a Procedure

CREATE PROCEDURE spUpdateEmployeeByID

@ID INT,

@Name VARCHAR(50),

@Gender VARCHAR(50), @DOB DATETIME,

@DeptID INT

AS

BEGIN

UPDATE Employee SET

Name = @Name,

Gender = @Gender,

DOB = @DOB,

DeptID = @DeptID

WHERE ID = @ID

END
GO

-- Executing the Procedure

-- If you are not specifying the Parameter Names then the order is important

EXECUTE spUpdateEmployeeByID 3, 'Palak', 'Female', '1994-06-17 10:53:27.060', 3

-- If you are specifying the Parameter Names then order is not mandatory

EXECUTE spUpdateEmployeeByID @ID =3, @Gender = 'Female', @DOB = '1994-06-17


10:53:27.060', @DeptID = 3, @Name = 'Palak'
SQL Server Stored Procedure Output Parameters:
The Input parameters of SQL Server Stored Procedure are used for bringing the values into
the procedure for execution. On the other hand, the SQL Server Stored Procedure output
parameters are used for carrying a value out of the procedure after its execution. We only
require assigning a value to the output parameter inside the procedure so that procedure
will send that value out at the end of the procedure execution. The Output parameter in SQL
Server can be declared either by using the OUT or OUTPUT keyword
Example: Stored Procedure with Output Parameter
Let’s create a simple stored procedure to understand the SQL Server Stored
Procedure Output Parameters
CREATE PROCEDURE spGetResult

@No1 INT,

@No2 INT,

@Result INT OUTPUT

AS

BEGIN

SET @Result = @No1 + @No2

END
The above SQL Server Stored Procedure takes 3 parameters. The @No1 and @No2 are
input parameters by default whereas the @Result is the output parameter. The Parameter
which is created using the OUT or OUTPUT keyword is called the output parameter in SQL
Server.
To execute a procedure with output parameter, First, we need to declare a variable, then we
need to pass that variable while calling the procedure by specifying the type as output as
shown below.
-- To Execute Procedure

DECLARE @Result INT

EXECUTE spGetResult 10, 20, @Result OUT

PRINT @Result
When we execute the above code it will print 30.
Let’s see more examples for a better understanding of SQL Server stored procedure output
parameters.
Create a stored procedure to get the total number of employees in the Employee table by
Gender. As we already discussed to create a Stored Procedure in SQL Server with an
output parameter, we need to use the keyword OUT or OUTPUT. In the following
Stored Procedure, the @EmployeeCount is an output parameter as we specified the
parameter with the OUTPUT keyword. 
CREATE PROCEDURE spGetEmployeeCountByGender

@Gender VARCHAR(30),

@EmployeeCount INT OUTPUT

AS

BEGIN

SELECT @EmployeeCount = COUNT(ID)

FROM Employee

WHER Gender = @Gender

END
Let’s see the different ways to execute the above SQL Server Stored Procedure with the
output parameter.
1. Step1: First declare a variable of the same data type as that of the output parameter.
Here we have declared the @EmployeeTotal integer variable.
2. Step2: Then we need to pass the @EmployeeTotal variable to the stored procedure.
We have to specify the variable with the OUTPUT keyword. If we don’t specify
the OUTPUT keyword, the variable will be NULL.
3. Step3. Execute
Way1: Allowed
DECLARE @EmployeeTotal INT

EXECUTE spGetEmployeeCountByGender 'Male', @EmployeeTotal OUTPUT


PRINT @EmployeeTotal
Note: If we don’t specify the output keyword when executing the stored procedure then
the @EmployeeTotal value will be null. For example, see the following
DECLARE @EmployeeTotal INT

EXECUTE spGetEmployeeCountByGender 'Male', @EmployeeTotal

PRINT @EmployeeTotal
Whether it will print null or not check the following:
DECLARE @EmployeeTotal INT

EXECUTE spGetEmployeeCountByGender'Male', @EmployeeTotal

IF (@EmployeeTotal IS NULL)

PRINT '@EmployeeTotal IS NULL'

ELSE

PRINT '@EmployeeTotal IS NOT NULL'


Way2: Not Allowed
DECLARE @EmployeeTotal INT

EXECUTE spGetEmployeeCountByGender @EmployeeTotal OUTPUT, 'Male'

PRINT @EmployeeTotal
Way3: Allowed
We can pass the parameters in any order when we use the parameter names. Here, we are
first passing the output parameter and then the input @Gender parameter.
DECLARE @EmployeeTotal INT

EXECUTE spGetEmployeeCountByGender @EmployeeCount = @EmployeeTotal


OUTPUT, @Gender ='Male'

PRINT @EmployeeTotal
Stored Procedure with Default Values:
Let’s see an example of how to use the stored procedure with default values.
CREATE PROCEDURE spAddNumber(@No1 INT= 100, @No2 INT)

AS
BEGIN

DECLARE @Result INT

SET @Result = @No1 + @No2

PRINT 'The SUM of the 2 Numbers is: '+ CAST(@Result AS VARCHAR)

END

-- Executing the above procedure:

1. EXEC spAddNumber 3200, 25

2. EXEC spAddNumber @No1=200, @No2=25

3. EXEC spAddNumber @No1=DEFAULT, @No2=25

4. EXEC spAddNumber @No2=25


In the 3rd and 4th cases, it uses the default value of 100 to the variable @No1 which has
been given while creating the procedure.
What are the advantages of using a Stored Procedure in an SQL Server?
This is one of the most frequently asked interview questions in SQL Server. Let discuss this
question in detail.
Execution Plan Retention which Improves the Application Performance
As there is no unnecessary compilation of queries this will reduce the burden on the
database (when we send a query to a SQL Server three things happen in order, 1st it checks
the syntax of that query, 2nd it compiles that query, 3rd it generates an execution plan) as
response user will get a quick response. Let’s get into more details.
The Stored Procedures are pre-compiled and their execution plan is cached and used again
when the same stored procedure is executed again. Although ad-hoc queries also create
and reuse plans, the plan is reused only when the query is the textual match and the
datatypes are matching with the previous call. Any changes in the datatype or you have an
extra space in the query then, a new plan is created.
Reduces the Network Traffic
The Stored Procedure reduces network traffic. When we execute a stored procedure we
need to send the procedure name and parameters so only these things are passed on the
network but if we are not using the stored procedure then we need to write the ad-hoc
queries and we need to execute them which may contain many numbers of lines. So the
stored procedure reduces the network traffic as a result performance of the application
increase.
Code Re-usability and Better Maintainability
Multiple applications can use the same stored procedure. The different applications which
want similar kind of data then they can use the same stored procedure. The advantage is
that if we want to change the stored procedure then we need to change it in one place that
will affect to all the application that uses it whereas if it is inline SQL query and if we have to
use it in multiple applications, then we end up with multiple copies of the same inline SQL
query, and if the logic has to change, then we have to change the logic at all the places,
which makes it harder maintaining inline SQL. So, the stored procedure provides code
reusability and maintainability.
Better Security 
By granting permission to the underlying database the user can do everything. He can view
all the records as well as modify the records. But if we want to restrict the user only to view
the records then we need to grant only for that stored procedure which will display the
records. In that way, we achieve better security with a stored procedure. Using a stored
procedure we can also avoid the SQL Injection attack.
What is an execution plan?
An execution plan is nothing but for the query to retrieve the data what is the best possible
way available. This depends on the indexes that available on the SQL Server to help that
query. Based on those it generates the execution plan and then it executes the query.
Joins in SQL Server with Examples
1. What are the different types of joins available in SQL Server?
2. What are ANSI and NON-ANSI Joins in SQL Server?
3. Inner Join in SQL Server?
4. What is Outer Join in SQL Server?
5. What is CROSS join in SQL Server?
6. Self-Join in SQL Server?
7. What is the difference between JOIN and UNION in SQL Server?
The SQL Server Joins are used to retrieve the data from two or more related tables.
In general, tables are related to each other using the primary key and foreign key
relationship but it is not mandatory. The tables involved in the joins must have a common
field. And based on that common field the SQL Server JOINS retrieves the records.
What are the different types of joins available in SQL Server?
The SQL Server Joins are classified into two types such as
1. ANSI format JOINS
2. NON-ANSI format JOINS
Again the ANSI format joins classified into three types such as
1. Inner join
2. Outer join
3. Cross join
Further, the outer join is divided into three types are as follows
1. Left outer join
2. Right outer join
3. Full outer join
NON-ANSI join in SQL Server are classified into four types such as
1. EQUI join
2. NON-EQUI join
3. SELF-join
4. Natural Join
Examples to Understand Joins in SQL Server:
We are going to use the following Employee and Projects tables to understand the SQL
Server Joins. The EmployeeId column in the Projects table is the foreign key referencing to
the Id column of the Employee table. The Id column is the Primary key column in the
Employee table.
Please use the following SQL Script to create the Company database, Employee, and
Projects tables with the required sample data.
-- Create Company Database

CREATE DATABASE Company;

-- Create Employee Table

CREATE TABLE Employee (

Id INT PRIMARY KEY,

Name VARCHAR(100) NOT NULL,


Department VARCHAR(100) NOT NULL,

Salary FLOAT NOT NULL,

Gender VARCHAR(45) NOT NULL,

Age INT NOT NULL,

City VARCHAR(45) NOT NULL

);

-- Populate Employee Table

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1001, 'John Doe', 'IT', 35000, 'Male', 25, 'London');

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1002, 'Mary Smith', 'HR', 45000, 'Female', 27, 'London');

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1003, 'James Brown', 'Finance', 50000, 'Male', 28, 'London');

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1004, 'Mike Walker', 'Finance', 50000, 'Male', 28, 'London');

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1005, 'Linda Jones', 'HR', 75000, 'Female', 26, 'London');

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1006, 'Anurag Mohanty', 'IT', 35000, 'Male', 25, 'Mumbai');

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1007, 'Priyanla Dewangan', 'HR', 45000, 'Female', 27, 'Mumbai');

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1008, 'Sambit Mohanty', 'IT', 50000, 'Male', 28, 'Mumbai');

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1009, 'Pranaya Kumar', 'IT', 50000, 'Male', 28, 'Mumbai');

INSERT INTO Employee (Id, Name, Department, Salary, Gender, Age, City) VALUES
(1010, 'Hina Sharma', 'HR', 75000, 'Female', 26, 'Mumbai');
-- Create Projects Table

CREATE TABLE Projects (

ProjectId INT PRIMARY KEY IDENTITY(1, 1),

Title VARCHAR(200) NOT NULL,

ClientId INT,

EmployeeId INT,

StartDate DATETIME,

EndDate DATETIME,

FOREIGN KEY (EmployeeId) REFERENCES Employee(Id)

);

-- Populate Projects Table

INSERT INTO Projects (Title, ClientId, EmployeeId, StartDate, EndDate) VALUES

('Develop ecommerse website from scratch', 1, 1003, GETDATE(), (Getdate() + 35)),

('WordPress website for our company', 1, 1002, GETDATE(), (Getdate() + 45)),

('Manage our company servers', 2, 1007, GETDATE(), (Getdate() + 55)),

('Hosting account is not working', 3, 1009, GETDATE(), (Getdate() + 65)),

('MySQL database from my desktop application', 4, 1010, GETDATE(), (Getdate() + 75)),

('Develop new WordPress plugin for my business website', 2, NULL, GETDATE(),


(Getdate() + 85)),

('Migrate web application and database to new server', 2, NULL, GETDATE(), (Getdate() +
95)),

('Android Application development', 4, 1004, GETDATE(), (Getdate() + 60)),

('Hosting account is not working', 3, 1001, GETDATE(), (Getdate() + 70)),

('MySQL database from my desktop application', 4, 1008, GETDATE(), (Getdate() + 80)),


('Develop new WordPress plugin for my business website', 2, NULL, GETDATE(),
(Getdate() + 90));
Inner Join in SQL Server
The Inner Join in SQL Server is used to return only the matching rows from both the tables
involved in the join by removing the non-matching records. The following diagram shows the
pictorial representation of SQL Server Inner Join.

Syntax to use Inner Join in SQL Server:


Please have a look at the following image which shows the syntax of Inner Join in the SQL
Server database. In SQL Server, you can use either the INNER JOIN or JOIN keyword to
perform Inner Join. If you use only the JOIN keyword, then it is also going to perform Inner
JOIN in SQL Server.

Inner Join Example in SQL Server:


Our requirement is to retrieve the EmployeeId, Name, Department, City, Title as Project,
and ClientId from the Employee and Projects tables. So, basically, we want the following
result set as our output.
Following is the SQL Server Inner Join Query that is joining the Employee and Projects
tables and gives you the result set as shown in the above image. Here, we are using the
EmployeeId column to check the similar values on the ON clause as both the tables having
this column (Id in Employee table and EmployeeId in Projects table).
SELECT Id as EmployeeID, Name, Department, City, Title as Project, ClientId

FROM Employee

INNER JOIN Projects

ON Employee.Id = Projects.EmployeeId;
If you look at the output, here, we got 8 rows. We did not get those 3 records that have the
NULL value in the EmployeeId column of the Projects table. So, this proves that the Inner
Join in SQL Server is used to return only the matching records from both the tables involved
in the join. The non-matching records are simply ignored or eliminated.
Instead of using the INNER JOIN keyword, we can also use the JOIN keyword as shown in
the below Query, and we will also get the same output as the previous example. This also
proofs that the default joins in SQL Server is Inner Join.
SELECT Id as EmployeeID, Name, Department, City, Title as Project, ClientId

FROM Employee

JOIN Projects

ON Employee.Id = Projects.EmployeeId;
Outer Join in SQL Server
The OUTER JOIN in SQL Server returns matched data rows as well as unmatched data
rows from both the tables involved in the join. Outer join in SQL Server is again classified
into three types as follows.
1. Left outer join
2. Right outer join
3. Full outer join
Left Outer Join in SQL Server
The LEFT OUTER JOIN in SQL Server is used to retrieve all the matching rows from both
the tables involved in the join as well as non-matching rows from the left side table. In this
case, the un-matching data will take a null value.
The question that should come to your mind is, which is the left table and which is the right
table? The answer is, the table mentioned to the left of the LEFT OUTER JOIN keyword is
the left table, and the table mentioned to the right of the LEFT OUTER JOIN keyword is the
right table. The following diagram is the pictorial representation of SQL Server Left Outer
Join.

Syntax to use Left Outer Join in SQL Server:


Please have a look at the following image which shows the syntax of Left Outer Join in SQL
Server. In SQL Server, you can use either the LEFT OUTER JOIN or LEFT JOIN keyword
to perform Left Outer Join. If you only use the LEFT JOIN keyword, then it is also going to
perform LEFT OUTER JOIN in SQL Server.
Example to Understand Left Outer Join in SQL Server
Now, we need to write a query to retrieve the Id as EmployeeId, Name, Department, City,
and Title as Project from the Employee and Projects tables. So, basically, we want the
query to produce the following result set as output. As you can see in the output, we are
retrieving all the matching records from both the table as well as the non-matching records
from the Employee table.

Now using SQL Server LEFT OUTER JOIN, we can join the Employee and Projects table
and display the information of the employees and the projects they are working on, we also
get the details of employees who are not working on any project and in that case, the
Project and ClientId column will take NULL values. The Following Query will give you the
results as shown in the above image.
SELECT Id as EmployeeID, Name, Department, City, Title as Project, ClientId

FROM Employee

LEFT OUTER JOIN Projects

ON Employee.Id = Projects.EmployeeId;
If you look at the output, here, we got all 10 records (i.e. all the records from
the Employee Table) including the row that has a null value for the EmployeeId column in
the Projects table. So, this proofs that the Left Outer Join will retrieve all the rows from
the Left-hand side Table including the rows that have a null foreign key value in the right-
hand side table.
Instead of using the LEFT OUTER JOIN keyword, you can also use
the LEFT JOIN keyword as shown in the below Query, and also you will get the same
output as the previous query.
SELECT Id as EmployeeID, Name, Department, City, Title as Project, ClientId

FROM Employee

LEFT JOIN Projects


ON Employee.Id = Projects.EmployeeId;
Right Outer Join in SQL Server
The RIGHT OUTER JOIN in SQL Server is used to retrieve all the matching rows from both
the tables involved in the join as well as non-matching rows from the right-side table. In this
case, the un-matching data will take NULL values.
The question that should come to your mind is, which is the left table and which is the right
table? The answer is, the table mentioned to the left of the RIGHT OUTER JOIN keyword is
the left table, and the table mentioned to the right of the RIGHT OUTER JOIN keyword is
the right table. The following diagram is the pictorial representation of SQL Server Right
Outer Join.

Syntax to use Right Outer Join in SQL Server:


Please have a look at the following image which shows the syntax of Right Outer Join in
SQL Server. In SQL Server, you can use either the RIGHT OUTER JOIN or RIGHT JOIN
keyword to perform Right Outer Join. If you only use the RIGHT JOIN keyword, then it is
also going to perform RIGHT OUTER JOIN in SQL Server.
Example to Understand Right Outer Join in SQL Server
Now, we need to write a query to retrieve the Id as EmployeeId, Name, Department, City,
and Title as Project from the Employee and Projects tables. So, basically, we want the
query to produce the following result set as output. As you can see in the output, we are
retrieving all the matching records from both the table as well as the non-matching records
from the Projects table.

Now using SQL Server RIGHT OUTER JOIN, we can join Employee and Projects table and
display the information of employees and the projects they are working on, we also get the
details of projects which are not yet assigned to any employee. The Following Query will
give you the results as shown in the above image.
SELECT Id as EmployeeID, Name, Department, City, Title as Project, ClientId

FROM Employee

RIGHT OUTER JOIN Projects


ON Employee.Id = Projects.EmployeeId;
If you look at the output, here, we got all 11 rows (i.e. all the rows from the Projects Table).
If you further notice, here, we got all the matching records from both the Employee and
Projects tables as well as all non-matching rows from the right-side table i.e. the Projects
Table. So, this proofs that the Right Outer Join retrieves all the rows from the Left-hand side
Table including the rows that have a null foreign key value in the left-hand side table.
Instead of using the RIGHT OUTER JOIN keyword, we can also use
the RIGHT JOIN keyword as shown in the below SQL Query, and also get the same output
as the previous query.
SELECT Id as EmployeeID, Name, Department, City, Title as Project, ClientId

FROM Employee

RIGHT JOIN Projects

ON Employee.Id = Projects.EmployeeId;
Full Outer Join in SQL Server
The Full Outer Join in SQL Server is used to retrieve all the matching records as well as all
the non-matching records from both the tables involved in the JOIN. The Un-matching data
in such cases will take the NULL values. The following diagram shows the pictorial
representation of Full Outer Join in SQL Server.

Syntax to use Full Outer Join in SQL Server:


Please have a look at the following image which shows the syntax of Full Outer Join in SQL
Server. In SQL Server, you can use either the FULL OUTER JOIN or FULL JOIN keyword
to perform Full Outer Join. If you only use the FULL JOIN keyword, then it is also going to
perform FULL OUTER JOIN in SQL Server.
Example to Understand Full Outer Join in SQL Server
Now, we need to write a query to retrieve the Id as EmployeeId, Name, Department, City,
and Title as Project from the Employee and Projects tables. So, basically, we want the
query to produce the following output. As you can see in the output, we are retrieving all the
matching records as well as all the non-matching records from both the table.

Now using SQL Server FULL OUTER JOIN, we can join Employee and Projects table and
display the information of employees and the projects they are working on, we also get the
details of projects which are not yet assigned to any employee as well as all the employees
who are not assigned with any projects. The Following Query will give you the results as
shown in the above image.
SELECT Id as EmployeeID, Name, Department, City, Title as Project, ClientId

FROM Employee

FULL OUTER JOIN Projects

ON Employee.Id = Projects.EmployeeId;
If you look at the output, here, we got all 13 rows (i.e. all the rows from the Projects Table
as well as all the rows from the Employee table). Instead of using the FULL OUTER
JOIN keyword, we can also use the FULL JOIN keyword as shown in the below Query, and
get the same output as the previous query.
SELECT Id as EmployeeID, Name, Department, City, Title as Project, ClientId

FROM Employee

FULL JOIN Projects

ON Employee.Id = Projects.EmployeeId;
Cross Join in SQL Server
The CROSS JOIN is created by using the CROSS JOIN keyword. The CROSS JOIN does
not contain an ON clause. In Cross Join, each record of a table is joined with each record of
the other table. In SQL Server, the Cross Join should not have either an ON or where
clause.
Example to Understand CROSS JOIN in SQL Server:
A Cross Join in SQL Server produces the Cartesian product of the tables involved in the
join. The Cartesian product means the number of records present in the first table is
multiplied by the number of records present in the second table. Please have a look at the
below SQL query which is an example of Cross Join for joining the Employee and Projects
Table.
SELECT Employee.Id as EmployeeId, Name, Department, City, Title as Project

FROM Employee

CROSS JOIN Projects;


The Employee is the LEFT Table which contains 10 rows and Projects is the RIGHT Table
which contains 11 rows. So, when you execute the above query, you will get 110 records in
the result set. 
What is Self-Join in SQL Server?
Joining a table by itself is called self-join in SQL Server. When we have some relation
between the columns within the same table then we need to use the self-join mechanism.
When we implement the self-join in SQL Server, we should create the alias for the table
name. We can create any number of aliases for a single table. Self-join is not a different
kind of join. It can be classified as any type of join, such as
1. Inner Join
2. Outer (Left, Right, Full) join
3. Cross Join
Note: The Self-join in SQL Server is just like any other joins except that the two instances of
the same table will be joined in the query.
What is the difference between Union and Join in SQL Server?
Joins and Unions are two different things. The Union in SQL Server is used to combines the
result-set of two or more select queries into a single result-set. On the other hand, the Joins
in SQL Server are used to retrieve the data from two or more related tables involved in the
join.
What is the General Formula for Joins in SQL Server?
Inner Join in SQL Server with Examples
1. What is Inner Join?
2. How to implement Inner Join in SQL Server?
3. When do we need to use Inner JOIN?
4. Example to Understand Inner JOIN
5. How to Join Three Tables in SQL Server.
What is Inner Join in SQL Server?
The Inner Join in SQL Server is used to returns only the matching rows from both the tables
involved in the join by removing the non-matching rows. That means Inner join results only
the matching rows from both the tables involved in the join. The following diagram shows
the pictorial representation of Inner Join.

How to implement Inner Join in SQL Server?


Please have a look at the following image which shows the syntax of SQL Server Inner
Join. Here, you can use either the INNER JOIN or JOIN keyword to perform Inner Join. If
you use only the JOIN keyword, then it is also going to perform the Inner JOIN operation in
SQL Server.
When do we need to use Inner JOIN in SQL Server?
If you want to select all the rows from the Left table that have a non-null foreign key value
then you need to use SQL Server Inner Join. To simplify this we can say that if you want to
retrieve all the matching rows from both the tables then you need to use the Inner Join. 
Example to Understand SQL Server Inner JOIN:
Let us understand how to implement Inner Join in SQL Server with an example. To
understand Inner Join in SQL Server, we are going to use the following Company and
Candidate tables.

Please use the below SQL Script to create Company and Candidate tables and populate


these tables with the required sample data. Here the CompanyId column of the Candidate
Table is a foreign key that is referencing the CompanyId column of the Company Table.
CREATE TABLE Company

CompanyId TinyInt Identity Primary Key,

CompanyName Nvarchar(50) NULL

GO

INSERT Company VALUES('DELL')

INSERT Company VALUES('HP')

INSERT Company VALUES('IBM')

INSERT Company VALUES('Microsoft')

GO
CREATE TABLE Candidate

CandidateId tinyint identity primary key,

FullName nvarchar(50) NULL,

CompanyId tinyint REFERENCES Company(CompanyId)

GO

INSERT Candidate VALUES('Ron',1)

INSERT Candidate VALUES('Pete',2)

INSERT Candidate VALUES('Steve',3)

INSERT Candidate VALUES('Steve',NULL)

INSERT Candidate VALUES('Ravi',1)

INSERT Candidate VALUES('Raj',3)

INSERT Candidate VALUES('Kiran',NULL)

GO
Now, our requirement is to write a query, to retrieve the CandidateId, FullName,
CompanyId, and CompanyName from Company and Candidate tables. The output of the
query should be as shown below. That means we want only the matching records from both
tables.

Following is the Query which is an example of Inner Join that is joining the Company and
Candidate tables and will give the result as shown in the above image.
SELECT Cand.CandidateId,
Cand.FullName,

Cand.CompanyId,

Comp.CompanyName

FROM Candidate Cand

INNER JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId;
If you look at the above output, we got only 5 rows. We did not get the 2 rows that have the
NULL value in the CompanyId column. Instead of using the INNER JOIN keyword, we can
also use the JOIN keyword as shown in the below SQL Query. JOIN or INNER JOIN means
the same. But it’s always better to use INNER JOIN, as this explicitly specifies your
intention.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyName

FROM Candidate Cand

JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId
So, in short, we can say that the Inner Join is used to returns only the matching records
from both the tables involved in the join. The non-matching rows are simply eliminated.
Joining three Tables in SQL Server:
Now we will see how to JOIN three tables in SQL Server. Already we have Company and
Candidate tables. Let us introduce the third table i.e. the Address table. So, we are going to
use the following Address table along with the Company and Candidate tables.

Please use the below SQL Script to create and populate the Address table with the required
sample data.
CREATE TABLE Address

AddressId INT IDENTITY PRIMARY KEY,

CandidateId tinyint REFERENCES Company(CompanyId),

Country VARCHAR(50),

State VARCHAR(50),

City VARCHAR(50),

GO

INSERT INTO Address Values (1, 'India', 'Odisha', 'BBSR');

INSERT INTO Address Values (2, 'India', 'Maharashtra', 'Mumbai');

INSERT INTO Address Values (3, 'India', 'Maharashtra', 'Pune');

INSERT INTO Address Values (4, 'India', 'Odisha', 'Cuttack');

GO
The following is the syntax to join three tables in SQL Server.

Example: Inner Joining Candidate, Company, and Address tables


The following Inner Join SQL Query Joining Candidate, Company, and Address tables.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,
Comp.CompanyName,

Addr.Country,

Addr.State,

Addr.City

FROM Candidate Cand

INNER JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId

INNER JOIN Address Addr

ON Addr.CandidateId = Cand.CandidateId;
When you execute the above query, you will get the following output. Now you can see, we
got only three records, this is because the following three records exist in all the three
tables.

It is also possible to use Different types of Joins while joining three tables in SQL Server.
For example, in the below SQL Query, we perform the first join (between Candidate and
Company) using Inner JOIN and the second join (between Candidate and Address)
operation using LEFT JOIN.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyName,

Addr.Country,

Addr.State,

Addr.City

FROM Candidate Cand


INNER JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId

LEFT JOIN Address Addr

ON Addr.CandidateId = Cand.CandidateId;
When you execute the above SQL Query, you will get the following output. In this case, for
the left join between Candidate and Campany, it will take null values for Country, State, and
City column which does not have any reference.

Note: You need to use Inner JOIN when you want to retrieve only the matching records
from both the tables involved in the JOIN by eliminating the non-matching records from both
the tables. In most real-time applications, we need to use this JOIN.
Left Outer Join in SQL Server with Examples
1. What is Left Outer Join?
2. How to implement Left Outer Join in SQL Server?
3. When do we need to use Left JOIN in SQL Server?
4. Example to Understand SQL Server Left Outer Join
5. How to retrieve only the non-matching rows from the left table in SQL Server?
6. Joining three Tables in SQL Server using Left Join
What is Left Outer Join in SQL Server?
The Left Outer Join in SQL Server is used to retrieve the matching records from both the
tables as well as the non-matching records from the left side table involved in the JOIN. In
that case, the un-matching data will take the null value. If this is not clear at the moment
then don’t worry we will see it with an example. The following diagram shows the pictorial
representation of the Left Outer Join.

The question that should come to your mind is which is the left table and which is the right
table? The answer is, the table which is mentioned to the left of the LEFT OUTER JOIN
keyword is the left table, and the table which is mentioned to the right of the LEFT OUTER
JOIN keyword is the right table. 
How to implement Left Outer Join in SQL Server?
Please have a look at the following image which shows the syntax of Left Outer Join in SQL
Server. Here, you can use either the Left Outer Join or the Left Join keywords to perform
the Left Outer Join operation. If you use only the Left JOIN keyword, then also it is going to
perform the Left Outer Join operation.
When do we need to use Left JOIN or Left Outer Join in SQL Server?
If you want to select all the records from the Left side table including the records that have a
null foreign key value then you need to use the SQL Server Left Outer Join. To simplify the
above definition, we can say that if you want to retrieve all the matching records from both
the tables involved in the join as well as the non-matching records from the left side table
then we need to use Left Join or Left Outer Join in SQL Server. 
Example to Understand Left Outer Join in SQL Server.
Let us understand how to implement Left Outer Join in SQL Server with Examples. To
understand Left Outer Join in SQL Server, we are going to use the following Company and
Candidate tables.

Please use the below SQL script to create the Company and Candidate tables and populate
them with some test data. Please note that the CompanyId column in the Candidate Table
is a foreign key referencing the CompanyId column of the Company Table.
CREATE TABLE Company

(
CompanyId TinyInt Identity Primary Key,

CompanyName Nvarchar(50) NULL

GO

INSERT Company VALUES('DELL')

INSERT Company VALUES('HP')

INSERT Company VALUES('IBM')

INSERT Company VALUES('Microsoft')

GO

CREATE TABLE Candidate

CandidateId tinyint identity primary key,

FullName nvarchar(50) NULL,

CompanyId tinyint REFERENCES Company(CompanyId)

GO

INSERT Candidate VALUES('Ron',1)

INSERT Candidate VALUES('Pete',2)

INSERT Candidate VALUES('Steve',3)

INSERT Candidate VALUES('Steve',NULL)

INSERT Candidate VALUES('Ravi',1)

INSERT Candidate VALUES('Raj',3)

INSERT Candidate VALUES('Kiran',NULL)

GO
Now our requirement is to write a query, to retrieve the CandidateId, FullName, CompanyId,
and CompanyName from Company and Candidate tables. The output of the query should
be as shown below.

Following is the SQL Query which is an example of Left Outer Join that is joining the
Company and Candidate tables and will give the results shown in the above image.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,

Comp.CompanyName

FROM Candidate Cand

LEFT OUTER JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId;
If you look at the output, here, we got all 7 rows (i.e. all the rows from the Candidate Table)
including the row that has a null value for the CompanyId column. So this proofs that the
SQL Server Left Outer Join will retrieve all the rows from the Left-hand side Table including
the rows that have a null foreign key value.
Instead of using the Left Outer Join keyword, we can also use the Left Join keyword. This
will also work as expected as Left Outer Join. That means the Left Outer Join or Left Join
means the same. Following is the SQL Query that uses Left Join Keyword to join Candidate
and Company tables.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,
Comp.CompanyName

FROM Candidate Cand

LEFT JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId;
In short, we can say that Left Outer Join will return all the matching records from both the
tables involved in the join as well as all the non-matching records from the left-hand side
table. Now, let us proceed and understand some tricky questions which may be asked in
SQL Server interviews.
How to retrieve only the non-matching rows from the left table in SQL Server?
Here, what we want is, we want to retrieve only the non-matching records from the left
table. For example, we want the following result sets when we join the Candidate and
Company tables. If you further notice, here we want to retrieve those rows from the
Candidate table whose does not have any reference in the Company table.

As we already discussed, the Left Outer Join is used to fetch all the matching records from
both the tables as well as all the non-matching records from the left-hand side table. In this
case, the non-matching records will take NULL value. So, to get the above output what we
need to do is, we need to perform Left Outer Join, and then in the where condition we need
to filter out the records which have NULL values. The following Left OuterJoin Query with
the where clause does the same.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,

Comp.CompanyName

FROM Candidate Cand

LEFT JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId

WHERE Comp.CompanyId IS NULL;


Joining three Tables in SQL Server using Left Join:
Now we will see how to join three tables in SQL Server. Already we have the Company and
Candidate tables. Let us introduce the third table i.e. the Address table. So, we are going to
use the following Address table along with the Company and Candidate tables to perform
joining operations between three tables in SQL Server.

Please use the following SQL Script to create and populate the Address table with the
required sample data.
CREATE TABLE Address

AddressId INT IDENTITY PRIMARY KEY,

CandidateId tinyint REFERENCES Company(CompanyId),

Country VARCHAR(50),

State VARCHAR(50),

City VARCHAR(50),

GO

INSERT INTO Address Values (1, 'India', 'Odisha', 'BBSR');

INSERT INTO Address Values (2, 'India', 'Maharashtra', 'Mumbai');

INSERT INTO Address Values (3, 'India', 'Maharashtra', 'Pune');

INSERT INTO Address Values (4, 'India', 'Odisha', 'Cuttack');

GO
The following is the syntax to join three tables in SQL Server.
Example: Left Joining Candidate, Company, and Address tables
The following Left Join SQL Query Joining Candidate, Company, and Address tables.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyName,

Addr.Country,

Addr.State,

Addr.City

FROM Candidate Cand

LEFT JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId

LEFT JOIN Address Addr

ON Addr.CandidateId = Cand.CandidateId;
When you execute the above SQL Query, you will get the following output. 
Note: You need to use LEFT OUTER JOIN when you want to retrieve all the matching
records from both the tables involved in the JOIN as well as non-matching records from the
left-hand side table. 
Right Outer Join in SQL Server with Examples
1. What is Right Outer Join?
2. How to implement Right Join in SQL Server?
3. When do we need to use the Right JOIN?
4. Example to Understand SQL Server Right Outer Join
5. How to retrieve only the non-matching rows from the right table?
6. Joining three Tables in SQL Server using Right Join
What is Right Outer Join in SQL Server?
The Right Outer Join in SQL Server is used to retrieve all the matching records from both
the tables involved in the join as well as all the non-matching records from the right-hand
side table. In that case, the un-matching data will take the null value. The following diagram
shows the pictorial representation of the Right Outer Join in SQL Server.

The question that should come to your mind is which is the left table and which is the right
table? The answer is, the table which is mentioned to the left of the RIGHT OUTER JOIN
keyword is the left table, and the table which is mentioned to the right of the RIGHT OUTER
JOIN keyword is the right table. The following diagram shows the pictorial representation of
Right Outer Join.
How to implement Right Outer Join in SQL Server?
Please have a look at the following image which shows the syntax of Right Outer Join.
Here, you can use either the Right Outer Join or Right JOIN keyword to perform the Right
Outer Join operation. If you use only the Right JOIN keyword, then it is also going to
perform Right Outer Join in SQL Server.
When do we need to use the Right JOIN in SQL Server?
If you want to select all the records from the Right side table including the records that have
a null foreign key value then you need to use the SQL Server Right Outer Join. To simplify
the above definition, we can say that if you want to retrieve all the matching rows from both
the tables involved in the join as well as the non-matching rows from the right side table
then you need to use Right Outer Join in SQL Server. 
Example to Understand SQL Server Right Outer Join.
Let us understand how to implement Right Outer Join in SQL Server with Examples. To
understand Right Outer Join, we are going to use the following Company and Candidate
tables.

Please use the following SQL script to create and populate the Company and Candidate
tables with some test data. Please note that here the CompanyId column of
the Candidate Table is the foreign key column referencing the CompanyId column of
the Company Table.
CREATE TABLE Company
(

CompanyId TinyInt Identity Primary Key,

CompanyName Nvarchar(50) NULL

GO

INSERT Company VALUES('DELL')

INSERT Company VALUES('HP')

INSERT Company VALUES('IBM')

INSERT Company VALUES('Microsoft')

GO

CREATE TABLE Candidate

CandidateId tinyint identity primary key,

FullName nvarchar(50) NULL,

CompanyId tinyint REFERENCES Company(CompanyId)

GO

INSERT Candidate VALUES('Ron',1)

INSERT Candidate VALUES('Pete',2)

INSERT Candidate VALUES('Steve',3)

INSERT Candidate VALUES('Steve',NULL)

INSERT Candidate VALUES('Ravi',1)

INSERT Candidate VALUES('Raj',3)

INSERT Candidate VALUES('Kiran',NULL)


GO
Write a query, to retrieve CandidateId, FullName, CompanyId, and CompanyName from
Company and Candidate tables. The output of the query should be as shown below.

Following is the Query which is an example of Right Outer Join that is joining the Company
and Candidate tables and will give the results shown in the above image.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,

Comp.CompanyName

FROM Candidate Cand

RIGHT OUTER JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId
If you look at the above, we got 6 rows. We got all the matching rows from both the tables
as well as non-matching rows from the right side table i.e. the Company Table. Instead of
using the Right Outer Join keyword, you can also use the Right Join keyword. This will
also work as expected as Right Outer Join. That means the SQL Server Left Outer Join or
Left Join means the same. The following example uses the Right Join keyword and we also
get the same output as the previous example.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,
Comp.CompanyName

FROM Candidate Cand

RIGHT JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId
In short, we can say that Right Outer Join will return all the matching records from both the
tables as well as all the non-matching records from the right-hand side table. 
How to retrieve only the non-matching rows from the right table?
Here, what we want is, we want to retrieve only the non-matching records from the right
table. For example, we want the following result sets when we join the Candidate and
Company tables. If you further notice, here we want to retrieve those rows from the
Candidate table whose does not have any reference in the Company table.

As we already discussed, the Right Outer Join is used to fetch all the matching records from
both the tables as well as all the non-matching records from the right-hand side table. In this
case, the non-matching records will take NULL value. So, to get the above output what we
need to do is, we need to perform the Right Outer Join operation, and then in the where
condition we need to filter out the records which have NULL values. The following Right
OuterJoin Query with the where clause does the same.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,

Comp.CompanyName

FROM Candidate Cand

Right JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId

WHERE Cand.CompanyId IS NULL;


Joining three Tables in SQL Server:
Now we will see how to Right Join three tables in SQL Server. Already we have the
Company and Candidate tables. Let us introduce the third table i.e. the Address table. So,
we are going to use the following Address table along with the Company and Candidate
tables to perform joining operations between three tables.
Please use the following SQL Script to create and populate the Address table with the
required test data.
CREATE TABLE Address

AddressId INT IDENTITY PRIMARY KEY,

CandidateId tinyint REFERENCES Company(CompanyId),

Country VARCHAR(50),

State VARCHAR(50),

City VARCHAR(50),

GO

INSERT INTO Address Values (1, 'India', 'Odisha', 'BBSR');

INSERT INTO Address Values (2, 'India', 'Maharashtra', 'Mumbai');

INSERT INTO Address Values (3, 'India', 'Maharashtra', 'Pune');

INSERT INTO Address Values (4, 'India', 'Odisha', 'Cuttack');

GO
The following is the syntax to join three tables in SQL Server.
Example: Right Joining Candidate, Company, and Address tables
The following Right Join SQL Query Joining Candidate, Company, and Address tables.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyName,

Addr.Country,

Addr.State,

Addr.City

FROM Candidate Cand

RIGHT JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId

RIGHT JOIN Address Addr

ON Addr.CandidateId = Cand.CandidateId;
When you execute the above Right Join SQL Query, you will get the following output. 
Full Outer Join in SQL Server with Examples
1. What is Full Outer Join?
2. How to implement Full Outer Join?
3. When do we need to use the Full Outer JOIN?
4. Example to Understand SQL Server Full Outer Join
5. How to retrieve only the non-matching rows from both the left and right table?
6. Joining three Tables in SQL Server using Full Outer Join
What is Full Outer Join in SQL Server?
The Full Outer Join in SQL Server is used to retrieve all the matching records from both the
tables involved in the join as well as all the non-matching records from both the tables. The
Un-matching data in such cases will take the null value. The following diagram shows the
pictorial representation of Full Outer Join in SQL Server.

How to implement Full Outer Join in SQL Server?


Please have a look at the following image which shows the syntax of SQL Server Full Outer
Join. Here, you can use either the Full Outer Join or Full JOIN keyword to perform Full Join.
If you use only the Full JOIN keyword, then it is also going to perform Full Outer Join.

When do we need to use the Full Outer JOIN?


If you want to select all the records from the left-hand side table plus all the records from the
right-hand side table then you need to use the SQL Server Full Outer Join. To simplify this
we can say that if you want to retrieve all the matching rows as well as all the non-matching
rows from both the tables involved in the join then you need to use Full Outer Join in SQL
Server. 
Example to Understand SQL Server Full Outer Join.
Let us understand how to implement Full Outer Join in SQL Server with Examples. To
understand Full Outer Join, we are going to use the following Company and Candidate
tables.

To create and populate the above two tables, please use the below SQL script. Please note
the CompanyId column of the Candidate Table is the foreign key column that is referencing
the CompanyId column of the Company Table.
CREATE TABLE Company

CompanyId TinyInt Identity Primary Key,

CompanyName Nvarchar(50) NULL

GO

INSERT Company VALUES('DELL')

INSERT Company VALUES('HP')

INSERT Company VALUES('IBM')

INSERT Company VALUES('Microsoft')

GO

CREATE TABLE Candidate


(

CandidateId tinyint identity primary key,

FullName nvarchar(50) NULL,

CompanyId tinyint REFERENCES Company(CompanyId)

GO

INSERT Candidate VALUES('Ron',1)

INSERT Candidate VALUES('Pete',2)

INSERT Candidate VALUES('Steve',3)

INSERT Candidate VALUES('Steve',NULL)

INSERT Candidate VALUES('Ravi',1)

INSERT Candidate VALUES('Raj',3)

INSERT Candidate VALUES('Kiran',NULL)

GO
Our requirement is to write a query, to retrieve CandidateId, FullName, CompanyId, and
CompanyName from Company and Candidate tables. The output of the query should be as
shown below.

Following is the Query which is an example of Full Outer Join that is joining the Company
and Candidate tables and will give the results shown in the above image.
SELECT Cand.CandidateId,
Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,

Comp.CompanyName

FROM Candidate Cand

FULL OUTER JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId
If you look at the output, now we now got 8 rows. That means all the records from the
Candidate Table as well as all the records from the Company Table. Instead of using the
Full Outer Join keyword, you can also use the Full Join keyword. This will also work as
expected as Full Outer Join. That means the SQL Server Full Outer Join or Full Join means
the same. The following example uses Full Join Keyword to join both tables.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,

Comp.CompanyName

FROM Candidate Cand

FULL JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId
In short, Full Outer Join, or you can say Full Join will return all the records from both left and
right tables involved in the join, including the non-matching rows. 
How to retrieve only the non-matching rows from both the left and right table?
Here, what we want is, we want to retrieve only the non-matching records from both tables.
For example, we want the following result sets when we join the Candidate and Company
tables. If you further notice, here all the matching rows are eliminated.
As we already discussed, the Full Outer Join is used to fetch all the matching records from
both the tables as well as all the non-matching records from both the table. In this case, the
non-matching records will take NULL value. So, to get the above output what we need to do
is, we need to perform Full Outer Join operation, and then in the where condition we need
to filter out the records which have NULL values. The following Full OuterJoin Query with
the where clause does the same.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,

Comp.CompanyName

FROM Candidate Cand

FULL JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId

WHERE Cand.CompanyId IS NULL

OR Comp.CompanyId IS NULL
Joining three Tables using Full Outer Join:
Now we will see how to Full Join three tables. Already we have the Company and
Candidate tables. Let us introduce the third table i.e. the Address table. So, we are going to
use the following Address table along with the Company and Candidate tables to perform
full outer joining operations between three tables.

Please use the following SQL Script to create and populate the Address table with the
required test data.
CREATE TABLE Address

AddressId INT IDENTITY PRIMARY KEY,


CandidateId tinyint REFERENCES Company(CompanyId),

Country VARCHAR(50),

State VARCHAR(50),

City VARCHAR(50),

GO

INSERT INTO Address Values (1, 'India', 'Odisha', 'BBSR');

INSERT INTO Address Values (2, 'India', 'Maharashtra', 'Mumbai');

INSERT INTO Address Values (3, 'India', 'Maharashtra', 'Pune');

INSERT INTO Address Values (4, 'India', 'Odisha', 'Cuttack');

GO
The following is the syntax to join three tables in SQL Server.

Example: Joining Candidate, Company, and Address tables using Full Outer
Join
The following Query Joining Candidate, Company, and Address tables.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyName,

Addr.Country,
Addr.State,

Addr.City

FROM Candidate Cand

FULL JOIN Company Comp

ON Cand.CompanyId = Comp.CompanyId

FULL JOIN Address Addr

ON Addr.CandidateId = Cand.CandidateId;
When you execute the above Full Outer Join Query, you will get the below output. 
Self Join in SQL Server with Example
What is Self Join in SQL Server?
The Self Join is nothing a concept where we need to join a table by itself. You need to use
Self Join when you have some relations between the columns of the same table. If this is
not clear at the moment then don’t worry we will discuss this with an example. I am sure at
the end of this article, you definitely have a very good idea of when to and how to use SQL
Server Self Join.
The point that you need to remember that when you are implementing the self-join
mechanism then you need to create the alias for the table. You can also create any number
of aliases for a single table in SQL Server. The Self Join is not a different type of join. It can
be classified as any type of join, such as
1. Inner Join
2. Outer (Left, Right, Full) join
3. Cross Join
The Self Join is like any other join except that the two instances of the same table will be
joined in the query.
Let’s understand Self Join with Examples
We are going to use the following Employee table to understand the SQL Server Self Join
concept.

Please use the following SQL script to create and populate the Employee table with the
required test data.
-- Create the Employee table

CREATE TABLE Employee

EmployeeID INT Primary Key,

Name VARCHAR(20),

ManagerID INT

GO
-- Insert the following records

INSERT INTO Employee VALUES(1, 'Pranaya', 3)

INSERT INTO Employee VALUES(2, 'Priyanka', 1)

INSERT INTO Employee VALUES(3, 'Preety', NULL)

INSERT INTO Employee VALUES(4, 'Anurag', 1)

INSERT INTO Employee VALUES(5, 'Sambit', 1)

GO
Self Join Examples in SQL Server:
The above Employee table contains the rows for both normal employees as well as
managers of that employee. So in order to find out the managers of each employee, we
need a SQL Server Self Join.  So here we need to write a query that should give the
following result. 

Self Join Query: 


A MANAGER is also an EMPLOYEE. Both the EMPLOYEE and MANAGER rows are
present in the same Employee table. Here we are joining the
table Employee with itself using different alias names, E for Employee and M for Manager.
Here we are going to use the Left Outer Join to get the records with ManagerId NULL. You
can see in the output that Preety’s record is also retrieved, but the MANAGER is NULL. If
you replace LEFT JOIN with INNER JOIN, you will not get Preety’s record.
SELECT E.Name as Employee, M.Name as Manager

FROM Employee E

LEFT JOIN Employee M

ON E.ManagerId = M.EmployeeId
Inner Self Join Employee table:
SELECT E.Name as Employee, M.Name as Manager

FROM Employee E
INNER JOIN Employee M

ON E.ManagerId = M.EmployeeId
When we execute the above query it produces the output as shown below.

Cross Self Join Employee table:


SELECT E.Name as Employee, M.Name as Manager

FROM Employee E

CROSS JOIN Employee M


Cross Join in SQL Server with Example
1. What is Cross Join?
2. How to implement Cross Join in SQL Server?
What is Cross Join?
When we combine two or more tables with each other without any condition (where or on)
then we call this type of joins Cartesian or cross join. In Cross Join, each record of a table is
joined with each record of the other table involved in the join. In SQL Server, the Cross Join
should not have either ON or where clause.
How to implement Cross Join in SQL Server?
Let us understand how to implement the Cross Join with an example. Let’s create two
Tables with the name Company and Candidate. Please use the below SQL Script to
create Company and Candidate tables and populate these two tables with some test data.
Please note that the CompanyId column of the Candidate table is a foreign key
referencing the CompanyId column of the Company Table.
CREATE TABLE Company

CompanyId TinyInt Identity Primary Key,

CompanyName Nvarchar(50) NULL

GO

INSERT Company VALUES('DELL')

INSERT Company VALUES('HP')

INSERT Company VALUES('IBM')

INSERT Company VALUES('Microsoft')

GO

CREATE TABLE Candidate

CandidateId tinyint identity primary key,

FullName nvarchar(50) NULL,

CompanyId tinyint REFERENCES Company(CompanyId)


)

GO

INSERT Candidate VALUES('Ron',1)

INSERT Candidate VALUES('Pete',2)

INSERT Candidate VALUES('Steve',3)

INSERT Candidate VALUES('Steve',NULL)

INSERT Candidate VALUES('Ravi',1)

INSERT Candidate VALUES('Raj',3)

INSERT Candidate VALUES('Kiran',NULL)

GO
A Cross Join produces the Cartesian product of the tables involved in the join. The
Cartesian product means the number of records present in the first table is multiplied by the
number of records present in the second table. Please have a look at the below SQL query
which will which is an example of Cross Join for joining the Candidate and Company Table.
SELECT Cand.CandidateId,

Cand.FullName,

Cand.CompanyId,

Comp.CompanyId,

Comp.CompanyName

FROM Candidate Cand

CROSS JOIN Company Comp


When we execute the above cross join query, it produces the result set as shown in the
image below.
Points to remember when working with Cross Join:
The Cross Join produces the Cartesian product of the tables involved in the join. This
means every row in the Left Table is joined to every row in the Right Table. The candidate
is LEFT Table and Company is RIGHT Table. In our example, we have 28 total numbers
rows in the result set. 7 rows in the Candidate table multiplied by 4 rows in the Company
Table. 
In SQL Server, the Cross Join does not have an ON clause with a Join condition. All other
JOINS use the ON clause with a Join Condition.  If you try to use an ON clause on a
CROSS JOIN then it will give you a syntax error.
Indexes in SQL Server with Examples
1. How does data search when there is no index?
2. Understanding the Balanced Tree (B-Tree) in SQL Server.
3. How will be the database engine retrieves the data from a table?
4. Multiple Examples to understand Indexes in SQL Server.
5. What is an index?
6. When SQL Server uses Indexes?
7. Types of indexes in SQL Server.
The goal of the index is to make the search operation faster. Then the next question that
should come to your mind is how does index make your search operation faster? Indexes
make the search operation faster by creating something called a B-Tree (Balanced
Tree) structure internally. So, in this article, first, we will understand the theory of Balanced
Tree (B-Tree) Structure, and then we will see the practical implementation of how indexes
make the search operator faster. And finally, we will discuss the different types of indexes
available in the SQL Server database.
How will be the database engine retrieves the data from a table in SQL
Server?
Whenever the database engine wants to retrieve the data from a database table it will adopt
two different mechanisms for searching the data
1. Table scan
2. Index Scan/Seek
What is Table Scan in SQL Server?
In Table Scan, the SQL Server Search Engine will search for the required information
sequentially one by one from the start to the last record of the table. If the table having more
rows, then it will take more time for searching the required data, so it is a time-consuming
process.
Let us understand how the SQL Server Database Engine searches the data when there is
no index available on the table i.e. Table Scan. When there is no index in the table, SQL
Server searches the data sequentially. Please have a look at the following image for a
better understanding.

Suppose, you want to search the value 50, then the search engine (i.e. SQL Server Search
Engine) will scan the record sequentially one by one from the beginning i.e. from 1, and until
it reaches the value 50. If you want to increase the search performance, then somehow you
have to minimize the number of scans. That is what exactly the B-Tree (Balanced Tree)
does.
What is Index Scan/Seek in SQL Server?
In Index Scan, the SQL Server Search Engine uses a B-Tree structure to search the
required data which drastically improves the performance of your search query by reducing
the number of scans. So, let us first understand what B-Tree structure is and how it reduces
the number scan which ultimately improves the performance of your search query.
Understanding the Balanced Tree (B-Tree) in SQL Server:
Whenever you create an index (or indexes) on some column(s) of a table in SQL Server
then what happens internally is, it creates a B-Tree structure. In the B-Tree structure, the
data is divided into three sections i.e. Root Node, Non-Leaf Nodes, and Leaf Nodes. In
order to understand this better please have a look at the following image which shows how
the data is divided and stored. As you can see, in the Root Node it has 30 and 50. In the
Non-Leaf node, it has 10, 30, 40, and 50. And in the leaf node, we have the actual data. So,
the leaf node is actually pointing to data.
Suppose, you want to search 50 here, then what will happen internally is, the search engine
will start the search from the root node. It will check whether 50 is less than or equal to 30.
As 50 is not less than or equal to 30, so the non-leaf nodes and leaf nodes that come under
the root node 30 are completely bypassed.
Then it will go to the next node i.e. 50 and check whether 50 is less than or equal to 50. And
the condition satisfies here. Then it goes to the non-leaf nodes (40, 50) which are under the
root node 50. It will check whether 50 is less than or equal to 40 and condition fail, so, it will
bypass all the leaf nodes which come under the non-leaf node 40. Then it will check the
other non-leaf node i.e. 50 and here the condition satisfies as 50 equals 50 and it goes to
scan the leaf node sequentially. That is, it approximately scans 10 records.
So, as you can see, due to the Root Node, Non-Leaf Nodes, and Leaf Nodes arrangement,
the complete records from 1 to 40 are bypassed.
How Index improves search performance in SQL Server?
Let us understand how SQL Server Index improves search performance with an
example. We are going to use the following Employee table to understand how Indexes
improves search performance.

Please use the following SQL Script to create and populate the Employee table with
the required sample data.
CREATE TABLE Employee

Id INT,

Name VARCHAR(50),

Salary INT,

Gender VARCHAR(10),

City VARCHAR(50),
Dept VARCHAR(50)

GO

INSERT INTO Employee VALUES (3,'Pranaya', 4500, 'Male', 'New York', 'IT')

INSERT INTO Employee VALUES (1,'Anurag', 2500, 'Male', 'London', 'IT')

INSERT INTO Employee VALUES (4,'Priyanka', 5500, 'Female', 'Tokiyo', 'HR')

INSERT INTO Employee VALUES (5,'Sambit', 3000, 'Male', 'Toronto', 'IT')

INSERT INTO Employee VALUES (7,'Preety', 6500, 'Female', 'Mumbai', 'HR')

INSERT INTO Employee VALUES (6,'Tarun', 4000, 'Male', 'Delhi', 'IT')

INSERT INTO Employee VALUES (2,'Hina', 500, 'Female', 'Sydney', 'HR')

INSERT INTO Employee VALUES (8,'John', 6500, 'Male', 'Mumbai', 'HR')

INSERT INTO Employee VALUES (10,'Pam', 4000, 'Female', 'Delhi', 'IT')

INSERT INTO Employee VALUES (9,'Sara', 500, 'Female', 'London', 'IT')


Currently, we don’t have an index in any of the columns of the Employee table. Let us write
a query to get the employee info whose id is 8.
SELECT * FROM Employee Where Id = 8;
When you execute the above query it will use a table scan to get the data. In order to make
sure it uses a table scan, please click on the Display Estimated Execution Plan button as
shown in the below image.

Once you click on the above Display Estimated Execution Plan icon it will show the
following image which clearly says that it performs a Table scan. 
Further, if you mouse over of Table Scan option, then you will see that the value of
the Estimated Number of Rows is 10 as our table currently holds 10 records as shown in
the below image.
You will not find any performance issues currently as the number of records is less. But if
your table contains a huge amount of data let say 1000000 records, then it will definitely
take much more time to get the data.
Creating Index on Id Column:
Let us create an index on the Id column of the Employee table by executing the following
query. Later we will discuss the syntax and the different types of indexes and their need and
use. But for now, we are just focusing on the need for Indexes and how we can improve the
search operation performance using indexes in SQL Server.
CREATE CLUSTERED INDEX IX_Employee_ID ON Employee(Id ASC);
Once you execute the above statement, the index gets created and you can see the index
in the indexes folder which is present inside the Employee table as shown in the below
image.

The above SQL Server Index stores the Id of each employee in ascending order. Once you
create the index, now let us execute the same SQL query to get the employee info whose id
is 8 as shown below.
SELECT * FROM Employee Where Id = 8;
Once you execute the above query, now again click on the Display Estimated Execution
Plan option which will show the following image which clearly shows that it perform an
Index Scan or Index Seek. 

Further, if you mouse over of Index Seek option, then you will see that the value of
the Estimated Number of Rows is 1 as shown in the below image which means it only
scan 1 row which improves the search operation.
Now, I hope you understood the basic need for Indexes and how indexes improve the
performance of the Search Operation in SQL Server. With this keep in mind let us proceed
and first discuss some of the theoretical concepts which are good if you are preparing for an
interview and then we will discuss the different types of Indexes in SQL Server and their
needs with Examples.
What is an Index in SQL Server?
1. It is a database object in SQL Server which is used to improve the performance of
search operations.
2. When we create an index on any column of a table, then SQL Server internally
maintains a separate table called index table. And when we are trying to retrieve the
data from the existing table, depends on the index table, SQL Server directly goes to
the table and retrieves the data very quickly.
3. In a table, we can use a maximum of 250 indexes.
When SQL Server uses Indexes?
The SQL Server uses indexes of a table provided that the select or update or delete
statement contained the “WHERE” clause and moreover the where condition column must
be an indexed column. If the select statement contains an “ORDER BY” clause then also
the indexes can be used.
Note: When SQL Server is searching for information under the database, first it will verify
the best execution plan for retrieving the data and uses that plan which can be either a full-
page scan and index scan.
The syntax for creating an Index in SQL Server:
CREATE [UNIQUE] [CLUSTERED/ NON-CLUSTERED] INDEX <INDEX NAME> ON
<TABLE NAME> (<COLUMN LIST>)
To see the index: sp_helpindex Employee
To drop an index: Drop index Employee.IX_Employee_Id
Types of indexes in SQL Server
SQL Server Indexes are divided into two types. They are as follows:
1. Clustered index
2. Non- clustered index
What is SQL Server Clustered index?
The Clustered Index in SQL Server defines the order in which the data is physically stored
in a table. In the case of a clustered index,  the leaf node store the actual data. As the leaf
nodes store the actual data a table can have only one clustered index. The Clustered Index
by default was created when we created the primary key constraint for that table. That
means the primary key column creates a clustered index by default.
When a table has a clustered index then that table is called a clustered table. If a table has
no clustered index its data rows are stored in an unordered structure.
What is SQL Server Non-Clustered Index?
In SQL Server Non-Clustered Index, the arrangement of data in the index table will be
different from the arrangement of data in the actual table. The data is stored in one place
and the index is stored in another place. Moreover, the index will have pointers to the
storage location of the actual data.
Clustered Index in SQL Server with Examples
1. Understanding the B-Tree structure of Clustered Index
2. What is a Clustered Index?
3. Why a table can have only one Clustered Index?
4. Example to understand the Clustered Index.
5. Understanding the SQL Server Composite Clustered Index. 
As we already discussed in our previous article, whenever we create an index, SQL Server
will internally create something called Balanced Tree (B-Tree) Structure. As we have two
types of Indexes i.e. Clustered and Non-clustered, so, let us first understand the Balanced
Tree (B-Tree) Structure for both Clustered and Non-clustered Indexes. In this article, we are
going to discuss the Balanced Tree (B-Tree) Structure of Clustered Index and in the next
article, we will discuss the Balanced Tree (B-Tree) Structure of Non-Clustered Index in
SQL Server.
Understanding the B-Tree structure of Clustered Index in SQL Server:
Let us understand the Balanced Tree (B-Tree) structure of the clustered index. Please have
a look at the following diagram which shows the B-Tree structure of the Clustered Index in
SQL Server. As you can see in the below image, the leaf nodes contain the actual data. So,
when we search for any data, the clustered index directly gets the data from the leaf node.

What is SQL Server Clustered index?


The Clustered Index in SQL Server defines the order in which the data is physically stored
in a table. That is the leaf node (Ref to the B-Tree Structure diagram) store the actual data.
As the leaf nodes store the actual data, a table can have only one clustered index. The
Clustered Index by default was created when we created the primary key constraint for that
table. That means the primary key column creates a clustered index by default.
When a table has a clustered index then the table is called a clustered table. If a table has
no clustered index, its data rows are stored in an unordered structure.
Understanding the Clustered Index with an example in SQL Server:
In order to understand the clustered index and to make sure that the clustered index by
default was created when we created the primary key constraint on a table, please execute
the following query.
CREATE TABLE Employee

Id INT PRIMARY KEY,

Name VARCHAR(50),

Salary INT,

Gender VARCHAR(10),

City VARCHAR(50),

Dept VARCHAR(50)

GO
As you can see in the above query, we created the Employee table having the Id column as
the Primary key column. The Primary key constraints in SQL Server by default create a
clustered indexes if there is no clustered index already exists on the table. To Confirm
whether the Clustered index is created or not execute the any one of the following
statement.
sp_helpindex Employee
Execute sp_helpindex Employee
Once you execute any of the above SQL statement, it will give you the following output.
Here, you can find the index name, type of index as index_description, and the column on
which the index is created as index_keys.

Or alternatively, you can check the below folder.


Now insert some records in random order into the Employee table. Notice the values of the
Id column of the Employee table is not in sequential order.
INSERT INTO Employee VALUES (3,'Pranaya', 4500, 'Male', 'New York', 'IT')

INSERT INTO Employee VALUES (1,'Anurag', 2500, 'Male', 'London', 'IT')

INSERT INTO Employee VALUES (4,'Priyanka', 5500, 'Female', 'Tokiyo', 'HR')

INSERT INTO Employee VALUES (5,'Sambit', 3000, 'Male', 'Toronto', 'IT')

INSERT INTO Employee VALUES (7,'Preety', 6500, 'Female', 'Mumbai', 'HR')

INSERT INTO Employee VALUES (6,'Tarun', 4000, 'Male', 'Delhi', 'IT')

INSERT INTO Employee VALUES (2,'Hina', 500, 'Female', 'Sydney', 'HR')


Now Fetch the data by executing the below query.
SELECT * FROM Employee;
When you execute the above Select query, it will give you the below output.
Notice, the records are sorted in the table in ascending order based on the Id column. This
is because of the Clustered Index which we created on the Id column. The job of Clustered
Index in SQL Server is to determine or organized data in either ascending or descending
order on the column(s) on which it is created. This also proofs that the Clustered Index
defines the order in which the data is physically stored in a table. And this is also the reason
that it doesnot allows to create more than one clustered index in a table in SQL Server.
Can we create multiple clustered indexes in a table in SQL Server?
It is not possible to create more than one clustered index in a table in SQL Server. The
Employee table that we have just created already has one clustered Index on the Id column
which already determines the physical storage order of the data in the table. Let us try to
create another clustered Index on the Salary column and see what happens. Let us try to
execute the following SQL Query.
CREATE CLUSTERED INDEX IX_Employee_Salary ON Employee(Salary)
When we try to execute the above SQL query, it throws the following error. This is because
we already have a clustered index that already arranged the data in the table. This is the
reason why a table can have only one clustered index. If you want to create a clustered
index, first delete the existing one and then create a new one.
Cannot create more than one clustered index on table ‘Employee’. Drop the existing
clustered index ‘PK__Employee__3214EC07DCDC2006’ before creating another.
What is Composite Clustered Index in SQL Server?
It is possible in SQL Server to create the clustered index with multiple columns and if we do
so, then it is called a composite clustered index. Let’s create a clustered index based on 2
columns. To do this we first have to drop the existing clustered index which is created on
the Id column.
DROP INDEX Employee.PK__Employee__3214EC07AED992AA
When you execute the above DROP INDEX query, then you will get an error message
stating An explicit DROP INDEX is not allowed on index
‘Employee.PK__Employee__3214EC07DCDC2006’. It is being used for PRIMARY KEY
constraint enforcement.
Why it is not allowing that we will discuss in a later article. But for now to delete the
clustered index, right-click on the index in the Object Explorer window and select DELETE
option from the context menu as shown in the below image.
Once you delete the existing Clustered Index, then execute the following CREATE INDEX
query to create a composite clustered Index on the Gender and Salary columns of the
Employee table.
CREATE CLUSTERED INDEX IX_Employee_Gender_Salary ON Employee(Gender
DESC, Salary ASC)
Fetching the Data:
Now issue the following select query to fetch the data,
SELECT * FROM Employee;
When you execute the above select query, you will get the following data. Now, you can see
the data FIRST arranged by the Gender in descending order and then by Salary in
ascending order.
Non-Clustered Index in SQL Server with Examples
1. What is the Non-Clustered Index in SQL Server?
2. Example to Understand the SQL Server Non-Clustered Index.
3. Understanding the Composite Non-Clustered Index.
4. What are the differences between a clustered and a non-clustered index in
SQL Server?
5. When should we create indexes on a table?
6. Which Index is faster, Clustered or Non-Clustered Index?
What is SQL Server Non-Clustered Index?
In the Non-Clustered Index, the arrangement of data in the index table will be different from
the arrangement of data in the actual table. The data is stored in one place and the index
table is stored in another place. Moreover, the index table will have pointers to the storage
location of the actual data.
In order to understand SQL Server Non-Clustered Indexes in a better way, please have a
look at the following diagram which shows the pictorial representation of the B-Tree
structure of a non-clustered index in SQL Server. Both clustered and the non-clustered
index has the same B-Tree structure (i.e. having the Root Node, Intermediate Node (Non-
Leaf Node), and Leaf Nodes). The only difference between the clustered and non-clustered
index is how the leaf nodes are worked. In the case of a Clustered Index, the leaf node
holds the actual data. On the other hand, the non-clustered index leaf-node has a Row
Identifier (RID), and this Row ID pointing to different things based on the situations.
Situation1:
If your table does not have a clustered index, then the Row Identifier (RID) of the non-
clustered index pointing to the heap table. A heap table is nothing but a table without
indexes. In the heap table, it will search record row by row until it finds the data.
Situation2:
If your table has a clustered index, then the Row Identifier (RID) of the non-clustered index
will point to the clustered index key and that indexed key is used to search the data.
How many non-clustered indexes can create in a table in SQL Server?
As the non-clustered indexes are created separately from the actual data, so, a table can
have more than one non-clustered index in SQL Server. In the index table, the data is
stored either in the ascending or descending order of the index key which does not make
any effect or changes to the actual data stored in the table. In SQL Server we can create a
maximum of 999 non-clustered indexes for a table.
Understanding the Non-Clustered Index in SQL Server with Examples:
Let us understand the non-clustered index with an example. Please create the following
tblOrder table by executing the below SQL Script. Please note that we don’t have any
indexes at the moment on the table.
CREATE TABLE tblOrder
(

Id INT,

CustomerId INT,

ProductId Varchar(100),

ProductName VARCHAR(50)

GO
Now insert some data into the table. In order to do this, here, we are inserting some mock
data using a loop. Please execute the following query.
DECLARE @i int = 0

WHILE @i < 3000

BEGIN

SET @i = @i + 1

IF(@i < 500)

Begin

INSERT INTO tblOrder VALUES (@i, 1, 'Product - 10120', 'Laptop')

END

ELSE IF(@i < 1000)

Begin

INSERT INTO tblOrder VALUES (@i, 3, 'Product - 1020', 'Mobile')

End

Else if(@i < 1500)

Begin

INSERT INTO tblOrder VALUES (@i, 2, 'Product - 101', 'Desktop')

End
Else if(@i < 2000)

Begin

INSERT INTO tblOrder VALUES (@i, 3, 'Product - 707', 'Pendrive')

End

Else if(@i < 2500)

Begin

INSERT INTO tblOrder VALUES (@i, 2, 'Product - 999', 'HD')

End

Else if(@i < 3000)

Begin

INSERT INTO tblOrder VALUES (@i, 1, 'Product - 100', 'Tablet')

End

END
Now, execute the following query to get the data by ProductId.
SELECT * FROM tblOrder WHERE ProductId = ‘Product – 101’;
After executing the above SQL Query, let us click on the Display Estimated Execution Plan
button or simply click (Ctrl + L) which will open the Display Estimated Execution Plan
window as shown below.

As you can see in the above image, here, the database search engine uses a table scan to
get the required data.
Creating Non-clustered Index in SQL Server:
Please execute the following SQL Query which will create a non-clustered index on the
ProductId column of tblOrder table.
CREATE NONCLUSTERED INDEX IX_tblOrder_ProductId
ON dbo.tblOrder (ProductId)

INCLUDE ([Id],[CustomerId],[ProductName])

GO
Once you created the non-clustered index, now execute the following query and check the
execution plan.
SELECT * FROM tblOrde WHERE ProductId = ‘Product – 101’;
Following is the execution plan. As you can see, it now uses a non-clustered index to get
the required data.

Can we create a composite Non-Clustered Index in SQL Server?


Yes. It is also possible to create a composite non-clustered index (non-clustered index
based on more than one column) in SQL Server like composite Clustered Index.
Sometimes, the Display Estimated Execution Plan gives you the information for creating
the missing indexes which can improve the performance of the search query. Let us
understand this with an example. We want to fetch the details from the order table where
CustomerId = 3 and ProductName = ‘Pendrive’ by executing the following query.
SELECT * FROM tblOrder WHERE CustomerId = 3 and ProductName = ‘Pendrive’;
Once, you execute the above query, now open the Display Estimated Execution Plan
window as shown below.

Now, right-click on the window and select the Missing Index Details option which will give
you the information to create the missing indexes. In our example, it will give us information
to create a composite non-clustered index based on CustomerId and ProductName. So, let
us create one non-clustered index based on the CustomerId in Ascending order and
ProductName in Descending Order by executing the following query.
CREATE NONCLUSTERED INDEX IX_tblOrder_CustomerId_ProductName
ON tblOrder(CustomerId ASC, ProductName DESC)

INCLUDE ([Id],[ProductId]);
Once you create the above composite non-clustered index, now execute the below query
and see the execution plan which should use the composite non-clustered index to fetch the
data.
SELECT * FROM tblOrder WHERE CustomerId = 3 and ProductName = ‘Pendrive’;
What is the difference between clustered and non-clustered indexes in SQL
Server?
1. We can create only one clustered index per table whereas we can create more than
one non-clustered index per table in SQL Server.
2. In the clustered index, the leaf node actually holds the data and in the case of a non-
clustered index, the leaf node actually points to the leaf node of a clustered index or
points to the heap table if the table does not have any clustered index.
3. The SQL Server Clustered Index determines the order in which the data is physically
stored in a table and hence does not require additional disk space whereas a Non-
Clustered Index in SQL Server is stored separately from the actual table, so
additional disk space is required.
When should we create indexes on a table in SQL Server?
We need to create indexes on table columns provided those columns are frequently used in
where conditions or order by clause in a select query. It is not advised to create an index on
each and every column because a number of indexes can degrade the performance of the
database also. This is because every modification we make to the data should be reflected
in all the index tables. How index impacts the performance of DML operations that we
will discuss in our next article.
Which Index is faster, Clustered or Non-Clustered Index?
The Clustered Index is slightly faster than the Non-Clustered Index in SQL Server. This is
because, in the case of the clustered index, the leaf node actually holds the actual data, and
hence when we search any data, it directly gets the data from the leaf node. On the other
hand, in the case of a Non-Clustered Index, the leaf node actually points to the leaf node of
clustered index or to the heap table and hence there is an extra look up from the Non-
Clustered Index to the actual table (leaf node of a clustered index or heap table) to fetch the
actual data.
How Index impacts DML Operations in SQL Server
In our previous three articles, we saw how indexes make our search operations faster. As
we know whenever a product comes into the market, there should be some advantages as
well as disadvantages that exist of that product. This is also the same in the case of
Indexes. In this article, we will discuss in detail, how indexes impact the insert, update and
delete operations in SQL Server.
Example to understand how Index impacts DML Operations in SQL Server:
In order to understand this, we are going to use the following Employee table. As you can
see, the following Employee table has two columns i.e. EmployeeId, and AddressLine.
Here, EmployeeId is of type integer and as it is marked as Primary Key, so by default it will
create a clustered index. The column AddressLine1 is of type varchar with size 2000.
-- Create Employee table with EmployeeId as Primary Key

CREATE TABLE Employee

EmployeeId INT Primary Key,

AddressLine1 Varchar(2000)

);
Once you created the Employee table, now insert four records into the table by executing
the below query.
INSERT INTO Employee VALUES (101, 'Address1')

INSERT INTO Employee VALUES (102, 'Address2')

INSERT INTO Employee VALUES (103, 'Address3')

INSERT INTO Employee VALUES (104, 'Address4')


Now let us understand, for this type of data type and this type of table, how will internal
index structure is formed. To make things simple and easy to understand, I have put the
entire thing together (table, data, and index structure) in a single image as shown below. As
we already discussed index is created based on the nodes i.e. we have a root node,
intermediate nodes (non-leaf nodes), and leaf nodes. And in the case of a clustered index,
the leaf node actually contains the data which you can see in the below image.
As you can see in the above image, currently the employee table is having two columns.
The EmployeeId column is of type integer and hence its size is 8 Bytes and the
AddressLine1 column size is 2000 Bytes as declared. That means one record will take 2008
bytes of memory space and currently we have four records, so a total of 8032 bytes of
memory space will be consumed.
The leaf nodes actually store the data in 8KB (approximately 8192 Bytes) page. So, from
the 8192 Bytes, the four records actually consume 8032 Bytes i.e. 160 Bytes are free from
the above data page.
Now, if you insert the fifth record into the table, then what will happen?
In order to insert a record, it requires 2008 Bytes. But currently, on the current data page,
only 160 Bytes are available. So, what it will do is, it will create another data page with a
size of 8KB. That means page split happens. To understand this better please have a look
at the following image.

Page split is nothing but when the current data page is filled with space or space is not
enough for the record to insert it will create a new data page and insert that record into the
new data page. This is called a Page Split. So, when your table having too many indexes,
then you have too many page splits which affect the performance of your application.
Another Drawback of Index in SQL Server:
Suppose you modify any data using the Update or you delete some data using the Delete
statement, then the updated information needs to be updated in the leaf node of all the
indexes (i.e. in the index table) where the data has been changed. Indexes can help us to
search and locate the data faster. However, too many indexes in a table can actually hurt
the performance of DML operations. So, you need to use the indexes properly to balance
the performance.
SQL Server Unique Index
When we create an Index by using the Unique option then it is called Unique Index. Then
the column(s) on which the unique index is created will not allow duplicate values i.e. it
works as a unique constraint. The Unique Index in SQL Server gives guarantees that the
column on which the index is created will not accept any duplicate values. 
Example: SQL Server Unique Index
We are going to use the following Employees table. Please use the following SQL Script to
create and populate the Employees table with the required sample data.
CREATE TABLE Employees

Id INT PRIMARY KEY,

FastName VARCHAR(30),

LastName VARCHAR(30),

Salary INT,

Gender VARCHAR(30),

City VARCHAR(30)

)
As we marked the Id column as the Primary Key column in the Employees table, by default
a UNIQUE CLUSTERED INDEX gets created in the Id column. To check whether
a UNIQUE CLUSTERED INDEX is created or not use the sp_helpindex system stored
procedure as shown below.
sp_helpindex Employees

The above output clearly shows that a unique clustered index is created on the Id column of
the Employees table. As we have a UNIQUE CLUSTERED INDEX on the Id column of the
Employees table, any attempt to Insert duplicate key values i.e. the Id column values of the
Employee table will throw an error. Let’s try to insert two records with the same Id values as
shown below.
INSERT INTO Employees VALUES(1,’Pranaya’, ‘Rout’,4500,’Male’,’Mumbai’)
INSERT INTO Employees VALUES(1,’Anurag’, ‘Mohanty’,2500,’Male’,’Delhi’)
When we try to insert the above two records, it gives us the below error which ensures that
we cannot insert duplicate key values when there is a unique index in SQL Server.
’Violation of PRIMARY KEY constraint ‘PK__Employee__3214EC0775FF9526’. Cannot
insert duplicate key in object ‘dbo.Employees’. The duplicate key value is (1)’.
Now let’s try to drop the Unique Clustered index on the Id column by using the DROP
command as shown below.
DROP INDEX Employees.PK__Employee__3214EC0775FF9526
When we try to execute the above DROP INDEX command, it gives us the below error
‘An explicit DROP INDEX is not allowed on index
‘Employees.PK__Employee__3214EC0775FF9526′. It is being used for PRIMARY KEY
constraint enforcement.’
So the above error message proves that the SQL server internally uses the UNIQUE
INDEX to enforce the uniqueness of values and primary key. To see the primary key and
Index, expand the keys folder in the object explorer window, and you can see the primary
key constraint. Similarly, expand the indexes folder in the object explorer window and you
can see the unique clustered index as shown in the below diagram.

You can see in the object explorer it just shows the clustered word only. To confirm whether
it is a UNIQUE index or not, right-click on the index and select properties. The properties
window shows the UNIQUE checkbox being selected as shown in the below diagram.
DELETE Clustered Index in SQL Server
We cannot delete the Unique Clustered Index using the DROP Command but SQL Server
allows us to delete the UNIQUE CLUSTERED INDEX from the object explorer. So Right-
click on the index, and then select the DELETE option and finally click on the OK button.
You will see that along with the UNIQUE index, the primary key constraint is also deleted.
Now, let’s try to insert some duplicate values for the ID column of the Employees table and
you will see that the rows get inserted into the table without any primary key violation error.
INSERT INTO Employees VALUES(2, ‘Priyanka’, ‘Dwegaan’, 4500, ‘Female’, ‘Mumbai’)
INSERT INTO Employees VALUES(2, ‘Preety’, ‘Tiwary’, 2500, ‘FEmale’, ‘Delhi’)
Once you insert the above two records, verify the Employee table as shown below.
SELECT * FROM Employees
Output:
So this proofs that the UNIQUE Index in SQL Server is used to enforce the uniqueness of
values. The UNIQUENESS is a property of an Index in SQL Server and both CLUSTERED
and NON-CLUSTERED indexes can be created as UNIQUE.
We cannot create a unique index on a single column if that column contains NULL in more
than one row. Similarly, we cannot create a unique index on multiple columns if the
combination of columns contains NULL in more than one row. These are treated as
duplicate values for indexing purposes. Let’s create a Unique Non-Clustered Index on the
FirstName and LastName columns of the Employees table
CREATE UNIQUE NONCLUSTERED INDEX
UIX_Employees_FastNamee_LastName On Employees(FastName, LastName)
The above unique non-clustered index ensures that no 2 entries in the index have the same
fast and last names. In the Unique Constraint article, we already discussed that a Unique
Constraint in SQL Server can be used to enforce the uniqueness of values, across one or
more columns.
Note: The Unique Constraint in SQL Server can be either unique clustered or unique non-
clustered. While creating an index in SQL Server, if clustered or non-clustered is not
specified by default it is non-clustered.
What are the differences between the UNIQUE Constraints and the UNIQUE
Index?
There are no major differences between a unique constraint and a unique index in SQL
Server. In fact, when we add a unique constraint, a unique index gets created behind the
scenes. To understand this, let’s add a unique constraint on the City column of the
Employees table
ALTER TABLE Employees ADD CONSTRAINT UQ_Emplyees_City UNIQUE (City)
Now when we execute the above query it gives us the below error.
The CREATE UNIQUE INDEX statement terminated because a duplicate key was
found for the object name ‘dbo.Employees’ and the index name ‘UQ_Emplyees_City’.
The duplicate key value is (Mumbai).
This is because we already have duplicate values on the City column of the Employees
table and hence it doesn’t allow creating Unique Index on the City Column. So let’s truncate
the table first and then create the Index as shown below.
TRUNCATE TABLE Employees
ALTER TABLE Employees ADD CONSTRAINT UQ_Emplyees_City UNIQUE (City)
Now, the statement executed successfully.
At this point, you may expect a unique constraint to be created within the constraints folder.
Refresh and expand the constraints folder in the object explorer window and you can see
that the constraint is not present in this folder. Now, refresh and expand the ‘indexes’ folder
and in the indexes folder, you can see a UNIQUE NON-CLUSTERED index with the
name UQ_Employees_City as shown below.
You can also verify the available constraints of a table by using the system stored
procedure SP_HELPCONSTRAINT as shown below
EXECUTE SP_HELPCONSTRAINT Employees
It will give us the below result.

When we create a UNIQUE Constraint in SQL Server, it actually creates a UNIQUE index
behind the scene. So, a UNIQUE index in SQL Server can be created explicitly by using
the CREATE INDEX statement or indirectly by using a UNIQUE constraint.
When should you be creating a unique constraint over a unique index?
To make the intentions clear create a unique constraint when the data integrity is the
objective. This makes the objective of the index very clear. In either case, the data is
validated in the same manner, and the query optimizer does not differentiate between a
unique index created by a unique constraint or manually created.
Points to Remember:
When we create a PRIMARY KEY constraint in SQL Server, a unique clustered index on
the column or columns is automatically created if a clustered index on the table does not
already exist.
When we create a UNIQUE constraint in SQL Server, a unique non-clustered index is
created automatically to enforce a UNIQUE constraint by default. 
We cannot create a UNIQUE constraint or a UNIQUE index in SQL Server on an existing
table if the table contains duplicate values in the key columns. To solve this, you need to
remove the key columns from the index definition or delete or update the duplicate values.
By default, duplicate values are not allowed on key columns, when you have a unique index
or constraint. For, example if you try to insert 10 rows, out of which 5 rows contain
duplicates, then all the 10 rows are rejected. However, if I want only the 5 duplicate rows to
be rejected and accept the non-duplicate 5 rows, then I can use the IGNORE_DUP_KEY
option. An example of using the IGNORE_DUP_KEY option is shown below.
CREATE UNIQUE INDEX IX_Employees_City ON Employees(City) WITH
IGNORE_DUP_KEY
Limitations and Restrictions
1. The unique index, UNIQUE constraint, or PRIMARY KEY constraint cannot be
created if duplicate key values exist in the data.
2. A unique non-clustered index can contain included non-key columns
Index in GROUP BY Clause in SQL Server
Let’s first discuss the Algorithm used by SQL Server to group the data.
The SQL Server database uses two different groups by algorithms.
1. The first one is the hash algorithm which will aggregate the input records into a
temporary hash table. Once all the input records are processed, then the hash table
is returned as the result.
2. The second algorithm is the sort/group algorithm which will first sort all the input data
by the grouping key so that the rows of each group follow each other in immediate
succession. Afterward, the database just needs to aggregate them.
In general, both algorithms need to materialize an intermediate state, so they are not
executed in a pipelined manner. Nevertheless, the sort/group algorithm can use an index to
avoid the sort operation, thus enabling a pipelined group by.
The index in GROUP BY Clause in SQL Server
Creating an Index on the column that is used in the GROUP BY clause can greatly improve
the performance of the query in SQL Server. Let’s understand this with an example. We
are going to use the following ProductSales table in this demo

Please use the below SQL Script to create and populate the ProductSales table with
test data.
-- Create the ProductSales table

CREATE TABLE ProductSales


(

ProductSalesID INT,

OrderID INT,

ProductID INT,

QunatitSold INT

GO

-- Populate the ProductSakes table with some test data

INSERT INTO ProductSales VALUES(1, 1001, 501, 3)

INSERT INTO ProductSales VALUES(2, 1001, 502, 2)

INSERT INTO ProductSales VALUES(3, 1001, 503, 4)

INSERT INTO ProductSales VALUES(4, 1002, 501, 1)

INSERT INTO ProductSales VALUES(5, 1002, 502, 2)

INSERT INTO ProductSales VALUES(6, 1003, 503, 2)

INSERT INTO ProductSales VALUES(7, 1003, 501, 3)

INSERT INTO ProductSales VALUES(8, 1004, 502, 4)

INSERT INTO ProductSales VALUES(9, 1004, 503, 4)

INSERT INTO ProductSales VALUES(10, 1001, 501, 3)

INSERT INTO ProductSales VALUES(11, 1001, 502, 2)

INSERT INTO ProductSales VALUES(12, 1001, 503, 4)

INSERT INTO ProductSales VALUES(13, 1002, 501, 1)

INSERT INTO ProductSales VALUES(14, 1002, 502, 2)

INSERT INTO ProductSales VALUES(15, 1003, 503, 2)

INSERT INTO ProductSales VALUES(16, 1003, 501, 3)


INSERT INTO ProductSales VALUES(17, 1004, 502, 4)

INSERT INTO ProductSales VALUES(18, 1004, 503, 4)


Generally, we use a GROUP BY clause to group the records and their aggregate values.
For example, counting the number of product sales with the same ProductId. To process a
query with a GROUP BY clause, the database will often sort the results on the columns
included in the GROUP BY clause.
How to see the query Execution Plan in SQL Server
To see the query execution plan, click on the Estimated Query Execution plan option as
shown below. We will discuss the Query Execution Plan in SQL Server in a later article. For
now, just understand how we can see the query execution plan in SQL Server.

Please consider the following query


SELECT PS.ProductID,

SUM(PS.QunatitSold) AS TotalQuantitySold

FROM ProductSales AS PS

GROUP BY PS.ProductID
In the above query, we are doing a SUM on the QuantitySold column and grouping it by the
ProductId column. The business requirement is to get the total quantity sold for each
product. When we run the above query, we get the following execution plan

As you can see from the above image, it uses a Table scan and we know the table scan is
bad for performance as it needs to scan each and every element present in the table. Let’s
try to improve the query by creating an index on the ProductId column and then see
the cost of the query.
CREATE NONCLUSTERED INDEX IX_ProductSales_ProductID ON
ProductSales(ProductID)
Now let’s execute the same query.
SELECT PS.ProductID,

SUM(PS.QunatitSold) AS TotalQuantitySold

FROM ProductSales AS PS

GROUP BY PS.ProductID
Once we execute the above query, now it gives us the below query execution plan.

As you can see from the above query execution plan image, now it uses Index Scan instead
of a Table scan and you can also see that it used the IX_ProductSales_ProductID non-
clustered index. But along with it also used nested loops and Rid Lookup and we can
improve this also. Now let’s create an index on both ProductId and QuantitySold
columns as shown below.
CREATE NONCLUSTERED INDEX IX_ProductSales_ProductID_QunatitSold ON
ProductSales(ProductID, QunatitSold)
Now again execute the Group By query to get the data as shown below.
SELECT PS.ProductID,

SUM(PS.QunatitSold) AS TotalQuantitySold

FROM ProductSales AS PS

GROUP BY PS.ProductID
It gives us the below execution plan.
When you mouse over to the Index Scan, you will see that it uses the newly created index
i.e. IX_ProductSales_ProductID_QunatitSold instead of the index
IX_ProductSales_ProductID. So this proofs that the SQL Server chooses the best query
plan execution based on the available indexes.
What is a Covering query?
If all the columns that we have requested in the select clause of the query are present in the
index, then there is no need to look up the table again. The requested column data can
simply be returned from the index. This is called Covering Query.
Please consider the following example.
SELECT ProductID, QunatitSold FROM ProductSales
From the above select query, the requested ProductID and QuantitySold column can simply
be returned from the Index IX_ProductSales_ProductID_QunatitSold as this index
contains the ProductID and QunatitySold column data.
The above query gives us the below Execution plan.
Advantages and Disadvantages of Indexes in SQL
Server
Advantages of Indexes in SQL Server:
To understand the advantages of Indexes in SQL Server, we are going to use the following
Employee table.

Please use the below SQL Script to create and populate the Employee table with the
required sample data.
-- Create Employee table

CREATE TABLE Employee

ID INT PRIMARY KEY,

FirstName VARCHAR(30),

LastName VARCHAR(30),

Salary INT,

Gender VARCHAR(30),

City VARCHAR(30),

Dept VARCHAR(30)

-- Populate the Employee table with some test data


INSERT INTO Employee VALUES (1,'Pranaya', 'Rout', 4500, 'Male', 'Mumbai', 'IT')

INSERT INTO Employee VALUES (2,'Anurag','Mohanty', 2500, 'Male', 'Delhi', 'IT')

INSERT INTO Employee VALUES (3,'Priyanka','Dewgaan', 5500, 'Female', 'Hyderabad',


'HR')

INSERT INTO Employee VALUES (4,'Sambit', 'Satapathy', 3000, 'Male', 'Hyderabad', 'IT')

INSERT INTO Employee VALUES (5,'Preety', 'Tiwary', 6500, 'Female', 'Mumbai', 'HR')

INSERT INTO Employee VALUES (6,'Tarun', 'Mallick', 4000, 'Male', 'Delhi', 'IT')

INSERT INTO Employee VALUES (7,'Hina', 'Sharma', 5000, 'Female', 'Delhi', 'HR')
Create a Non-Clustered Index on the Salary column of the Employee table
CREATE NONCLUSTERED INDEX IX_Employee_Salary ON Employee (Salary)
Now the Index table stores the data something similar to the below image.

Let’s discuss the advantages of using Indexes in SQL Server


Searching For Records
The most common use for an index in SQL Server is to find a record or set of records
matching a WHERE clause condition. Please consider the following SQL Query.
SELECT * FROM Employee WHERE Salary > 3000 AND Salary < 6000
The above SQL Select query gets benefits from the index that we created on the Salary
column. This is because the salaries are sorted in ascending order in the index. From the
index, it’s easy to identify the records where the salary is between 3000 and 6000, and
using the row address the corresponding records from the table can be fetched quickly.
In SQL Server, not only the SELECT statement but also the DELETE and UPDATE
statements can get benefit from the index. Please have a look at the following DELETE and
UPDATE statements
DELETE FROM Employee where Salary = 5500
UPDATE Employee SET Salary = 8000 WHERE Salary = 6500
To update or delete a row, the SQL server needs to first find that row, and the index can
help in searching and finding that specific row quickly which ultimately improves the
performance of the query.
Sorting Records
When we ask for a sorted result set, first, the database will try to find an index and try to
avoid sorting the results during the execution of the query. We can control the sorting of a
result set by specifying a field, or fields, in an ORDER BY clause, with the sort order as
ASC (ascending) or DESC (descending). For example, the following query returns all the
employees sorted by Salary:
SELECT * FROM Employee ORDER BY Salary
As we have an index on the Salary column of the Employee table, the Salaries are already
sorted. Here, the database engine simply scans the index from the first entry to the last
entry and retrieves the rows in sorted order. This avoids the sorting of rows during query
execution, which can significantly improve the processing time. The same index works
equally well with the following query simply by scanning the index in reverse order.
SELECT * FROM Employee ORDER BY Salary DESC
Grouping Records
We can use a GROUP BY clause to group the records and aggregate values. For example,
counting the number of employees for each department. To process a query with a GROUP
BY clause, the database will often sort the results on the columns included in the GROUP
BY clause. As of now, we do not have any index on the department column. So let’s first
create an index on the department column.
CREATE NONCLUSTERED INDEX IX_Employee_Dept ON Employee (Dept)
The following query counts the number of employees at each department by grouping
together records with the same department value.
SELECT Dept, COUNT(Dept) AS EmployeeCount FROM Employee GROUP BY Dept
Here, to group the Employees with the same department, the query engine can use the
index IX_Employee_Dept to retrieve the already sorted departments. Since the matching
departments are present in consecutive index entries, it is to count the total number of
employees at each Department quickly. 
Maintaining a Unique Column
The Columns which require unique values (such as primary key columns) must have a
unique index applied. There are several methods available in SQL Server to create a
unique index. Marking a column as a primary key will automatically create a unique index
on the column as we already discussed in our previous article. Let’s create a unique index
on the FirstName column.
CREATE UNIQUE INDEX IDX_Employee_FirstName On Employee (FirstName)
The above SQL command will not allow any duplicate values in the FirstName column, and
an index is the best tool for the database to use to enforce this rule. Each time an
application adds or modifies a row in the table, the database needs to search all existing
records to ensure none of the values in the new data duplicates existing values.
Disadvantages of Indexes:
As we already discussed, indexes provide an outstanding performance benefit to searches,
there is also a downside to indexing. Let’s discuss some of those drawbacks.
Additional Disk space:
The Clustered index in SQL Server does not require any additional disk space. But each
and every non clustered index requires additional disk space as it is stored separately from
the table. The amount of space required will depend on the size of the table, and the
number and types of columns used in the index.
Generally, the Disk space is cheap enough to trade for application performance, particularly
when a database serves a large number of users. To see the space required for a table,
use the sp_spaceused system stored procedure as shown below.
EXEC sp_spaceused Employee
Provide the table name to the system stored procedure sp_spaceused which will return the
amount of space used by the data and all indexes associated with the table as shown
below.

According to the above output, the table data uses 8 kilobytes, while the table
indexes use 56 kilobytes. The ratio of index size to table size can vary greatly, depending on
the columns, data types, and the number of indexes on a table.
Insert Update and delete statement become Slow:
Another drawback of using indexes in SQL Server is the performance implication on data
modification statements. When any DML statement such as Insert, Update and Delete
modifies data in a table, the database needs to update all of the indexes where data has
changed. As we already discussed the indexes can help to search and locate the rows that
we want to modify, however, too many indexes to update can actually hurt the performance
of data modifications. This leads to a delicate balancing act when tuning the database for
performance.
A Disadvantage to Clustered Indexes
If we update a record and change the value of an indexed column in a clustered index, the
database might need to move the entire row into a new position to keep the rows in sorted
order. This behavior essentially turns an update query into a DELETE followed by an
INSERT, with an obvious decrease in performance. A table’s clustered index can often be
found on the primary key or a foreign key column because key values generally do not
change once a record is inserted into the database.
Built-in String Functions in SQL Server
The Functions in SQL server broadly divided into 2 types
1. Built-in function
2. User-Defined Functions
Built-In string functions in SQL Server are used with SQL SELECT expressions to calculate
values and manipulate the data. These built-in functions can be used anywhere, where
expressions are allowed.
What are Built-In Functions?
In SQL Server a built-in function is a piece of code that takes one or more inputs and
returns a value.  An example of a built-in function is ABS(), which when given a value
calculates the absolute (non-negative) value of the number.
Note: SQL built-in functions may or may not take parameters but always returns a value.
There are several built-in functions available in SQL Server. In this article, we will discuss
the most common use of built-in string functions in SQL Server.
ASCII(Character_Expression)
The ASCII function returns the ASCII code that represents a specific character.
To find the ASCII Code of the capital letter ‘A’
Example: Select ASCII(‘A’)
Output: 65
This function is commonly used for comparing characters without knowing whether they are
in the upper case or lower case. Uppercase and lower case letters translate into different
ASCII values
Let’s see with an example.
Select ASCII(‘A’) as UpperCase, ASCII(‘a’) as LowerCase
OUTPUT:

CHAR(Integer_Expression)
This function converts an integer ASCII code to a character. That means the CHAR function
does the opposite of the ASCII function. The Integer_Expression should be between 0 and
255.
Printing uppercase alphabets using CHAR() function:
DECLARE @Number int

SET @Number = 65

WHILE(@Number <= 90)

BEGIN

PRINT CHAR(@Number)

SET @Number = @Number + 1

END
Note: The while loop will become an infinite loop if you forget to include the following line.
Set @Number = @Number + 1
Printing lowercase alphabets using CHAR() function:
DECLARE @Number int

SET @Number = 97

WHILE(@Number <= 122)

BEGIN

PRINT CHAR(@Number)

SET @Number = @Number + 1

END
Another way of printing lower case alphabets using CHAR() and LOWER()
functions.
DECLARE @Number int

SET @Number = 65

WHILE(@Number <= 90)

BEGIN

PRINT LOWER(CHAR(@Number))

SET @Number = @Number + 1

END
LTRIM(Character_Expression)
This function returns a character expression after it removes the leading blanks. That
means it removes blanks on the left-hand side of the given character expression.
Example: Removing the 3 white spaces on the left-hand side of the ‘   Hello’ string using
LTRIM() function.
Select LTRIM(‘   Hello’)
Output: Hello
RTRIM(Character_Expression)
This function returns a character string after truncating all trailing blanks. That means it
removes blanks on the right-hand side of the given character expression.
Example: Removing the 3 white spaces on the left-hand side of the ‘Hello   ‘ string using
RTRIM() function.
Select RTRIM(‘Hello   ‘)
Output: Hello
If you want to remove white spaces on either side of the given character expression, then
you need to use LTRIM() and RTRIM() as shown below.
Select LTRIM(RTRIM(‘   Hello   ‘))
Output: Hello
LOWER(Character_Expression)
This function returns a character expression after converting all the uppercase character
data to lowercase. That means it converts all the characters in the given
Character_Expression to lowercase letters.
Example: Select LOWER(‘CONVERT This String Into Lower Case’)
Output: convert this string into lower case
UPPER(Character_Expression)
This function returns a character expression with lowercase character data converted to
uppercase. That means it converts all the characters in the given Character_Expression to
uppercase letters.
Example: Select UPPER(‘CONVERT This String Into upperCase’)
Output: CONVERT THIS STRING INTO UPPERCASE
REVERSE(‘Any_String_Expression’)
This function returns the reverse of a character expression. That means it reverses all the
characters in the given string expression.
Example: Select REVERSE(‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’)
Output: ZYXWVUTSRQPONMLKJIHGFEDCBA
LEN(String_Expression)
This function returns the number of characters, instead of the number of bytes, of the
specified string expression, excluding trailing blanks. That means it returns the count of total
characters in the given string expression, excluding the blanks at the end of the expression.
Example: Select LEN(‘ Functions   ‘)
Output: 10
LEFT(Character_Expression, Integer_Expression)
This function returns the left part of a character string with the specified number of
characters. That means it returns the specified number of characters from the left-hand side
of the given character expression.
Example: Select LEFT(‘ABCDE’, 3)
Output: ABC
RIGHT(Character_Expression, Integer_Expression)
This function returns the right part of a character string with the specified number of
characters. That means it returns the specified number of characters from the right-hand
side of the given character expression.
Example: Select RIGHT(‘ABCDE’, 3)
Output: CDE
CHARINDEX(‘Expression_To_Find’, ‘Expression_To_Search’, ‘Start_Location’)
This function returns the starting position of the specified expression in a character string.
The Start_Location parameter is optional.
Example: In this example, we get the starting position of ‘@’ character in the email string
‘sara@aaa.com’. 
Select CHARINDEX(‘@’,’hina@aaa.com’,1)
Output: 5
SUBSTRING(‘expression’, ‘Start’, ‘Length’)
As the name, suggests, this function returns substring (part of the string), from the given
expression. You specify the starting location using the ‘start’ parameter and the number of
characters in the substring using the ‘Length’ parameter. All 3 parameters are mandatory.
Example: Display just the domain part of the given email ‘info@dotnettutorials.net’.
Select SUBSTRING(‘info@dotnettutorials.net’,6, 19)
Output: dotnettutorials.net
In the above example, we have hardcoded the starting position and the length parameters.
Instead of hard-coding, we can dynamically retrieve them using CHARINDEX() and LEN()
string functions as shown below.
Example:
Select SUBSTRING(‘info@dotnettutorials.net’,(CHARINDEX(‘@’,
‘info@dotnettutorials.net’) + 1),
(LEN(‘info@dotnettutorials.net’) – CHARINDEX(‘@’,’info@dotnettutorials.net’)))
Output: dotnettutorials.net
OVER Clause in SQL Server with Examples
OVER Clause in SQL Server:
The OVER clause in SQL Server is used with PARTITION BY to break up the data into
partitions. Following is the syntax of the OVER clause.

The specified function is going to operate for each partition. See the following example. Let
say we have three departments (HR, IT, Payroll).
COUNT (Department) OVER (PARTITION BY Department) 
In the above example, the data will be partition by Department i.e. there will be three
partitions (IT, HR, and Payroll), and then the COUNT() function will be applied to each
partition. Here, you can use a wide range of built-in functions such as COUNT(), SUM(),
MAX(), ROW_NUMBER(), RANK(), DENSE_RANK(), AVG(), MIN(), etc.
Example: OVER clause in SQL Server
Let us see an example, to understand the use of the SQL Server Over clause. We are going
to use the following Employee table.

Please use the following SQL Script to create and populate the Employees table with
the required data.
CREATE TABLE Employees

ID INT,
Name VARCHAR(50),

Department VARCHAR(50),

Salary int

Go

INSERT INTO Employees Values (1, 'James', 'IT', 15000)

INSERT INTO Employees Values (2, 'Smith', 'IT', 35000)

INSERT INTO Employees Values (3, 'Rasol', 'HR', 15000)

INSERT INTO Employees Values (4, 'Rakesh', 'Payroll', 35000)

INSERT INTO Employees Values (5, 'Pam', 'IT', 42000)

INSERT INTO Employees Values (6, 'Stokes', 'HR', 15000)

INSERT INTO Employees Values (7, 'Taylor', 'HR', 67000)

INSERT INTO Employees Values (8, 'Preety', 'Payroll', 67000)

INSERT INTO Employees Values (9, 'Priyanka', 'Payroll', 55000)

INSERT INTO Employees Values (10, 'Anurag', 'Payroll', 15000)

INSERT INTO Employees Values (11, 'Marshal', 'HR', 55000)

INSERT INTO Employees Values (12, 'David', 'IT', 96000)


Example:
We need to generate a report to display the total number of employees department-wise.
Along with this we also need to display the Total Salary, Average Salary, Minimum Salary,
and Maximum Salary department wise. That means we need to generate a report like
below.
We can easily achieve the above data simply by using the GROUP BY clause in SQL
Server. The following SQL Query will give you the desired output.
SELECT Department,

COUNT(*) AS NoOfEmployees,

SUM(Salary) AS TotalSalary,

AVG(Salary) AS AvgSalary,

MIN(Salary) AS MinSalary,

MAX(Salary) AS MaxSalary

FROM Employees

GROUP BY Department
Example:
Now the business requirement changes, now we also need to show the non-aggregated
values (Name and Salary) in the report along with the aggregated values as shown in the
below image.

You may be intended to use the following SQL query by adding the Salary, Name column in
the select clause. But this is not going to be work.
SELECT Name, Salary, Department,

COUNT(*) AS NoOfEmployees,

SUM(Salary) AS TotalSalary,

AVG(Salary) AS AvgSalary,

MIN(Salary) AS MinSalary,

MAX(Salary) AS MaxSalary

FROM Employees

GROUP BY Department
When you execute the above query, you will get the following error. This is because it is not
possible to include the non-aggregated column in the select list when you are using a group
by clause in SQL Server.

How can we achieve the desired output?


We can get the desired output in two ways.
Solution1:
One of the ways to get the desired output is by including all the aggregations in a subquery
and then JOINING that subquery with the main query. The following example exactly does
the same.
SELECT Name, Salary, Employees.Department,

Departments.DepartmentTotals,

Departments.TotalSalary,

Departments.AvgSalary,

Departments.MinSalary,

Departments.MaxSalary

FROM Employees

INNER JOIN

( SELECT Department, COUNT(*) AS DepartmentTotals,


SUM(Salary) AS TotalSalary,

AVG(Salary) AS AvgSalary,

MIN(Salary) AS MinSalary,

MAX(Salary) AS MaxSalary

FROM Employees

GROUP BY Department) AS Departments

ON Departments.Department = Employees.Department
Once you execute the above query then you will get the desired output. But look at the
number of T-SQL statements that we wrote.
Solution2:
The second way that is the most preferable way to get the desired output is by using the
OVER clause combined with the PARTITION BY clause as shown in the below code.
SELECT Name,

Salary,

Department,

COUNT(Department) OVER(PARTITION BY Department) AS DepartmentTotals,

SUM(Salary) OVER(PARTITION BY Department) AS TotalSalary,

AVG(Salary) OVER(PARTITION BY Department) AS AvgSalary,

MIN(Salary) OVER(PARTITION BY Department) AS MinSalary,

MAX(Salary) OVER(PARTITION BY Department) AS MaxSalary

FROM Employees
Once you execute the above code, you will get the output as expected.
Row_Number Function in SQL Server with Examples
1. What is ROW_NUMBER Function in SQL Server?
2. Understanding the importance of PARTITION BY and ORDER BY Clause in
ROW_NUMBER Function.
3. How to use the ROW_NUMBER Function with and without the PARTITION BY
clause?
4. Multiple examples to understand the Row_Number function.
5. Finally, one real-time example of the ROW_NUMBER Function.
ROW_NUMBER Function in SQL Server:
The Row Number function was introduced in SQL Server 2005. The ROW_NUMBER
function is basically used when you want to return a sequential number starting from 1.
The ROW_NUMBER() is a built-in function in SQL Server that assigns a sequential integer
number to each row within a partition of a result set. The row number always starts with 1
for the first row in each partition and then increases by 1 for the next row onwards in each
partition. The syntax to use the ROW_NUMBER function is given below.

PARTITION BY value_expression:
This is optional. The PARTITION BY clause is used to divide the result set that is produced
by the FROM clause into partitions and then the ROW_NUMBER function is applied to each
partition. Here, the value_expression specifies the column name (s) using which the result
set is going to be partitioned. As it is optional, if you did not specify the PARTITION BY
clause, then the ROW_NUMBER function will treat all the rows of the query as a single
group.
Order_by_clause:
It is required. The ORDER BY clause is basically used to define the sequence in which
each row is going to assign its unique ROW_NUMBER.
Note: If you did not use the PARTITION BY clause with the ROW_NUMBER function, then
the result set will consider as a single partition.
Example: Row_Number Function in SQL Server
Let us see some examples to understand the ROW NUMBER function in SQL Server. We
are going to use the following Employee table.
Please use the following SQL Script to create and populate the Employees table with
the required data.
CREATE TABLE Employees

ID INT,

Name VARCHAR(50),

Department VARCHAR(50),

Salary int

Go

INSERT INTO Employees Values (1, 'James', 'IT', 15000)

INSERT INTO Employees Values (2, 'Smith', 'IT', 35000)

INSERT INTO Employees Values (3, 'Rasol', 'HR', 15000)

INSERT INTO Employees Values (4, 'Rakesh', 'Payroll', 35000)

INSERT INTO Employees Values (5, 'Pam', 'IT', 42000)


INSERT INTO Employees Values (6, 'Stokes', 'HR', 15000)

INSERT INTO Employees Values (7, 'Taylor', 'HR', 67000)

INSERT INTO Employees Values (8, 'Preety', 'Payroll', 67000)

INSERT INTO Employees Values (9, 'Priyanka', 'Payroll', 55000)

INSERT INTO Employees Values (10, 'Anurag', 'Payroll', 15000)

INSERT INTO Employees Values (11, 'Marshal', 'HR', 55000)

INSERT INTO Employees Values (12, 'David', 'IT', 96000)


ROW_NUMBER Function without PARTITION BY:
Let us see an example of the ROW_NUMBER function without using the PARTITION BY
Clause. As we already discussed if we did not specify the PARTITION BY Clause, then the
ROW_NUMBER function will treat the whole result set as a single group. As a result, the
ROW_NUMBER function will provide a consecutive sequence numbering for all the rows
present in the result set based on the column(s) specified in the order by clause. The
following is an example of the ROW_NUMBER function without using the PARTITION BY
clause.
SELECT Name, Department, Salary,
ROW_NUMBER() OVER (ORDER BY Department) AS RowNumber
FROM Employees
Once you execute the above query, you will get the below output. As you can see in the
below output, there will be no partition and hence all the rows are assigned with consecutive
sequence numbering starting from 1 to 12.
Note: If you did not specify the ORDER BY clause then you will get the error stating “The
function ‘ROW_NUMBER’ must have an OVER clause with ORDER BY”.
Row_Number Function with PARTITION BY Clause:
Let us see an example of the ROW_NUMBER function using the PARTITION BY Clause.
When you specify the PARTITION BY Clause, then the data is partitioned based on the
column you specify in the Partition BY clause. Please have a look at the following image to
understand this better. As you can see we have specified Department in the Partition By
clause and Name in the Order By clause.
In our example, we have three departments. So, the Partition By Clause will divide all the
rows into three partitions or you can say three groups. Then in each group or in each
partition, the data is sorted based on the Name column. The ROW_NUMBER function then
gives a unique sequence to each partition starting from 1. As we have three partitions, each
partition will start from 1 that which you can see in the below image.
Now execute the following code and you will get the output as we discussed in the
previous image.
SELECT Name, Department, Salary,

ROW_NUMBER() OVER

PARTITION BY Department

ORDER BY Name

) AS RowNumber
FROM Employees
Note: When the partition changes the row number is reset to 1. So, basically whenever you
want to give a unique number on the fly to the rows in the result set, then you need to use
the ROW_NUMBER function.
Real-time Example of ROW_NUMBER function in SQL Server:
Let us see one real-time example of the ROW_NUMER function in SQL Server. To
understand this first delete all the records which are there in the Employees table by
executing the following SQL query.
TRUNCATE TABLE Employees
Once you delete all the data from the Employees table then please insert the following
records into the Employees table.
INSERT INTO Employees Values (1, 'James', 'IT', 15000)

INSERT INTO Employees Values (1, 'James', 'IT', 15000)

INSERT INTO Employees Values (2, 'Rasol', 'HR', 15000)

INSERT INTO Employees Values (2, 'Rasol', 'HR', 15000)

INSERT INTO Employees Values (2, 'Rasol', 'HR', 15000)

INSERT INTO Employees Values (3, 'Stokes', 'HR', 15000)

INSERT INTO Employees Values (3, 'Stokes', 'HR', 15000)

INSERT INTO Employees Values (3, 'Stokes', 'HR', 15000)

INSERT INTO Employees Values (3, 'Stokes', 'HR', 15000)


Now, intentionally we have inserted some duplicate data into the Employees table as shown
below.
Now we need to delete the duplicate records from the Employees table. Once you delete
the duplicate data, then your table should have the following record.

The following SQL query will delete the duplicate records from the Employees table. Here,
the PARTITION BY clause divides the query result set into partitions based on the ID
values. That means it will create multiple partitions based on the ID values and give
sequential integer numbers starting from 1 for each partition. Then here the DELETE
statements will delete the records from each partition where the RowNumber is greater than
1. It means it will keep only one record from each partition and delete the rest of all.
WITH DeleteDuplicateCTE AS
(
     SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ID) AS
RowNumber
     FROM Employees
)
DELETE FROM DeleteDuplicateCTE WHERE RowNumber > 1
Once you execute the above SQL query, now verify the Employees table and you can see
the duplicates records are deleted. So, in short, the ROW_NUMBER function Returns an
increasing unique number for each row starting from 1, even if there are duplicates.
Scalar Valued Function in SQL Server with Examples
Before understanding the Scalar Function in SQL Server, let us first understand what is a
function and the different types of functions available in SQL Server.
What is a function in SQL Server?
A function in SQL Server is a subprogram that is used to perform an action such as
complex calculation and returns the result of the action as a value. There are two types of
functions in SQL Server, such as
1. System Defined Function
2. User-Defined Function
The functions which are already defined by the system and ready to be used by the
developer are called system-defined functions whereas if the function is defined by the user
or developer then such types of functions are called the user-defined function.
Some functions take parameters; do some processing and returning some results back. For
example SELECT SQUARE(3)
Some functions may not take any parameters, but returns some result, for
example, SELECT GETDATE()
So we can say that a function can have a parameter that is optional but a function should
always return a value that is mandatory.
Types of User-Defined Function:
In SQL Server, we can create three types of User-Defined Functions, such as
1. Scalar Valued Functions
2. Inline Table-Valued Functions
3. Multi-Statement Table-Valued Functions
In this article, we are going to discuss SQL Server Scalar Valued Function in detail with
Examples, the rest two user-defined functions are going to be discussed in our next article.
SQL Server Scalar Valued Functions
The user-defined function which returns only a single value (scalar value) is known as the
Scalar Valued Function. Scalar Value Functions in SQL Server may or may not have
parameters that are optional but always return a single (scalar) value which is mandatory.
The returned value which is return by the SQL Server Scalar Function can be of any data
type, except text, ntext, image, cursor, and timestamp. Following is the syntax to create
a User-Defined Scalar Value Function in SQL Server.
As you can see in the above image, we can use Create Function followed by the function
name Statement to create a user-defined function in SQL Server. Then we need to specify
the input parameters. It is optional. Then we need to specify the return value data type i.e.
going to be returned from the function body by using the Returns followed by the return data
type statement. This is mandatory. Then we need to write the function body in between the
AS BEGIN and the AND block. From the function body, at some point, we need to write the
Return statement followed by the return value. This is mandatory. 
Syntax for calling a Function in SQL Server:
SELECT dbo.<FunctionName>(Value)
When calling a scalar user-defined function we must specify the two-part name i.e. owner
name and function name. The dbo stands for the database owner name. We can also
invoke a scalar function in SQL Server using the complete three-part name i.e. database
name. Owner name and function name.
Examples: SQL Server Scalar Valued User-Defined Functions
Let us understand the SQL Server Scalar User-Defined Function with some Examples.
Example1: Create a Scalar Function in SQL Server which will return the cube of a
given value.
The function will an integer input parameter and then calculate the cube of that integer
value and then returns the result. Here, the result produced will be in Integer. So, while
creating the function, we need to specify the Returns data type as INT. The following
function exactly does the same.
CREATE FUNCTION SVF1(@X INT)

RETURNS INT

AS
BEGIN

RETURN @X * @X *@X

END
Once you created the function, as it is a database object, so it will be physically created
inside the database with the following folder location. As it is a Scalar function, so it is
created inside the Scalar-Valued Function folder as shown in the below image.

To execute the above function call it like below


SELECT dbo.SVF1(5)
Output: 125
Example2: Write a Scalar Function to get the date difference.
Let us now create a function that will calculate and returns the age of an employee. To
compute the age we require the date of birth. So, let’s pass the date of birth as a parameter.
So the user-defined CalculateAge() function returns an integer and accepts date parameter.
CREATE FUNCTION CalculateAge

@DOB DATE

RETURNS INT

AS

BEGIN
DECLARE @AGE INT

SET @AGE = DATEDIFF(YEAR, @DOB, GETDATE())-

CASE

WHEN (MONTH(@DOB) > MONTH(GETDATE())) OR

(MONTH(@DOB) = MONTH(GETDATE()) AND

DAY(@DOB) > DAY(GETDATE()))

THEN 1

ELSE 0

END

RETURN @AGE

END
Calling the above function:
SELECT dbo.CalculateAge (’02/29/1988′)
SELECT dbo.CalculateAge (’02/29/1988′) AS AGE
Scalar Valued Function in Select Clause:
The User Defined Scalar Valued Function can also be used in the select clause of an SQL
Query in SQL Server. To understand the above points we are going to use the following
Employee table.

Please use the following SQL Script to create and populate the Employee table with the
required sample data.
-- Create Table Employee

CREATE TABLE Employee

ID INT PRIMARY KEY,


Name VARCHAR(50),

DOB DATETIME

GO

-- Populate Employee table with some test data

INSERT INTO Employee(ID, Name, DOB) VALUES(1, 'Pranaya', '1988-02-29


21:29:16.667')

INSERT INTO Employee(ID, Name, DOB) VALUES(2, 'Kumar', '1989-03-27


21:29:16.667')

INSERT INTO Employee(ID, Name, DOB) VALUES(3, 'Rout', '1990-04-15 21:29:16.667')


Let’s see how to use the SQL Server Scalar Function i.e. CalculateAge function in the
Select clause of an SQL Query. As you can see in the below query, in the select clause we
call the CalculateAge function by passing the DOB as an input parameter. The
CalculateAge function takes the DOB value and returns the Age.
SELECT ID, Name, DOB, dbo.CalculateAge(DOB) AS Age

FROM Employee
Once you execute the above query, you will get the following output.

SQL Server Scalar Valued Function in Where Clause:


Let’s see how to use the user-defined Scalar Valued function (CalculateAge) in the where
clause of an SQL Query. Let’s find out the details of those employees whose age is greater
than 31. At the current date, we have two employees whose age is greater than 31.
SELECT ID, Name, DOB, dbo.calculateAge(DOB) AS Age

FROM Employee

WHERE dbo.calculateAge(DOB) > 31


Once you execute the above query, you will get the following output.
If you want to alter the function then you need to use ALTER
FUNCTION FuncationName statement and to delete the function, you need to use DROP
FUNCTION FuncationName. To view the text of the user-defined function you need to
use sp_helptext FunctionName
A stored procedure can also accept the DOB of an employee and return the age but we
cannot use a stored procedure in a select clause or where clause. This is one of the
differences between a function and a stored procedure. In our upcoming articles, we will
discuss more differences between them.
Example:
CREATE PROCEDURE spCalcualateAge(@DOB DATE)

AS

BEGIN

DECLARE @AGE INT

SET @AGE =DATEDIFF(YEAR, @DOB,GETDATE())-

CASE

WHEN (MONTH(@DOB)>MONTH(GETDATE()))OR

(MONTH(@DOB)=MONTH(GETDATE())AND

DAY(@DOB)>DAY(GETDATE()))

THEN 1

ELSE 0

END

SELECT @AGE

END

--Execute

Execute spCalcualateAge '02/29/1988'


Inline Table-Valued Function in SQL Server with
Examples
What is a Table-Valued Function in SQL Server?
In the case of a Table-Valued Function, we can return a table as an output from the
function. These are again of two types.
1. Inline Table-valued Function
2. Multi-statement table value function
Note: In this article, we are going to discuss Inline Table-Valued Function and in our next
article, we will discuss Multi-Statement Table-valued Functions with Examples.
What are Inline Table-Valued functions in SQL Server?
In the case of an Inline Table-Valued Function, the body of the function will have only
a Single Select Statement prepared with the “RETURN” statement. And here, we need to
specify the Return Type as TABLE by using the RETURNS TABLE statement. The
following image shows the syntax of the Inline Table-Valued Function and how to call an
Inline Table-Valued Function in SQL Server.

Points to Remember:
1. We specify TABLE as the Return Type instead of any scalar data type.
2. The function body is not closed between BEGIN and END blocks. This is because
the function is going to return a single select statement.
3. The structure of the Table that is going to be returned is determined by the select
statement used in the function.
Example: Inline Table-Valued Function in SQL Server 
Let us understand the Inline Table-Valued Function in SQL Server with some examples. We
are going to use the following Student table to understand this concept.
Please use the following SQL Script to create and populate the Student table with the
required sample data which we are going to use.
-- Create Student Table

CREATE TABLE Student

ID INT PRIMARY KEY,

Name VARCHAR(50),

Gender VARCHAR(50),

DOB DATETIME,

Branch VARCHAR(50)

-- Populate the Student Table with test data

INSERT INTO Student VALUES(1, 'Pranaya', 'Male','1996-02-29 10:53:27.060', 'CSE')

INSERT INTO Student VALUES(2, 'Priyanka', 'Female','1995-05-25 10:53:27.060', 'CSE')

INSERT INTO Student VALUES(3, 'Anurag', 'Male','1995-04-19 10:53:27.060', 'ETC')

INSERT INTO Student VALUES(4, 'Preety', 'Female','1996-03-17 10:53:27.060', 'ETC')

INSERT INTO Student VALUES(5, 'Sambit', 'Male','1997-01-15 10:53:27.060', 'CSE')


Example:
Create a function that accepts student id as input and returns that student details
from the table.
CREATE FUNCTION FN_GetStudentDetailsByID

@ID INT

RETURNS TABLE

AS

RETURN (SELECT * FROM Student WHERE ID = @ID)


Once you create the above function, then call it like below.
SELECT * FROM FN_GetStudentDetailsByID(2)
Once you execute the above SQL Statement, it will give you the following output.

Example:
Create a function to accept branch name as input and returns the list of students who
belongs to that branch.
CREATE FUNCTION FN_GetStudentDetailsByBranch

@Branch VARCHAR(50)

RETURNS TABLE

AS

RETURN (SELECT * FROM Student WHERE Branch = @Branch)


Once you create the above function, then call it by executing the below statement.
SELECT * FROM FN_GetStudentDetailsByBranch(‘CSE’)
Once you execute the above SQL Statement, it will give you the following output.
Note: As the inline table-valued user-defined function, is returning a table, issue the select
statement against the function, as if we are selecting the data from a TABLE.
Example:
Create a function that returns student Name, DOB, and Branch by GENDER.
CREATE FUNCTION FN_GetStudentDetailsByGender

@Gender VARCHAR(50)

RETURNS TABLE

AS

RETURN (SELECT Name, DOB, Branch FROM Student WHERE Gender = @Gender)
Once you create the above function, then call it by executing the below statement.
SELECT * FROM FN_GetStudentDetailsByGender(‘Male’)
Once you execute the above SQL Statement, it will give you the following output.

Where can we use Inline Table-valued Functions in SQL Server?


The Inline Table-Valued function in SQL Server can be used to achieve the functionalities of
parameterized views, and the table returned by the inline table-valued function in SQL
Server can also be used in joins with other tables.
Inline Table-Valued Function with JOINs in SQL Server
Let us understand how to use Inline Table-Valued Function with Joins with an example. We
are going to use the following Department and Employee tables to understand this concept.
Please use the following SQL Script to create and populate the Department and Employee
tables with sample data.
-- Create Department Table

CREATE TABLE Department

ID INT PRIMARY KEY,

DepartmentName VARCHAR(50)

GO

-- Populate the Department Table with test data

INSERT INTO Department VALUES(1, 'IT')

INSERT INTO Department VALUES(2, 'HR')

INSERT INTO Department VALUES(3, 'Sales')

GO

-- Create Employee Table

CREATE TABLE Employee

(
ID INT PRIMARY KEY,

Name VARCHAR(50),

Gender VARCHAR(50),

DOB DATETIME,

DeptID INT FOREIGN KEY REFERENCES Department(ID)

GO

-- Populate the Employee Table with test data

INSERT INTO Employee VALUES(1, 'Pranaya', 'Male','1996-02-29 10:53:27.060', 1)

INSERT INTO Employee VALUES(2, 'Priyanka', 'Female','1995-05-25 10:53:27.060', 2)

INSERT INTO Employee VALUES(3, 'Anurag', 'Male','1995-04-19 10:53:27.060', 2)

INSERT INTO Employee VALUES(4, 'Preety', 'Female','1996-03-17 10:53:27.060', 3)

INSERT INTO Employee VALUES(5, 'Sambit', 'Male','1997-01-15 10:53:27.060', 1)

INSERT INTO Employee VALUES(6, 'Hina', 'Female','1995-07-12 10:53:27.060', 2)

GO
Let’s first create an Inline Table-Valued Function that returns the Employees by
Gender from the Employees table.
CREATE FUNCTION FN_GetEmployeessByGender

@Gender VARCHAR(50)

RETURNS TABLE

AS

RETURN (SELECT ID, Name, Gender, DOB, DeptID FROM Employee WHERE Gender =
@Gender)
Now, let’s join the Employees returned by the inline table-valued function with the
Departments table as shown below
SELECT Name, Gender, DOB, DepartmentName

FROM FN_GetEmployeessByGender('Male') Emp

JOIN Department Dept on Dept.ID = Emp.DeptID


When we execute the above query, it will give us the following output.

Example: Table-valued Function Returning data From two Tables using Join
in SQL Server
In the below example we are joining the Employee and Department table and returning data
from the function body as a single SQL Statement. This is very similar to Views but here we
are taking an input parameter and based on the parameter we are returning the result. This
is not possible (input parameter) in the case of SQL Server Views.
CREATE FUNCTION FN_EmployeessByGender

@Gender VARCHAR(50)

RETURNS TABLE

AS

RETURN (

SELECT Emp.ID, Name, Gender, DOB, DepartmentName

FROM Employee Emp

JOIN Department Dept on Emp.DeptId = Dept.Id

WHERE Gender = @Gender)


Once you create the above function, then call it by executing the below statement.
SELECT * FROM dbo.FN_EmployeessByGender(‘Female’);
Once you execute the above SQL Statement, it will give you the following output.
Multi-Statement Table Valued Function in SQL Server
Multi-Statement Table-Valued Function in SQL Server
The Multi-Statement Table Valued Function in SQL Server is the same as the Inline Table-
Valued Function means it is also going to returns a table as an output but with the following
differences.
1. The Multi-Statement Table-Valued Function body can contain more than one
statement. In Inline Table-Valued Function, it contains only a single Select statement
prepared by the return statement.
2. In Multi-Statement Table-Valued Function, the structure of the table returned from
the function is defined by us. But, in Inline Table-Valued Function, the structure of
the table is defined by the Select statement that is going to return from the function
body.
The following image shows the syntax of the Multi-Statement Table-Valued Function in SQL
Server.

Note: In the case of Multi-Statement Table Valued Function in SQL Server, we need to
define the structure of the table being return.
Example: Multi-Statement Table-Valued Function in SQL Server
Let us understand Multi-Statement Table-Valued Function comparing with the Inline Table-
Valued Function in SQL Server with an example. We are going to use the following
Department and Employee tables.
Please use the below SQL Script to create and populate the Department and
Employee tables with sample data.
-- Create Department Table

CREATE TABLE Department

ID INT PRIMARY KEY,

DepartmentName VARCHAR(50)

GO

-- Populate the Department Table with test data

INSERT INTO Department VALUES(1, 'IT')

INSERT INTO Department VALUES(2, 'HR')

INSERT INTO Department VALUES(3, 'Sales')

GO

-- Create Employee Table

CREATE TABLE Employee

(
ID INT PRIMARY KEY,

Name VARCHAR(50),

Gender VARCHAR(50),

DOB DATETIME,

DeptID INT FOREIGN KEY REFERENCES Department(ID)

GO

-- Populate the Employee Table with test data

INSERT INTO Employee VALUES(1, 'Pranaya', 'Male','1996-02-29 10:53:27.060', 1)

INSERT INTO Employee VALUES(2, 'Priyanka', 'Female','1995-05-25 10:53:27.060', 2)

INSERT INTO Employee VALUES(3, 'Anurag', 'Male','1995-04-19 10:53:27.060', 2)

INSERT INTO Employee VALUES(4, 'Preety', 'Female','1996-03-17 10:53:27.060', 3)

INSERT INTO Employee VALUES(5, 'Sambit', 'Male','1997-01-15 10:53:27.060', 1)

INSERT INTO Employee VALUES(6, 'Hina', 'Female','1995-07-12 10:53:27.060', 2)

GO
Example:
Let’s write both Inline and Multi-Statement Table-Valued functions in SQL Server that
return the following output.
Using Inline Table-Valued function
-- Inline Table Valued function:

CREATE FUNCTION ILTVF_GetEmployees()

RETURNS TABLE

AS

RETURN (SELECT ID, Name, Cast(DOB AS Date) AS DOB

FROM Employee)
Calling the Inline Table-Valued Function: SELECT * FROM ILTVF_GetEmployees()
Using Multi-Statement Table-Valued function
-- Multi-statement Table Valued function:

CREATE FUNCTION MSTVF_GetEmployees()

RETURNS @Table Table (ID int, Name nvarchar(20), DOB Date)

AS

BEGIN

INSERT INTO @Table

SELECT ID, Name, Cast(DOB AS Date)

FROM Employee

Return

End
Calling the Multi-statement Table Valued Function: SELECT * FROM
MSTVF_GetEmployees()
What are the differences between Inline and Multi-Statement Table-Valued
Functions in SQL Server?
1. In an Inline Table-Valued Function, the returns clause cannot define the structure
of the table that the function is going to return whereas in the Multi-Statement
Table-Valued Function the returns clause defines the structure of the table that the
function is going to return.
2. The Inline Table-Valued Function cannot have BEGIN and END blocks whereas
the Multi-Statement Table-Valued Function has the Begin and End blocks.
3. It is possible to update the underlying database table using the inline table-valued
function but it is not possible to update the underlying database table using the multi-
statement table-valued function.
4. Inline Table-Valued functions are better for performance than the Multi-Statement
Table-Valued function. So, if the given task can be achieved using an Inline Table-
Valued Function, then it is always preferred to use Inline Table-valued Function over
the Multi-Statement Table-Valued function.
Reason For Better Performance: Internally SQL Server treats an Inline Table-Valued
function much like a view and treats a Multi-Statement Table-Valued function as a stored
procedure.
Example: Update underlying database table using the inline table-valued
function in SQL Server
SELECT * FROM dbo.ILTVF_GetEmployees()
For the above function, Employee is the underlying database table.
UPDATE ILTVF_GetEmployees() SET Name=’Pranaya1′ WHERE ID= 1
The above update query will change the name Pranaya to Pranaya1, in the underlying
table Employee. When we try to do the same thing with the multi-statement table-valued
function, we will get an error stating ‘Object ‘MSTVF_GetEmployees’ cannot be
modified.‘ The reason is that the multi-statement table-valued function did not get the data
directly from the underlying database table instead it gets the data from the table variable.
Note: In Inline Table-Valued functions, we get the data directly from the underlying base
table(s), and in the case of the Multi-Statement Table-Valued function, it gets the data from
the table variable.
What is the Difference Between Functions and Procedures in SQL Server?
1. A function must return a value, it is mandatory whereas a procedure returning a
value is optional.
2. The procedure can have parameters of both input and output whereas a function can
have only input parameters.
3. In a procedure, we can perform Select. Update, Insert and Delete operations
whereas function can only be used to perform select operations. It cannot be used to
perform Insert, Update, and Delete operations that can change the state of the
database.
4. A procedure provides the options to perform Transaction Management, Error
Handling, etc whereas these operations are not possible in a function.
5. We call a procedure using EXECUTE/ EXEC command whereas a function is called
by using SELECT command only.
6. From a procedure, we can call another procedure or a function whereas from a
function we can call another function but not a procedure.
7. User-Defined Functions can be used in the SQL statements anywhere in
the WHERE/HAVING/SELECT section where as Stored procedures cannot be.
SQL Server Common Table Expressions (CTE)

What is a Common Table Expression


A Common Table Expression, also called as CTE in short form, is a temporary named result set that
you can reference within a SELECT, INSERT, UPDATE, or DELETE statement. The CTE can also be used
in a View.

In this article, we will see in detail about how to create and use CTEs from our SQL Server.

Syntax and Examples for Common Table


Expressions
The CTE query starts with a “With” and is followed by the Expression Name. We will be using this
expression name in our select query to display the result of our CTE Query and be writing our CTE
query definition.

2 WITH expression_name [ ( column_name [,...n] ) ]

3 AS

4 ( CTE_query_definition )

To view the CTE result we use a Select query with the CTE expression name.

2 Select [Column1,Column2,Column3 …..] from expression_name

Or


2 Select * from expression_name

Common Table Expression (CTE) Types


There are two types of CTEs: Recursive and Non-Recursive

Non-Recursive CTEs

Non-Recursive CTEs are simple where the CTE doesn’t use any recursion, or repeated processing in
of a sub-routine. We will create a simple Non-Recursive CTE to display the row number from 1 to 10.

As per the CTE Syntax each CTE query will start with a “With” followed by the CTE Expression name
with column list.

Here we have been using only one column as ROWNO. Next is the Query part, here we write our
select query to be execute for our CTE. After creating our CTE query to run the CTE use the select
statement with CTE Expression name.

1  

2 ;with ROWCTE(ROWNO) as  

3    (  

4      SELECT

5   ROW_NUMBER() OVER(ORDER BY name ASC) AS ROWNO

6 FROM sys.databases

7 WHERE database_id <= 10

8     )  

9  

10 SELECT * FROM ROWCTE

11  

Output: When we run the query, we can see the below output.


Recursive CTE
Recursive CTEs are use repeated procedural loops aka recursion. The recursive query call themselves
until the query satisfied the condition. In a recursive CTE we should provide a where condition to
terminate the recursion.:

We will see how to create a simple Recursive query to display the Row Number from 1 to 10 using a
CTE.

Firstly we declare the Integer variable as “RowNo” and set the default value as 1 and we have created
our first CTE query as an expression name, “ROWCTE”. In our CTE we’ll first display the default row
number and next we’ll use a Union ALL to increment and display the row number 1 by one until the
Row No reaches the incremented value to 10. To view the result, we will use a select query to display
our CTE result.

1  

2 Declare @RowNo int =1;

3 ;with ROWCTE as  

4    (  

5       SELECT @RowNo as ROWNO    

6 UNION ALL  

7       SELECT  ROWNO+1  

8   FROM  ROWCTE  

9   WHERE RowNo < 10


10     )  

11  

12 SELECT * FROM ROWCTE

13  

Output: When we run the query, we can see the below output.

CTE Query to display Date Range:

Let’s consider as there is a scenario to display the date from start date to end date all one by one as
each row with details. In order to display the recursive data, we will be using the CTE Query.

Here we will write a CTE query to display the dates range with week number and day. For this we set
the start and end date in parameter. Here in this example we have used the getdate() to set the start
date as Todays date, and for end date we add 16 days from today.

CTE without Union All

Here we can see we have create a simple CTE query to display the RowNo, start date and week
number. When we run this we will get only one result with RowNo as “1” ,StartDate as current date
and week number along with week day.

1  

2 declare @startDate datetime,  

3         @endDate datetime;  
4   

5 select  @startDate = getdate(),  

6         @endDate = getdate()+16;  

7 -- select @sDate StartDate,@eDate EndDate  

8 ;with myCTE as  

9    (  

10       select 1 as ROWNO,@startDate StartDate,'W - '+convert(varchar(2),  

11             DATEPART( wk, @startDate))+' / D ('+convert(varchar(2),@startDate,106)+')' as 'WeekNumber'  

12     )  

13 select ROWNO,Convert(varchar(10),StartDate,105)  as StartDate ,WeekNumber from myCTE ;

14  

Output: When we run the query, we can see the below output.

CTE with Union All

In order to display the result from start date to end date one by one as recursive, we use a Union All
to increment RowNo, to add the day one by one till the condition satisfied the date range, in order
to stop the recursion we need set some condition. In this example, we repeat the recursion to display
our records until the date is less than or equal to the end date.

1  

2 declare @startDate datetime,  

3         @endDate datetime;  

4   

5 select  @startDate = getdate(),  
6         @endDate = getdate()+16;  

7 -- select @sDate StartDate,@eDate EndDate  

8 ;with myCTE as  

9    (  

10       select 1 as ROWNO,@startDate StartDate,'W - '+convert(varchar(2),  

11             DATEPART( wk, @startDate))+' / D ('+convert(varchar(2),@startDate,106)+')' as 'WeekNumber'      

12   union all  

13        select  ROWNO+1 ,dateadd(DAY, 1, StartDate) ,  

14               'W - '+convert(varchar(2),DATEPART( wk, StartDate))+' / D ('+convert(varchar(2),  

15                dateadd(DAY, 1, StartDate),106)+')' as 'WeekNumber'    

16   FROM  myCTE  

17   WHERE dateadd(DAY, 1, StartDate)<=  @endDate    

18     )  

19 select ROWNO,Convert(varchar(10),StartDate,105)  as StartDate ,WeekNumber from myCTE

20  

Output: When we run the query, we can see the below output.


Multiple CTE
In some scenarios, we need to create more than one CTE query and join them to display our result. In
this case, we can use the Multiple CTEs. We can create a multiple CTE query and combine them into
one single query by using the comma. Multiple CTE need to be separate by “,” comma fallowed by
CTE name.

We will be using above same date range example to use more than one CTE query, here we can see
as we have created two CTE query as CTE1 and CTE 2 to display date range result for both CTE1 and
for CTE2.

Example :

1  

2 Declare @startDate datetime,@endDate datetime;  

3 Declare @startDate1 datetime,@endDate1 datetime;  

4 Set  @startDate  = '2017-02-10'; Set  @endDate    = '2017-02-15';  

5 Set  @startDate1 = '2017-02-16'; Set  @endDate1   = '2017-02-28';          


6  

7  

8 WITH    CTE1  

9   AS (  

10     SELECT 'CTE1' CTEType ,@startDate StartDate,'W'+convert(varchar(2),DATEPART( wk, @startDate))

+'('+convert(varchar(2),@startDate,106)+')' as 'WeekNumber'  
11

    UNION ALL
12

    SELECT CTEType, dateadd(DAY, 1, StartDate) ,'W'+convert(varchar(2),DATEPART( wk, StartDate))


13
+'('+convert(varchar(2),dateadd(DAY, 1, StartDate),106)+')' as 'WeekNumber'
14
    FROM CTE1  
15
    WHERE dateadd(DAY, 1, StartDate)<=  @endDate  
16
             ),  
17
  CTE2  
18
  AS (  
19
       SELECT 'CTE2' CTEType, @startDate StartDate,'W'+convert(varchar(2),DATEPART( wk,
20
@startDate1))+'('+convert(varchar(2),@startDate1,106)+')' as 'WeekNumber'  

21
      UNION ALL

22
  SELECT 'CTE2' valuetype, dateadd(DAY, 1, StartDate) ,'W'+convert(varchar(2),DATEPART(

23 wk, StartDate))+'('+convert(varchar(2),dateadd(DAY, 1, StartDate),106)+')' as 'WeekNumber'        

24   FROM CTE2  

25   WHERE dateadd(DAY, 1, StartDate)<=  @endDate1  

26 )

27   
    SELECT CTEType, Convert(varchar(10),StartDate,105)  as StartDate ,WeekNumber    FROM    CTE1  

UNION ALL  
28
    SELECT CTEType, Convert(varchar(10),StartDate,105)  as StartDate ,WeekNumber    FROM    CTE2

Output: When we run the query, we can see the below output.

Using CTE query for SQL Table


Now let’s see on, how to use CTE query for our SQL server table data.

Create Database: First, we create a database for creating our table


1  

2 USE MASTER    

3 GO    

4 -- 1) Check for the Database Exists .If the database is exist then drop and create new DB    

5 IF EXISTS (SELECT [name] FROM sys.databases WHERE [name] = 'CTEDB' )    


6 DROP DATABASE CTEDB    

7 GO    

8     

9 CREATE DATABASE CTEDB    

10 GO    

11     

12 USE CTEDB    

13 GO  

14  

Create Table: Now we create a sample Item Table on the created Database.


1  

2 IF EXISTS ( SELECT [name] FROM sys.tables WHERE [name] = 'ItemDetails' )    

3 DROP TABLE ItemDetails    

4 GO    

5     

6 CREATE TABLE ItemDetails    

7 (    

8 Item_ID int identity(1,1),    

9 Item_Name VARCHAR(100) NOT NULL,    

10 Item_Price int NOT NULL,    

11 Date VARCHAR(100) NOT NULL ,

12 CONSTRAINT [PK_ItemDetails] PRIMARY KEY CLUSTERED    


(    
13

[Item_ID] ASC    
14

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,


15
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]    

16
) ON [PRIMARY]    

17
    

18
GO    

19
 

Insert Sample Data: We will insert few sample records for using in our CTE Query.
1  

2 Insert into ItemDetails(Item_Name,Item_Price,Date) values('Access Point',950,'2017-02-10')    

3 Insert into ItemDetails(Item_Name,Item_Price,Date) values('CD',350,'2017-02-13')    

4 Insert into ItemDetails(Item_Name,Item_Price,Date) values('Desktop Computer',1400,'2017-02-16')    

5 Insert into ItemDetails(Item_Name,Item_Price,Date) values('DVD',1390,'2017-03-05')    

6 Insert into ItemDetails(Item_Name,Item_Price,Date) values('DVD Player',450,'2017-05-07')    

7 Insert into ItemDetails(Item_Name,Item_Price,Date) values('Floppy',1250,'2017-05-07')    

8 Insert into ItemDetails(Item_Name,Item_Price,Date) values('HDD',950,'2017-07-10')      

9 Insert into ItemDetails(Item_Name,Item_Price,Date) values('MobilePhone',1150,'2017-07-10')    

10 Insert into ItemDetails(Item_Name,Item_Price,Date) values('Mouse',399,'2017-08-12')    

11 Insert into ItemDetails(Item_Name,Item_Price,Date) values('MP3 Player ',897,'2017-08-14')    

12 Insert into ItemDetails(Item_Name,Item_Price,Date) values('Notebook',750,'2017-08-16')    

13 Insert into ItemDetails(Item_Name,Item_Price, Date) values('Printer',675,'2017-07-18')    

14 Insert into ItemDetails(Item_Name,Item_Price,Date) values('RAM',1950,'2017-09-23')    


15 Insert into ItemDetails(Item_Name,Item_Price,Date) values('Smart Phone',679,'2017-09-10')    

16 Insert into ItemDetails(Item_Name,Item_Price,Date) values('USB',950,'2017-02-26')    

17     

18 select * from ItemDetails

19  

CTE Example:

Now we will create a simple temporary result using CTE Query. Here in this CTE Query we have given
the expression name as “itemCTE” and we have added the list of Columns which we use in the CTE
query. In the CTE query we display all item details with the year.

1  

2 ;WITH itemCTE (Item_ID, Item_Name, Item_Price,SalesYear)

3 AS

4 (

5   SELECT Item_ID, Item_Name, Item_Price ,YEAR(Date) SalesYear

6   FROM ItemDetails  

7 )

8  

9 Select * from itemCTE

10  

Output: When we run the query, we can see the below output.


CTE using Union ALL

Let’s consider there is a below two scenarios to display the result.

1. The first scenario is to display each Item Price of current Year.


2. The second scenario is to increment 10% to each Item Price for next year.

For this we use the above CTE Query. In this query, we add the UNION ALL and in UNION ALL Query
we do calculation to add 10% to each item Price and show in next row with adding one year.

1  

2 ;WITH itemCTE (Item_ID, Item_Name, Item_Price,MarketRate,SalesYear)

3 AS

4 (

5     SELECT Item_ID, Item_Name, Item_Price ,'Present Price' as MarketRate,YEAR(Date) as SalesYear

6     FROM ItemDetails  

7 UNION ALL

8 SELECT Item_ID as Item_ID, Item_Name,


9 (Item_Price + (Item_Price *10 )/100) as Item_Price,

10 'Future Price' as MarketRate,  YEAR(dateadd(YEAR, 1, Date))  as SalesYear

11     FROM ItemDetails

12     

13 )

14 SELECT * from itemCTE Order by Item_Name,SalesYear

15  

Output: When we run the query, we can see the below output.

Common Table Expressions (CTE) for Insert


Now we will see how to insert the CTE result to another table. For this let’s consider our above Item
Table. We insert the Item details result of above CTE query to Item History table. For this first we
create an Item History table.

Create Item History Table: In this history table, we add the same columns as item table along with
MarketRate column as present or future Item price. Here is the query to create an ItemHistory table.
1  
2
CREATE TABLE ItemHistory    
3
(    
4
ID int identity(1,1),
5
oldITEMID    int,
6
Item_Name VARCHAR(100) NOT NULL,    
7
Item_Price int NOT NULL,  
8
MarketRate  VARCHAR(100) NOT NULL,    
9
Date VARCHAR(100) NOT NULL )
10
 

CTE Insert Example:

Here we use above same CTE query Insert the result in to the Item History table. From this query we
insert both item details of present year Item price along with the next year Item prices added as 10%
more.

1  

2 ;WITH itemCTE (Item_ID, Item_Name, Item_Price,MarketRate,Date)

3 AS

4 (

5     SELECT Item_ID, Item_Name, Item_Price ,'Present Price' as MarketRate,Date  

6     FROM ItemDetails  

7 UNION ALL

8 SELECT Item_ID as Item_ID, Item_Name,(Item_Price + (Item_Price *10 )/100) as Item_Price,

9 'Future Price' as MarketRate,  dateadd(YEAR, 1, Date) as Date


10     FROM ItemDetails

11     

12 )

13 -- Define the outer query referencing the CTE name.

14 Insert into ItemHistory(oldITEMID ,Item_Name,Item_Price,MarketRate,Date)  

15 SELECT Item_ID, Item_Name, Item_Price,MarketRate,year(Date) from itemCTE Order by Item_Name,Date

16  

Output: When we run the query, we can see the below output as 30 records has been inserted into
our Item History table.

Select Query:

To view the item history result we select and display all the details.

2 select * from ItemHistory

Output: When we run the query, we can see the below output from item history table.
Create View with CTE Example:
Now we see how to use the above CTE query can be used in a view. Here we create a view and we
add the CTE result inside the view. When we select the view as a result, we can see the CTE output
will be displayed.

Example Query:
1  

2 CREATE VIEW CTEVIEW

3 AS

4 WITH    itemCTE1 AS

5         (

6          SELECT Item_ID, Item_Name, Item_Price ,'Present Price' as MarketRate,Date as IDate

7     FROM ItemDetails  
UNION ALL
8

SELECT Item_ID as Item_ID, Item_Name,(Item_Price + (Item_Price *10 )/100) as Item_Price,


9

'Future Price' as MarketRate,  dateadd(YEAR, 1, Date) as IDate


10

    FROM ItemDetails
11

      )
12

  
13

  SELECT Item_ID, Item_Name, Item_Price,MarketRate,year(IDate) as IDate from itemCTE1


14

15

16 GO

17

18
-- T-SQL test view

19
SELECT * FROM CTEVIEW Order by Item_Name,IDate

20
GO

21
 

Output: When we run the query, we can see the below output as result from the View.
How to write a clean CTE Query:

Here are some basic guidelines that need to be followed to write a good CTE Query.

1. A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE statement that
references some or all the CTE columns.
2. Multiple CTE query definitions can be defined in a non recursive CTE.
3. A CTE can reference itself and previously defined CTEs in the same WITH clause
4. We can use only one With Clause in a CTE
5. ORDER BY, INTO, COMPUTE or COMPUTE BY, OPTION, FOR XML cannot be used in non-
recursive CTE query definition
6. SELECT DISTINCT, GROUP BY, HAVING, Scalar aggregation, TOP, LEFT, RIGHT, OUTER JOIN
(INNER JOIN is allowed) subqueries cannot be used in a recursive CTE query definition.

Conclusion
CTEs can be used to create a recursive query and can be used to reference itself multiple times. CTEs
can be used instead of views and finally a CTE is easy and simple for readability and code
maintainability.

You might also like