Peter Lalovsky Learn Microsoft SQL Server Intuitively. Transact SQL The Solid Basics
Peter Lalovsky Learn Microsoft SQL Server Intuitively. Transact SQL The Solid Basics
Learn
Microsoft® SQL Server®
Intuitively
Transact-SQL:
The Solid Basics
Peter Lalovsky
Learn Microsoft® SQL Server® Intuitively
Transact-SQL: The Solid Basics
Copyright
©2016 zPL Concept
Notice of Rights
All rights reserved. No part of this publication may be reproduced, distributed, or transmitted in any form or by any
means, including photocopying, recording, or other electronic or mechanical methods, without the prior written
permission of the publisher.
Trademarks
All brand names, product names, and technologies presented in this book are trademarks or registered trademarks
of their respective owners.
Notice of Liability
The information in this book is distributed on an “As Is” basis, without warranty. While every precaution has been
taken in the preparation of the book, neither the author nor the publisher shall have any liability to any person or
entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the instructions
contained in this book or by the computer software and hardware products described in it.
Paperback ISBN
ISBN-13: 978-0995245105
Website
http://learnintuitively.lalovsky.com/sqlserver
Audience
Beginner/Intermediate
Table of Contents
The Basics
Intro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4
What is a Database (DB) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5
Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9
Working with DB (DB Professional Roles) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
DB Types (Transactional (OLTP) ETL Analytical (OLAP)) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
How the DBE is Structured . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Relational DBs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Naming Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
SQL and T-SQL (Transact-SQL) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
T-SQL Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
DDL and DML Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
CRUD (Create, Read, Update, Delete or INSERT, SELECT, UPDATE, DELETE) . . . . . . . . . . . . . . . . . . . . . . 55
Data Types and Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Collations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
NULL and 3VL (Three-valued Logic) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
SQL Server® Management Studio (SSMS) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
DDL Statements
CREATE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
ALTER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
DROP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Script Objects in SSMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
GROUP BY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
HAVING . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
GROUPING SETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
ROLLUP and CUBE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
Execution Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Subqueries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
UNION, EXCEPT and INTERSECT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
PIVOT and UNPIVOT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
Good practice
Comment, clarification and explanation
! Pay attention
1 ∞
One-to-Many relation
Linking tables
Hint
Sequence
Reference in the book
Intro
Looking at colors and forms and making connections between them creates associations in the brain.
The brain links the colors and forms by using its natural attribute – the intuition.
The following example will give you an exact explanation of the above statement.
Blue
Red
Green
This book Next you can I love add more and starts from more SQL Server® - scratch and and more knowledge one
of the to the solid gives you most powerful background, databases in a solid base built by the world. in SQL Serv-
er®. this book.
You know what to do and you did it. Your intuition helped you with this simple exercise.
We can do this:
This book Next you can I love add more and starts from more SQL Server® - scratch and and more knowledge one
of the to the solid gives you most powerful background, databases in a solid base built by the world. in SQL Serv-
er®. this book.
or this: Metadata
The database (DB) contains tables that store data. One of the usage of the DB is to serve webpages.
This book will help your intuition to teach you in Transact-SQL programing.
You can find the supporting source code, videos and more on http://learnintuitively.lalovsky.com/sqlserver.
4
What is a Database (DB)
Database (DB)
The Database (DB) is an electronic warehouse where we store data. The data in the DB is a representation of infor-
mation that exists in the real world and it is strictly organized. Our emails, social networks, bank accounts, medical
or school records are stored in DBs. We can store literally any information in a DB.
The following examples will help you draw the big picture.
Let’s move our contacts from the old notebook (paper) to a file (electronic).
First we create a text file like this:
After we add a few contacts, we notice that repeating Name, Email and Phone for every single contact is time and
space consuming. We convert the format of the text file to this:
Our next step is moving the data from the text file to a spreadsheet like Microsoft® Office Excel® or LibreOffice®
Calc - the simplest database.
The spreadsheet allows us to handle the information much easier than the text file - insert every slice of data in its
own cell, quick search, add filters on the columns, order the data etc.
After we start using the spreadsheet (actually this is our first DB), we add multiple emails and phones for a single
contact:
5
What is a Database (DB)
Oups! We discovered our first data issue: we used a comma (,) to split the data, but the comma itself is data. We fix
this issue by splitting the data in separate cells:
Later, we search for a contact and we are confused: Column Phone 1 is a mobile or business phone? The same ques-
tion stands for column Email. We add additional columns to define the type on the phones and emails:
After one year of using the spreadsheet, we notice that a person has 50 phones and we added 100 columns (Phone
and Phone Type pairs), but... what if we split the attributes (Email and Phone) into separate tables and one phone
for one contact is one row?
The next step is to convert our contacts DB from one table that stores all the data to multiple related tables:
Emails
ContactName Email Type
Anna Laurier anna.laurier@customer2.net Personal
Anna Laurier al@customer2.net Business
John Smith john.smith@customer1.com Business
John Smith js@customer1.com Business
6
What is a Database (DB)
We just built our first One-to-Many relation – One Contact may have multiple phones and emails.
In few months we decide to add description to the phone and email types. We find that it will be easier to manage
separate tables for email and phone types and link them to the tables that store email and phone. We also decide
to add pictures for our contacts. As we decide to show only one picture per contact, we add column Picture in table
Contacts.
The other significant change is the replacement of the long column headers ContactName, EmailType and Phone-
Type with short numbers, used to link the tables.
EmailTypes
EmailTypeID EmailTypeName EmailTypeDescription
1 Personal Send jokes, pictures and any other
personal data.
2 Business Related to business conversations.
Send contacts and invoices.
Emails
ContactID Email EmailTypeID
1 anna.laurier@customer2.net 1
1 al@customer2.net 2
2 john.smith@customer1.com 2
2 js@customer1.com 2
Contacts
ContactID ContactName Picture
Phones
ContactID Phone PhoneTypeID
1 +1 555 2222 1
1 +1 555 2223 2
2 +1 555 1111 2
2 +1 555 1112 2
2 +1 555 1113 3
PhoneTypes
PhoneTypeID PhoneTypeName PhoneTypeDescription
1 Home Stationary at home. Used by all family members.
2 Mobile Available all the time.
3 Business In business hours only. Located in the office.
7
What is a Database (DB)
The last step in our example is to transform the data in the DB to “human readable” reports like this
Anna Laurier
Emails
anna.laurier@customer2.net (Personal)
al@customer2.net (Business)
Phones
+1 555 2222 (Home)
+1 555 2223 (Mobile)
John Smith
Emails
john.smith@customer1.com (Business)
js@customer1.com (Business)
Phones
+1 555 1111 (Mobile)
+1 555 1112 (Mobile)
+1 555 1113 (Business)
or parameterized reports that will give us the option to search the contacts by City, Company, Job Title and any oth-
er way that we can imagine, based on the available data.
Already confused? It’s OK. The best thing about the database is that it is a never ending learning adventure! Let’s
stop complicating the example for now. We learned that:
• The DB stores data in electronic format
• The data in the DB is strictly organized
• The data in the DB relates to each other
We use software to manipulate the data in the DBs. This software is an interface (the point where two systems
cross) between the DBs and the DB professionals. It is known as RDBMS (Relational Database Management Sys-
tem). Some of the most popular RDBMS are:
8
Installation
This book is for Microsoft® SQL Server® – one of the most powerful RDBMS. SQL Server® is released in different
editions that serve different needs - Enterprise, Standard, Web, Developer and Express.
The Express edition is free. It doesn’t support all the functionalities of the other editions, but it includes all that
we need in this book. The another version - Developer edition - is also free. It includes all the functionalities of the
Enterprise edition, but serves only development and testing. It can not be used on a production server.
The Express edition has different components. We need to download and install the DB engine (DBE) and the main
tool that we use to manage the data and the objects in SQL Server® – SSMS (SQL Server® Management Studio).
Before starting the installation, we need to verify that our computer meets the minimum requirements.
Hardware:
• Processor: x64 only, 1.4 GHz
• Memory: 512 MB
• Storage: 6 GB free disk space, NTFS file format
• Internet connection
Software:
• Operating system - Windows 8 or Windows Server 2012
9
Installation
Click:
• SELECT LANGUAGE: English
• WHICH PACKAGE WOULD
YOU LIKE TO DOWNLOAD?:
Express Advanced
• SELECT DOWNLOAD LOCA-
TION: Location of your choice
• Next
10
Installation
11
Installation
12
Installation
13
Installation
14
Installation
15
Installation
16
Installation
17
Installation
18
Installation
Click Install.
19
Installation
20
Installation
21
Working with DB
(DB Professional Roles)
DB Professional Role
These tasks are done by DB professionals and are divided in 3 main roles:
• Database Administrator (DBA):
• Installs and configures the RDBMS
• Schedules backups and restores DBs and automations
• Replicates data between multiple DBs
• Monitors and optimizes the load of the DB server
• Manages the DB security (groups and users)
• Database Architect:
• Designs the conceptual, logical and physical data models
• Creates DB objects, entities and relations
• Database Developer – Creates the Transact-SQL code which:
• Connects the DB with the interface that manipulates the data:
Interface DB
Bank cash machine (ATM) Your bank account
Your browser Your email
• Manipulates data internally in the DB, between different DBs and the exchange of data with another
data sources (API to another DBs, flat files on FTP, etc.).
• Extracts data from different sources, transforms data and loads it into destination tables.
• Serves internal automated processes such as to send emails from the email queue every 2 minutes and
move data to an archive table.
22
DB Types
These systems are grouped in the OLTP (On-line Transaction Processing). They execute a lot of on-line transactions
to generate data.
The following example shows the main elements in the process of data generation in OLTP.
23
DB Types
OLTP
To make business decisions and analyses based on data created in OLTP, you need to cleanse, calculate and aggre-
gate the data... in one word transform it.
• How much did our sales people sell last month?
• Which 5 marketers signed the most new contracts last year?
These are questions that lead to business decisions.
The process that Extract, Transform and Load data from the OLTP systems to the destination system is called ETL
– Extract, Transform, Load. This process is necessary because the OLTP system generates a lot of data that is not
useful for the analyses (corrected lines in orders, internal moves of items, etc.), unclean data (data for non existing
items, garbage from bad transactions, etc.) or data from different sources, that needs to be joined for the analyses.
24
DB Types
The ETL loads data into the Data Warehouse or OLAP (On-line Analytical Processing) system. The data in the OLAP
is organized into the Data Warehouses and Data Marts. It can be sliced and dice in any possible way. We query the
OLAP system to build reports that help the business to make decisions.
OLAP
The dataflow between the DB systems:
25
How the DBE is Structured
and tools:
• SSMS (SQL Server® Management Studio) - Visual tool for writing and executing SQL code and managing DB
objects
• Profiler - Tracing and monitoring
• Tuning Advisor - Helps us to improve the performance of the DB, by suggesting sets of indexes
Database
Schema
Table
View
Function
Stored Procedure
Hierarchy
26
How the DBE is Structured
Schemas
Namespace to store separately logical sets of objects related to the data for Sales, People, Human Resources, etc.
The Schemas facilitate the management of the DB security. The default schema is “dbo” (DB object).
Tables
The tables are composed of:
• Rows (records) – Items
• Columns – Attributes (quality or characteristic belonging to the data) of the items
• Cells – Joins an attribute to an item
• Values in cells – The value of the data stored in a cell
Columns
Column headers Attribute 1 Attribute 2 Attribute 3 Attribute 4
Item 1
Value for Physical element
Rows Item 2 and Item 2 Logical element
Attribute 3
Value
Item 3
Value
Attribute Item
Vechile NumberOfSeats
Airplane 312
Car 5
Item
27
How the DBE is Structured
The value for the attribute Email and Item in row 4 is john.smith@customer1.com.
The value for the attribute LastName, which belongs to item Melanie, is Larson.
28
How the DBE is Structured
We can create a view to select all the columns from the HumanResources table, excluding the Salary column and
make it visible to everyone.
Another view is the column Salary which is visible only to the HR director.
When the business logic needs to change, we edit the logic in the function only. Objects dependent on the function
don’t need to be edited.
An example of not parameterized scalar FN is: Select the last day of the current month. The result that the FN
returns is based on today’s date.
The parameterized scalar FN udf_UnitConvertor accepts the parameters Type, FromMetric, ToMetric and Value
and returns the value in the desired metric.
Input Output
Type FromMetric ToMetric Value Returned Value
Length Inch Centimeter 1 2.54
Volume Liter US Gallon 10 2.64172
29
How the DBE is Structured
udf_CustomersEligibleForDiscount
CustomerID Sales
Functions on page 227
5174 2356.15
7652 1005.14
We can now compare a SP to a warehouse, factory and related business processes together:
1. We store the wood (warehouse) for the bodies of the guitars
2. We create guitars (production)
3. We store and ship (distribution) the guitars
All these steps in the process are dependent on each other and are executed in order.
30
Relational DBs
RDBMS
Data in the DB refers to each other. We can’t create a sales order for a customer that doesn’t exist. Sales and Cus-
tomers data are related. This is why databases like SQL Server® are called RDBMS (Relational database manage-
ment system).
The tables store different attributes of data. Based on the stored data, the tables are logically defined as:
• Structure (Dim or Dimension in OLAP) tables – stores the structure of data like customers, items and man-
ufacturers
• Data (Fact in OLAP) tables – stores the values (Metrics, Measures), related to the structure tables such as
quantity and price of purchased item, made by manufacturer and total value of sales for a customer, etc
The structure and data tables are organized into logical schema. We can define two main schemas:
• Star - Represents a data table in the middle and the structure tables on the rays of the star
• Snowflake - an extension of the Star schema. Represents a data table in the middle and structure table in
the rays and related structure tables on the sub rays
Star Schema
31
Relational DBs
PK
PK PK
FK
FK
FK
FK
FK
PK PK
The Customers table stores the structure of the customers. One row is one customer. The column CustomerID
identifies the row and can store only unique numeric values.
This column is called Primary Key (PK). In table Customers we store all the attributes of the customer like names,
addresses, phone numbers, emails, etc. The same logic applies to all the structure tables.
The data table combines the structure tables with the values (metrics) that the table represents. In the Sales
table, these values are DateOfSale, Quantity and ItemPrice.
The column SaleID is the PK. Columns CustomerID, ItemID and ManufacturerID are linked to the structure tables
(Customers, Items and Manufacturers), they are not unique (they may store duplicates) because one customer can
order the same item by the same manufacturer multiple times. These columns are called Foreign Key (FK).
32
Relational DBs
There can only be one PK column per table and its type is:
• Natural (real data) - created by attribute(s) in the table
We can use the column Email (unique values) as natural PK
• Surrogate (not meaningful data) - consecutive number (columns CustomerID, ManufactiurerID, etc)
The tables that make up the Star schema may look like this:
Manucaturers (structure)
Customers (structure) Manufac- Manufactur- Manufacture-
Custo- Customer- Customer- CustomerAddress turerID erName rAddress
merID FirstName LastName 1 Toys For Boys 164 Blue Str.
1 John Smith 48 Mountain Str. Ltd.
2 Adrian Jameson 9423 Highway Road 2 Little Princess 1534 av. Lake
Fun Inc. Shore
3 Michael Olson 8 av. Riverside
3 Toys and 74 Green For-
1
Nature Co. est Blvd.
33
Relational DBs
Snowflake Schema
An extension of the Star Schema. The structure table refers to other structure tables, called outriggers.
1 1
Customers (structure)
∞ ∞
Custo- Custom- Custom- Custom- Custom-
merID erFirst- erLast- erCoun- erTypeID
Name Name tryID
1 John Smith 1 1
2 Adrian Jameson 2 2
3 Michael Olson 3 3
Data Table
Col 1 Col 2 Col 3
... ... ...
... ... ...
Items (structure)
ItemID Item- ItemDe- ItemCol- ItemSi-
Code scription orID zeID
1 D-123-RS Dress 1 1
2 S-456-GM Shirt 2 2
Structure Table 3 H-789-BL Hat 3 3
Col 1 Col 2 Col 3 ∞
... ... ... ∞
... ... ... 1
34
Relational DBs
Entity
Entity is a logical group of tables that store related data on one subject - Time, Items, Manufacturers, Vendors and
Customers. The entities give us the option to extend the flexibility of the data. We can store multiple variants of
one item in separate table.
If we manufacture or sell clothes, one item is the dress and the multiple variants are size, color, fabric and other.
We can store multiple phones for one customer in CustomersPhones table. The uniqueness (one row represents
one customer) of table Customers is kept, and the requirement to store multiple phones for one customer is met.
The entities above are used in all subject areas (Purchases , Sales , Marketing Campaigns) of the business.
35
Relational DBs
Sales
Time
Days Quarters
Day- Day- DaysIn- WeeksIn-
Day- Quar- QuarterID
Day Of- Of- Quarter Quarter
OfMonth terID
Week Year
1969-01 90 14
1969-03-30 1 30 89 1969-01
1969-02 91 14
1969-03-31 2 31 90 1969-01
1969-03 92 14
1969-04-01 3 1 91 1969-02
1 Entity
1 ∞
Product
Items Colors
Item- Col- Color- Color-
ItemID ItemDescription
Code orID ID Name
1 P123 Plastic Airplane Blue 1 1 Red
2 F125 Rag Doll 33 cm. 2 2 Green
3 1234 Wooden Horse (Age: 1 3 Blue
3-6 years)
1 Entity
1 ∞
Suppliers
Vendors Classes
Ven- Ven- Vendor- Clas-
VndorName ClassName
dorID dorCode ClassID sID
1 VC12 Toys 3 1 Critical
Wholesales
2 Digital
2 VA56 Toys and 1
3 Tactical
Games
1
3 CU05 Infinite Toys 3 Entity
1 ∞
Sales ∞ ∞ ∞
Ven- DateOf- Quan- Item-
SaleID ItemID
dorID Sale tity Price
1 1 2 1969-03-31 536 3.14
2 3 3 1969-03-30 78 22.75
3 2 1 1969-04-01 13 673.24
Entity
36
Relational DBs
The presented structures (star, snowflake and entity) are usually built by a DB architect, so as mentioned, the DB
professional’s roles can overlap and a DBA or developer can also be involved.
Relations
Customers CustomersMarketingDetails
Custo- First- Last- Custo- Market- Market-
Email
merID Name Name merID ingEmails ingCalls
1 John Smith js@customer1.net 1 1 1
2 Anna Laurier lauriera@customer2.org 2 NULL 1
3 Kimberly Nelson kim_nel@customer3.com 3 0 NULL
1 1
• One-to-Many - one row corresponds to many rows in another table. One customer may have multiple
phone numbers
Customers CustomersPhones
Custo- First- Last- Custo-
Email Phone Type
merID Name Name merID
1 John Smith js@customer1.net 1 +1 555 2222 Home
2 Anna Laurier lauriera@customer2.org 1 +1 555 2223 Mobile
3 Kimberly Nelson kim_nel@customer3.com 1 +1 555 2224 Business
• Many-to-Many - many rows corresponds to many rows in another table. This relation is built by two One-to-
Many relations. One customer can rent multiple cars and one car can be rented to multiple customers
Customers Rents Cars
Custo- First- Last- Custo- CarID Make
Email RentID CarID
merID Name Name merID
1 Toyota
1 John Smith js@customer1.net 1 3 2
2 Ford
2 Anna Laurier lauriera@customer2.org 2 2 2
3 Mazda
3 Kimberly Nelson kim_nel@customer3.com 3 3 1
1
1 4 1 1
5 2 3
∞ ∞
37
Relational DBs
To manage these relations, we create a PRIMARY KEY (PK) and a FOREIGN KEY (FK) constraints on the columns
that define the relations. We are flagged to insert a FK when a corresponding PK doesn’t exist.
FOREIGN KEY is a column in the data table that is linked to the PK in the structure table. The values in the FK
column can be duplicated.
Customers Items
Custo- First- Last- Item-
Email ItemID ItemDescription
merID Name Name Code
1 John Smith john.smith@customer1.com 1 P123 Plastic Airplane Blue
2 Anna Laurier anna.laurier@customer2.net 2 F12S Rag Doll 33 cm.
3 Melanie Larson melanie.larson@customer4.biz 3 1234 Wooden Horse (Age:
3-6 years)
1
1
PRIMARY KEY
PRIMARY KEY
Sales ∞ ∞
SaleID CustomerID ItemID DateOfSale Quantity Price
1 2 3 1967-12-14 73 0.2563
2 1 3 1966-05-17 12 5.2341
3 1 2 1968-10-27 52 62.3251
38
Relational DBs
When a physical PK-FK relation is created, we can’t insert FK with no corresponding PK. That means that we
need a customer to exists in the Customers table (PK) to sell to this customer (FK in Sales table).
Relation Keys
One-to-One PK to PK
One-to-Many PK to FK
Many-to-Many PK to FK and FK to PK (junction table)
39
Naming Conventions
The DB objects, the identifiers and the variables need to have names. The goal is to build a easy to understand DB
structure and programming code. To construct the names, we follow rules called Naming Conventions. The rules
are defined by the DB professionals and are not mandatory. We can group the naming conventions as:
Descriptive Names
Case
The different options of combining the upper and lower case define:
• PascalCase – every word starts with upper case MyFirstTable
• camelCase – first word starts with lower case and every next word starts with upper case myFirstTable
• All UPPER, all lower case MYFIRSTTABLE, myfirsttable
Delimited
40
Naming Conventions
Prefix
We can add custom clarification in the prefix to identify the action that the object performs or to which project the
object is assigned:
• usp_Monthly_ETL__ProjectID_1234 is the name of a stored procedure, assigned to ProjectID 1234, that
runs an ETL once per month.
• usp_pid1234__Monthly_ETL and ups_pid1234__ReportByClient
are assigned to the same projectID 1234.
We can use some of the suggestions below or define our custom prefixes.
Suffix
By adding both prefix and suffix, we benefit from all the advantages that they offer.
41
Naming Conventions
When we delimit the name with underscores, we can delimit the name from the prefix and the suffix with two de-
limiters (__) to distinct the delimiter between:
• Prefix and suffix
• Name and the delimiter inside the name
By adding:
• Delimiter in the name underscore (_)
• Delimiter after prefix and before suffix two underscores (__)
we split the elements clearly and make the name much more intuitive Prefix__This_Is_Object_Name__Suffixfx
To facilitate the read and use of the (object, column, variable) name, we start with the common word of a group:
Group Name
Date DateInvoiced, DateOrdered, @DateStart, @DateEnd
Name NameFirst, NameLast, NameDisplayed
Status StatusOrder, StatusRejection, StatusActive etc
42
Naming Conventions
As the object name is an identifier, two objects of the same type (table, view, function, stored procedure) and with
the same name can’t exist on one level. They can exist in different schemas.
Two schemas with the same name can’t exist in one DB, but can exist in different DB.s
Two DBs with the same name can’t exist on one DBE Instance, but can exist in different instances.
To point to the exact DB object, we use the full path to the object.
SDSO
SDSO Syntax FROM Clause Example
Level
Object [ObjectName] FROM [MyTable]
FROM [vw_MyView]
FROM [udf_MyFunction]
Schema [SchemaName].[ObjectName] FROM [dbo].[MyTable]
Database [DatabaseName].[SchemaName].[ObjectName] FROM [MyDatabase].[mySchema].[vw_MyView]
Server [ServerName].[DatabaseName].[SchemaName].[ObjectName] FROM [SQLServer1].[Client173].[dbo].[usp_ETL_
Daily]
Using the DatabaseName.SchemaName.ObjectName structure is making our code more robust and able to be
executed in different databases and schemas on the same server.
43
Naming Conventions
Server 1
Database 1 Database 2
Schema 1 Schema 1
Table 1 Table 1
Table 2 Table 2
Stored Procedure 1
Schema 2
Table 1
Schema 2
Table 1
copy
Table 2
When we use the DSO structure, we can copy [Database 1].[Schema 1].[Stored Procedure 1] that queries
[Database 1].[Schema 2].[Table 2] to [Database 2].[Schema 2].[Stored Procedure 1 Copy] and the
copied stored procedure will continue to query the same object - [Database 1].[Schema 2].[Table 2].
44
SQL and T-SQL (Transact-SQL)
SQL is an abbreviation of Structured Query Language. This is the programming language that communicates with
the DB.
SQL is Structured because it is built by batches that hold statements which hold clauses in an organized structure.
SQL runs Queries to retrieve data from the DB (Select queries). The select queries can be understood as ques-
tions. We ask the DB for specific data and it gives us back an answer (the data, organized in recordset). We may
also ask (query) the DB to add, modify or delete existing data (Modify queries).
SQL is standardized by the American National Standards Institute (ANSI) and the International Organization for
Standardization (ISO). The standards are revised continuously to cover new technologies and improve the function-
ality of the databases. The revisions have names like SQL-92, SQL:2003 or SQL:2008.
As different RDBMSs are created by different companies, the SQL has specific extensions that serve the specific
software. The extended SQL respects the standards defined by ANSI and ISO (ANSI SQL). The extended SQL in Mic-
rosoft® SQL Server® is called T-SQL or Transact-SQL.
45
SQL and T-SQL (Transact-SQL)
Some of the most popular database software and the extended SQL:
46
T-SQL Elements
Code Elements
Batch
SQL code, grouping one or multiple statements (commands) and executing on one run.
In SSMS we use the keyword GO to separate the batches, customizable in Tools Options Query Execution
SQL Server® General:
47
T-SQL Elements
The GO batch separator sends the batch to the DBE for execution.
GO 50 will send the batch 50 times to the DBE (will be executed 50 times).
Statement
The statements in a batch are executed consecutively and synchronously - the execution of a second statement will
start after the execution of the first statement executed has finished.
Every statement starts with a keyword (CREATE, ALTER, DROP, SELECT, INSERT, UPDATE, DELETE, etc.) and terminates
with a semicolon (;).
Clause
Component that constructs the statement, structured in exact order. There can be one or multiple in a statement
and define the type of the statement (DDL or DML).
Keyword
Words, existing in the English language, used in T-SQL to construct the clauses and statements. They are defined
by ANSI and ISO and modified in T-SQL to extend the functionality of the SQL Server®. Keywords are reserved in
T-SQL, so it is not good practice to use them as identifiers. When we use a keyword as an identifier, we surround it
in square brackets ([]).
Keywords are not case sensitive. CREATE TABLE and create table will be executed the same way, but we
write the keywords in upper case to make them stand out to distinguish them from the identifiers (names of
DB objects, column names in table etc.) and the values.
48
T-SQL Elements
Identifier
SELECT
FirstName Identifier (column name)
, LastName
FROM Customers Identifier (table name)
WHERE FirstName = 'Beverly';
UPDATE Customers
SET Email = 'b@BeverlyWebsite.com'
WHERE
FirstName = 'Beverly'
AND LastName IS NULL;
GO
They are:
• Required (created by the DB architect) or
• Optional (automatically generated by SQL Server®)
Identifiers are collected in the Object Catalog and can be queried to perform a task such as to bulk update some
value in all string columns in multiple tables.
The case sensitivity of the identifiers is defined by the SQL Server® Collation.
Collation on page 66
49
T-SQL Elements
Data Type
When we create or alter DB object, we specify the data type that table column, variable, function parameter or
stored procedure parameter can store. One table column can store strings, whilst another can store numbers, dates
and times or booleans.
ALTER TABLE Customers
CREATE TABLE Customers ADD Phone VARCHAR(16);
( GO
FirstName NVARCHAR(32)
, LastName NVARCHAR(64) Data Type
, Email VARCHAR(128)
);
DECLARE @MyVariable INT;
GO
GO
CREATE and ALTER DDL statements specify the data type that table column can store or the data type of SP or FN
parameters. When we create (DECLARE) a variable, we also specify the data type that the variable can
store.
SELECT ('MyString' + 3) AS Expression; The data type of the elements has to be the same in order the
GO expression to be evaluated correctly.
String Integer
Conversion failed when converting the varchar value
'MyString' to data type int.
50
T-SQL Elements
Value
ItemID = 2
AND DateOfSale IN ('1967-04-15', '1967-05-17')
AND NOT IsCorrection = 0;
GO
Alias
51
T-SQL Elements
Square Bracket
We surround the identifier that holds a special characters (not letter or number) in square brackets. Else they are
not valid in T-SQL and we can’t execute the batch.
SELECT [First!Name] AS [First Name]
FROM [Approved?Customers];
GO Special Character
Literal value
Variable
The variable is place in the memory of the computer where we store value that can vary.
Variable
DECLARE Create variable and
@ItemID NVARCHAR(64) = 2 assign a value to it
, @DateStart DATE = '1967-04-15'
, @DateEnd DATE = '1967-05-17';
SELECT
CustomerID The value of @DateEnd is '1967-05-17'
, ItemID
, (TotalSales * 1.3) AS ExpandedSales
FROM Sales
Use the variable
WHERE
ItemID = @ItemID
AND DateOfSale IN (@DateStart, @DateEnd)
AND NOT IsCorrection = 0;
52
T-SQL Elements
Delimiter
SELECT
FirstName
, LastName
, Email
FROM Customers
WHERE FirstName = 'Beverly';
Identifier delimiter
UPDATE Customers
SET Email = 'b@BeverlyWebsite.com'
WHERE
FirstName = 'Beverly'
AND LastName IS NULL; Statement delimiter
GO Batch delimiter
Syntax
Every programming language is following rules, called syntax. The above rules define the SQL syntax.
53
DDL and DML Statements
The statements that create, alter or delete DB objects are grouped as Data Definition Language (DDL). The state-
ments that query or manipulate the data are called Data Manipulation language (DML).
DDL
We can recognize the DDL Statements by their first clause - CREATE, ALTER, TRUNCATE, DROP.
DML
The DML statements manipulate the data in the DB and their first clause are INSERT, SELECT, UPDATE, DELETE
54
CRUD
CRUD is an abbreviation of the four basic actions that we perform in the DB - Create, Read, Update and Delete.
We apply CRUD actions on DDL and DML as follows:
DDL DML
Abbreviation Clause Action Clause Action
Create CREATE, TRUNCATE Create object INSERT Create row in a table
Update ALTER Change the definition of an object UPDATE Update data in a table
Delete DROP, TRUNCATE Delete object DELETE Delete row from a table
The DDL TRUNCATE statement is logically a DML statement, because it deletes all the data in a table, but not the ta-
ble itself. It is in the DDL group, because it changes the object (resets the identity column). It is a variation of DROP
and CREATE statements together.
55
Data Types and Conversions
Table columns
Customers
CustomerID FirstName LastName Email YTDSales DateRegistered TimeRegistered
1 John Smith jsmith@js1.org 523.43 1967-03-25 17:36:55.2030000
2 Anna Larson larsona@al2.com 8542.12 1968-12-14 03:21:28.7470000
3 Michelle Boyer mb@customer3.net 85.59 1965-07-18 13:47:00.8130000
Variable
Expression
56
Data Types and Conversions
Strings Other
Character Non-Unicode UNIQUEIDENTIFIER
CHAR TIMESTAMP - Obsolete. Replaced with ROWVERSION.
VARCHAR XML
TEXT - Obsolete. Replaced with VARCHAR(MAX) SQL_VARIANT
Character Unicode TABLE
NCHAR HIYERERARCHYID
NVARCHAR CURSOR
NTEXT - Obsolete. Replaced with NVARCHAR(- Spacial
MAX) GEOGRAPHY
GEOMETRY
Binary
Numerics - Exact
Fraction
Precision:
Name Storage Size Range (From) Range (To)
(bytes)
DECIMAL(P, S) 1 to 9: 5 -1038 + 1 1038 - 1
DEC 10 to 19: 9
NUMERIC(P, S) 20 to 28: 13
29 to 38: 17
57
Data Types and Conversions
Thousands
Thousands
Thousands
Thousands
Thousands
Hundreds
Hundreds
Hundred
Hundred
Millions
Millions
Millions
Ones
Tens
Tens
Tens
Tens
Tens
1,000,000
100,000
10,000
1,000
100
10
10
100
1,000
10,000
100,000
1,000,000
10,000,000
7 6 5 4 3 2 1 . 1 2 3 4 5 6 7
Decimal
Position
Whole Number Decimal Fraction
Point
Monetary, Currency
Storage Size
Name Range (From) Range (To)
(bytes)
MONEY 8 -922,337,203,685,477.5808 922,337,203,685,477.5807
SMALLMONEY 4 -214,748.3648 214,748.3647
Boolean
BIT columns per table:
Name Range (From) Range (To)
Storage Size (bytes)
BIT 1 to 8: 1 0 1
9 to 16: 2 False True
17 to 24: 3 No Yes
25 to 32: 4
...
Numerics - Approximate
58
Data Types and Conversions
Strings
Character Non-Unicode
Number of Number of
Name Storage Size Characters Characters
(From) (To)
CHAR(N) 1 byte per N 1 8,000
VARCHAR(N) N is 1 to 8000: 1 byte per N + 2 bytes 1 8,000
N is MAX: up to 231 - 1 bytes (2,147,483,647 bytes or 2 GB)
Character Unicode
Number of Number of
Name Storage Size Characters Characters
(From) (To)
NCHAR(N) 2 bytes per N 1 4,000
NVARCHAR(N) N is 1 to 4000: 2 byte per N + 2 bytes 1 4,000
N is MAX: up to 231 - 1 bytes (2,147,483,647 bytes or 2 GB)
Binary
Number of Number of
Name Storage Size (bytes) Characters Characters
(From) (To)
BINARY(N) 1 byte per N 1 8,000
VARBINARY(N) N is 1 to 8000: 1 byte per N + 2 bytes 1 8,000
N is MAX: up to 231 - 1 bytes (2,147,483,647 bytes or 2 GB)
N - number of characters
59
Data Types and Conversions
N: Storage
Preci-
Name Rande (From) Rande (To) Size Format Note
sion
(bytes)
DATE 1 0001-01-01 9999-12-31 YYYY-MM-DD
day
TIME(N) 100 00:00:00.0000000 23:59:59.9999999 0 to 2: 3 HH:MI:SS.fffffff
ns 3 to 4: 4
5 to 7: 5
Not specified: 5
DATETIME 3.33 1753-01-01 9999-12-31 8 YYYY-MM-DD The last decimal frac-
ms 00:00:00.000 23:59:59.997 HH:MI:SS.fff tion digit is rounded
to 0, 3, 7
DATETIME2(N) 100 0001-01-01 9999-12-31 0 to 2: 6 YYYY-MM-DD
ns 00:00:00.0000000 23:59:59.9999999 3 to 4: 7 HH:MI:SS.fffffff
5 to 7: 8
Not specified: 8
SMALLDATETIME 1 1900-01-01 2079-06-06 4 YYYY-MM-DD SS is always 00
min 00:00:00 23:59:59:00 HH:MI:SS 29 seconds - rounded
down
30 seconds - ronded
up
DATETIMEOFFSET 100 0001-01-01 9999-12-31 0 to 2: 8 YYYY-MM-DD Time zone offset
ns 00:00:00.0000000 23:59:59.9999999 3 to 4: 9 HH:MI:SS.fffffff Range: -14:00 -
5 to 7: 10 (- or +) HH:MI +14:00
Not specified: 10
60
Data Types and Conversions
Precision examples
TIME TIME(0) TIME(1) TIME(2) TIME(3) TIME(4) TIME(5)
14:10:36.7718695 14:10:37 14:10:36.8 14:10:36.77 14:10:36.772 14:10:36.7719 14:10:36.77187
TIME(6) TIME(7)
14:10:36.771870 14:10:36.7718695
DATETIME2(6) DATETIME2(7)
1967-05-12 1967-05-12
16:19:53.473959 16:19:53.4739587
When we build expression or compare values, we need to have the same data type for the elements of the expres-
sion and the compared values.
Implicit conversion is the conversion that the SQL Server® is doing automatically in the background without no-
tifying that it is being made. It converts compatible (String and convertible to String, Numeric and convertible to
Numeric and so on) data types.
61
Data Types and Conversions
Expression
Implicit conversion
SELECT '3' + 3 AS Expression; Expression
GO Number 6
String
Explicit conversion
SELECT 'My String ' + CONVERT(CHAR(1), 3) AS Expression; Expression
GO My String 3
String Numeric, converted to String
Comparison
Implicit conversion
SELECT *
Date
FROM Sales
WHERE DateOfSale = '1968-09-15';
GO String
Explicit conversion
SELECT * Numeric, converted to String
FROM Sales
WHERE (MonthLiteral + ' ' + CAST(YearFiscal AS VARCHAR(9))) = 'September 1968';
GO
String
String
62
Data Types and Conversions
The VALUES clause creates a VR. This VR respects the Data Type Precedence (on page 64)
Clarification:
INSERT DatabaseName.SchemaName.TableName
(
ColumnString
, ColumnNumeric
)
VALUES
(1, 2) Numeric, Numeric
, ('3', 4) String, Numeric
, ('D', '5'); String, String
GO
In the first row 1 is Numeric. The data type of the first column will be defined as INT.
In the second row, the string '3' is implicitly converted to INT.
As the data type of the first column is defined as INT, the string 'D' can’t be implicitly converted to INT.
The same is valid for any VR.
INSERT DatabaseName.SchemaName.TableName
(
ColumnString
, ColumnNumeric
)
SELECT 1, 2 Numeric, Numeric
UNION ALL SELECT '3', 4 String, Numeric
UNION ALL SELECT 'D', '5'; String, String
GO
Date
SELECT CAST('08/31/1965 14:11:56.3506429' AS DATE) AS [Date];
1965-08-31
GO
63
Data Types and Conversions
Time
SELECT CAST('08/31/1965 14:11:56.3506429' AS TIME) AS [Time];
14:11:56.3506429
GO
When the DB converts implicitly, it converts data with a lower precedence data type to one with higher precedence.
64
Data Types and Conversions
SMALLINT NVARCHAR
TINYINT NCHAR
BIT
Character
Other VARCHAR
TIMESTAMP CHAR
UNIQUEIDENTIFIER
Binary
String VARBINARY
Unicode BINARY
65
Collations
Collation
We define the collation in DDL (create or alter object) and in DML statements (select, join or filter the data).
Server Level
Check all the collations on the server name
SELECT *
Albanian_BIN Albanian, binary sort
FROM sys.fn_helpcollations();
Albanian_BIN2 Albanian, binary code
GO point comparison sort
... ...
SELECT SERVERPROPERTY('Collation') AS ServerCollation;
GO
Check the current collation on the server
ServerCollation
DB level SQL_Latin1_General_CP1_CI_AS
66
Collations
Column level
USE Collations;
GO
[name] AS ColumnName
, [collation_name] AS ColumnCollation ColumnName ColumnCollation
FROM Collations.sys.columns FirstName Japanese_CS_AI_WS
WHERE Object name Email Korean_Wansung_CS_AI_WS
OBJECT_NAME([object_id]) = 'CollationsTable'
--AND [name] = N'Email'; Uncomment to filter exact column
GO
The keyword COLLATE, added to column identifier, switches the collation of the column:
SELECT
T1.Column1
, T2.Column2 T2.Column1 is not Latin1_General_CI_AI
FROM and we need to equalize the collations
Table1 AS T1 in order to join the objects
JOIN Table2 AS T2
ON T1.Column1 = T2.Column1 COLLATE Latin1_General_CI_AI
GO
T1.Column1 is Latin1_General_CI_AI
67
Collations
SELECT
T1.Column1
, T1.Column2
FROM
Table1 AS T1
JOIN Table2 AS T2 Equalize the collations to apply to filter condition
ON T1.Column1 = T2.Column1
WHERE T1.Column1 = T2.Column1 COLLATE Latin1_General_CI_AI;
GO
! When we UNION recordsets, the collation of the matching columns have to be the same
The flags in the collation name explain the properties of the collation:
Case Sensitivity
• CI - Case Insensitive Code Page CP(X) (X is between1 and 4)
• CS - Case Sensitive • CP1 - Code page 1252 (Latin1 (ANSI))
• CP1251 - Code Page 1251 (Cyrillic)
Accent Sensitivity • CP949 - Code page 949 (Korean)
• AI - Accent Insensitive
• AS - Accent Sensitive Binary
• BIN - older BIN collations
Kana (Japanese kana characters) • BIN2 - newer BIN2 collations
• KI - Kana Insensitive
• KS - Kana Sensitive Unicode
• CS - Supplementary Characters
Width
• WI - Width Insensitive
• WS - Width Sensitive
68
NULL and 3VL (Three-valued Logic)
Based on the standard ISO 9075-1: 2011, NULL is a “Special value that is used to indicate the absence of any data
value”.
When we add new nullable column to an existing table, it stores NULL, before we insert values.
When we create (DECLARE) a new variable, its value is NULL, i.e. a value is missing.
NULL is a marker that shows an absence of data.
NULL is not an empty string, space, multiple spaces or 0 (zero), all of which are actually values. It is an absence of
value - unknown or nothing.
The 3VL (Three-value Logic) explains that the NULL marker or placeholder can’t result in True or False.
The result of comparison with NULL is unknown.
This is why we compare NULL in a table column or variable with the special keyword IS (IS NULL, IS NOT NULL) and
manipulate NULL separately from the other values.
The question should we use or not use NULL in the DB architecture and development is not explicit. Both options
have their pros and cons.
SELECT *
FROM dbo.Customers
WHERE LastName NOT LIKE 'Smi%';
GO
The result above is correct, because when we compare column LastName to Smi*, the DBE asks the question “Col-
umn LastName on this row begins with Smi?” and returns the rows, corresponding to answer Yes (True):
To select the rows that we need, we need to select the rows where “LastName doesn’t start with Smi or LastName
is unknown?”:
69
NULL and 3VL (Three-valued Logic)
SELECT *
FROM dbo.Customers
WHERE
FirstName LastName
(
Anna NULL
LastName NOT LIKE 'Smi%'
Melanie Johnson
OR LastName IS NULL
);
GO
We use built-in functions to replace NULL with a real value and vice versa:
• ISNULL(ValueToCheck, SpecifiedValue) - When ValueToCheck IS NULL SpecifiedValue is returned
• NULLIF(ValueToCheck, SpecifiedValue) - When ValueToCheck is equal to SpecifiedValue, NULL is
returned
The built-in function COALESCE returns the first not NULL value from the specified values:
• COALESCE(ValueToCheck1, ValueToCheck2, ValueToCheck3, 'N/A') - returns the first not NULL
value and N/A if all the precedent values are NULL. If all the specified values are NULL, NULL is returned.
70
Operators
Operators
Arithmetic
8 - operand
Comparison (Relational) 19 - operand
Compares the values of the operands and returns true, false or unknown (boolean operator). The boolean opera-
tors test the truth or falsity of the condition.
When the values of the operands can’t be implicitly converted, we convert them with the built-in functions CAST(),
TRY_CAST(), CONVERT(), TRY_CONVERT(), PARSE(), TRY_PARSE(), STR().
Operator True If Compares if the left operand Result
= 19 = 8 equals False
Data Types and Con-
<> 19 <> 8 doesn’t equal True
!= 19 != 8 versions on page 56
> 19 > 8 is greater than True
< 19 < 8 is less than False
>= 19 >= 8 is greater than or equal to True
! reverses the meaning of the operator.
!< 19 !< 8 isn’t less than Not ISO standard.
<= 19 <= 8 is less than or equal to False
!> 19 !> 8 isn’t greater than
the right operand
71
Operators
Assignment
Current New
Operator Name Description Example
Value Value
= Assignment Overwrites the current value. UPDATE @Table1 123456 8
SET Column1 = 8;
SELECT @Variable1 = 8;
+= Addition Adds 8 to the current value and assigns the result UPDATE @Table1 19 27
SET Column1 += 8;
SELECT @Variable1 += 8;
-= Subtraction Substracts 8 from the current value and assigns UPDATE @Table1 19 11
the result SET Column1 -= 8;
SELECT @Variable1 -= 8;
*= Multiplication Multiplies the current value by 8 and assigns the UPDATE @Table1 19 152
result SET Column1 *= 8;
SELECT @Variable1 *= 8;
/= Division Divides the current value by 8 and assigns the UPDATE @Table1 19 2.375
result SET Column1 /= 8;
SELECT @Variable1 /= 8;
%= Modulo Divides the current value by 8 and assigns the UPDATE @Table1 19 3
remainder SET Column1 %= 8;
SELECT @Variable1 %= 8;
Logical
NOT
Operator Example Result (reverses the meaning
of the operator)
AND (Age >= 18) AND (DateRegistered = '1968-05-18') True if both operands AND NOT
(expressions) return True
OR (Age >= 18) OR (DateRegistered = '1968-05-18') True if one of the operands OR NOT
(expressions) return True
EXISTS EXISTS (Subuery) True if the subquery returns at NOT EXISTS
least one row
BETWEEN Age BETWEEN 18 AND 32 True if Age is in the range NOT BETWEEN
from 18 to 32 (including)
IN Age IN (18, 22) True if Age matches any value NOT IN
in the list 18, 22
IS NULL Age IS NULL True if Age is NULL IS NOT NULL
LIKE FirstName LIKE 'Smi%' True if FirstName starts with NOT LIKE
'Smi'
72
Operators
Operator
Left Operand Right Operand AND OR
(8 = 8) (12 = 12) True True True AND True = True True OR True = True
(8 = 8) (12 = 13) False True True AND False = False True OR False = True
(8 = 9) (12 = 13) False False False AND False = False False OR False = False
Concatenation
Operator Precedence
Direction of
Order Operation T-SQL Result Explanation
the execution
1 Parentheses Left to right SELECT 7 + 6 * 5; 37 7 + 30
SELECT (7 + 6) * 5; 65 13 * 5
23
2 Exponents Right to left SELECT POWER(POWER(2, 3), 2); 64 2 = 28
(power and SELECT 7 + 6 * POWER(2, 3); 55 7 + 6 * 8 = 7 + 48
root)
SELECT - 4 + POWER(2, 3); 4 -4+8=8-4
3 Multiplication Left to right SELECT 7. / 6. * 5.; 5.833330 1.166666 * 5
and division SELECT 5. * 6. / 7.; 4.285714 30 / 7
4 Addition and Left to right SELECT 7 + 6 - 5; 8 13 - 5
subtraction SELECT 5 - 6 + 7; 6 -1+7=7-1
73
Operators
Examples:
Arithmetical Operators
DB defined precedence: Parentheses to force the precedence:
2 + 4 * 6 = 26 (2 + 4) * 6 = 36
4 * 6 = 24 + 2 = 26 2 + 4 = 6 * 6 = 36
Not 2 + 4 = 6 * 6 = 36
Logical Operators
DB defined precedence:
SELECT *
FROM dbo.Customers
WHERE
Age >= 18
OR IsEligible = 1
AND DateRegistered = '1969-12-17';
GO
74
SQL Server® Management Studio
(SSMS)
Until now we have defined the theory and the basics. The fun starts here :-) From now on the main tool to learn
T-SQL is SSMS (SQL Server® Management Studio).
Log in SSMS
To start SQL Server® Management Studio, click the Start menu and Type SQL Server® 2016 Management Studio
in the search box, then click on SQL Server® 2016 Management Studio in the top section.
When login in with Windows Authentication, the text fields Login, Password and the checkbox Remember pass-
word are not active.
75
SQL Server Management Studio
(SSMS)
We can use the built-in function SUSER_SNAME() to verify our login name:
Session ID
Returned Rows
Execution Time
On the left-hand side is the Object Explorer (DBs, Tables, Views, Functions, Stored Procedures,etc.).
The right part of the window is the area where we write our T-SQL code.
The bottom-right area is where we examine the result of the batch and the error messages (if any).
76
SQL Server Management Studio
(SSMS)
Tables, views and stored procedures are in the database’s Programmability section:
By selecting a portion of the code and click Execute (F5), we execute only the selected portion of the code:
By selecting a keyword and pressing Shift + F1, we search Books Online for this keyword.
77
Constraints
As the name suggests, constraints constrain data. These are rules that belong to the table columns that limit the
values that we can stored in the columns.
• A column with a PK constraint can store only unique values and can’t store NULL
• PK uniquely identifies the rows in the table
• Only one PK can exist in a table
Customers
CustomerID FirstName LastName Email
1 Anabel Larson anabel.larson@customer5.info
2 Anna Laurier anna.laurier@customer2.net
3 Beverly NULL NULL
4 John Smith john.smith@customer1.com
5 John Smith john.smith@customer3.org
PK
NOT NULL Insert NULL fail
UNIQUE Insert 3 fail (duplicate)
Composite (compound) PK
• The PK constraint is defined in multiple columns
• Neither of the columns can store NULL
• The combination of the column values define the unique value for the PK
Customers
FirstName LastName Email
Anabel Larson anabel.larson@customer5.info
Anna Laurier anna.laurier@customer2.net
John Smith john.smith@customer1.com
Melanie Larson melanie.larson@customer4.biz
Zak Smith NULL
PK
NOT NULL NOT NULL
UNIQUE
Insert NULL fail Insert NULL fail
Insert 'John' Insert 'Henderson' OK
Insert 'John' Insert 'Smith' fail (duplicate)
78
Constraints
SELECT
OBJECT_NAME([parent_object_id]) AS TableName
, [name] AS PKName
FROM sys.key_constraints
WHERE [type] = 'PK';
GO
• Constraints the column to store only values that exist in the referencing PK column in another table
• Ensures the referential integrity of the data
Customers
CustomerID FirstName LastName Email
1 Anabel Larson anabel.larson@customer5.info
2 Anna Laurier anna.laurier@customer2.net
3 Beverly NULL NULL
4 John Smith john.smith@customer1.com
5 John Smith john.smith@customer3.org
PK
Reference
Sales
SaleID CustomerID ItemID Quantity Prce DateOrdered
1 3 3 3 1.24 1967-10-17
2 3 5 5 2.43 1968-05-08
3 4 2 2 12.44 1965-09-04
4 3 3 7 5.88 1968-12-17
5 5 1 1 28.19 1968-03-04
PK FK
Only values,
existing in Insert 6 - fail (doesn’t exist)
the PK refer- Insert 3 - OK (exist)
ence column Insert NULL - OK
FK Referential Actions
79
Constraints
When we update or delete a value in a PK column, the FK can be automatically actioned with the following rules:
• Delete - ON DELETE
• Update - ON UPDATE
Rules
Referential Action Update PK Delete PK
NO ACTION Not Allowed Not Allowed
CASCADE Updates FK with PK Deletes the FK
SET NULL Updates FK to NULL Updates FK to NULL
SET DEFAULT Updates FK with the DEFAULT value Updates FK with the DEFAULT value
CASCADE (UPDATE)
Customers
CustomerID FirstName LastName Email
1 Anabel Larson anabel.larson@customer5.info
2 Anna Laurier anna.laurier@customer2.net
3 Beverly NULL NULL Update CustomerID to 6
4 John Smith john.smith@customer1.com
5 John Smith john.smith@customer3.org
PK
Reference
Sales
SaleID CustomerID ItemID Quantity Prce DateOrdered
1 3 1004 3 1.24 1967-10-17
2 1 5273 5 2.43 1968-05-08
Updates FK (CustomerID) to 6
3 4 5432 2 12.44 1965-09-04
4 3 8541 7 5.88 1968-12-17
5 5 9514 1 28.19 1968-03-04
PK FK
80
Constraints
Reference
Sales
SaleID CustomerID ItemID Quantity Prce DateOrdered
1 3 1004 3 1.24 1967-10-17
2 1 5273 5 2.43 1968-05-08 Deletes row where FK (Custo-
3 4 5432 2 12.44 1965-09-04 merID) = 3
4 3 8541 7 5.88 1968-12-17
5 5 9514 1 28.19 1968-03-04
PK FK
Reference
Sales
SaleID CustomerID ItemID Quantity Prce DateOrdered
1 3 1004 3 1.24 1967-10-17
2 1 5273 5 2.43 1968-05-08 Updates FK (CustomerID)
3 4 5432 2 12.44 1965-09-04 to NULL
4 3 8541 7 5.88 1968-12-17
5 5 9514 1 28.19 1968-03-04
PK FK
81
Constraints
Reference
Sales
SaleID CustomerID ItemID Quantity Prce DateOrdered
1 3 1004 3 1.24 1967-10-17
2 1 5273 5 2.43 1968-05-08 Updates FK (CustomerID) to the
3 4 5432 2 12.44 1965-09-04 DEFAULT value of the column
4 3 8541 7 5.88 1968-12-17
5 5 9514 1 28.19 1968-03-04
PK FK
DEFAULT (DF)
When a row is inserted and no value is specified for a column with a DF value, the default value is inserted. DF is a
replacement for NULL.
82
Constraints
Customers
Custome- First- Last-
Email DateRegistered
rID Name Name
1 Anabel Larson anabel.larson@customer5.info 1967-12-04
2 Anna Laurier anna.laurier@customer2.net 1966-05-17
3 Beverly NULL NULL 1964-08-11
4 John Smith john.smith@customer1.com 1966-11-07
5 John Smith john.smith@customer3.org 1965-08-11
DF
DateRegistered = DF Definition
today
Insert new line without
Adam Hasen ah@customer17.com
specifying DateRegistered
6 Adam Hasen ah@customer17.com 1969-12-08
New line with default Da-
Today is 1969-12-08
teRegistered (today)
CHECK (CK)
Customers
Custome- First- Last-
Email
rID Name Name
1 Anabel Larson anabel.larson@customer5.info
2 Anna Laurier anna.laurier@customer2.net
3 Beverly NULL NULL
4 John Smith john.smith@customer1.com
5 John Smith john.smith@customer3.org
CK
Column Email contains @ CK Definition
sign
6 Adam Hasen ah@customer17.com Insert valid Email - OK
7 Adam Hasen ah#customer17.com Insert not valid Email - fail
4 John Smith john.smith&customer1.com Update CustomerID = 4 with not
valid Email - fail
83
Constraints
UNIQUE (UQ)
UQ
6 Adam Hasen ah@customer17.com Insert not existing value in Email - OK
7 Adam Hasen ah@customer17.com Insert duplicate in Email - fail
4 John Smith ah@customer17.com Update CustomerID = 4 with duplicate
Email - fail
84
Constraints
Constrains a column not to store NULL. We can’t insert or update a row, without specifying a value for the NN col-
umn.
Customers
Custome- First- Last-
Email
rID Name Name
1 Anabel Larson anabel.larson@customer5.info
2 Anna Laurier anna.laurier@customer2.net
3 Beverly NULL bev@customer7.pl
4 John Smith john.smith@customer1.com
5 John Smith john.smith@customer3.org
NOT NULL
6 Adam Hasen ah@customer17.com Insert Email - OK
7 Adam Hasen Insert row and skip Email - fail
4 John Smith NULL Update CustomerID = 4 with Email = NULL - fail
Data Type
The data type that a column in a table can store is also a constraint. We can’t insert String into a column with a
Numeric data type.
Constraints are created, altered and deleted with DDL statements (on page 86)
85
DDL Statements
CREATE
Creates new DB objects. It is good practice to use the DSO naming convention to point to the database and the
schema, where we need to create the object.
The keyword CREATE is the first clause in a DDL statement, followed by the type of the object (DATABASE, SCHEMA,
TABLE, VIEW, etc.) and the name of the object (identifier).
CREATE DATABASE
Creates a new database by using the default settings for DB location and DB size. We can see where the default DB
location is:
In SSMS Object Explorer (F8) (right click on the server name) Properties Database Settings
Database default locations
86
DDL Statements
CREATE
SELECT *
FROM sys.databases
WHERE [name] = 'LearnSQLServerIntuitively'; Comment this line to select all the DBs
GO
87
DDL Statements
CREATE
The DB files:
SELECT *
FROM sys.database_files;
GO
To point to a specific database, we use the keyword USE in the beginning of the batch:
USE LearnSQLServerIntuitively;
GO
After we change the DB with USE, all the following statements in the batch are be executed against DB LearnSQLS-
erverIntuitively.
CREATE SCHEMA
After the DB exists, we create the next level DB object - the schema. To create a schema:
SELECT *
FROM sys.schemas;
GO
CREATE TABLE
After the keyword CREATE and the identifier for the name of the table, we list (in brackets) the column names and
their data types:
88
DDL Statements
CREATE
We can create a table and the constraints belonging to the table in one DDL CREATE statement:
To create the PK and let the DBE generate the name for the PK:
To specify a name for the PK right after column name and data type:
89
DDL Statements
CREATE
);
GO
We can add a FK to column and let the DBE generate the FK name:
90
DDL Statements
CREATE
, ItemID INT
, Quantity INT
, Price MONEY
, DateOrdered DATETIME
, CONSTRAINT PK__Sales PK with specified name on column SaleID
PRIMARY KEY (SaleID)
);
GO
or
To add the referential actions to the FK, we list them after the FK definition:
91
DDL Statements
CREATE
To create a DEFAULT constraint, we add the keyword DEFAULT and the constraint definition after the column
name and data type:
To specify a name for the constraint, we add the keyword CONSTRAINT, followed by the constraint name and
definition:
To create the CK with an automatically generated name, we add the keyword CHECK next to the column name and
data type, followed by the constraint definition:
92
DDL Statements
CREATE
FirstName NVARCHAR(32)
, LastName NVARCHAR(64)
, [Email Address] VARCHAR(128)
CHECK (CHARINDEX('@', [Email Address]) > 0) CK with automatically generated name
, [Date Registered] DATETIME
);
GO
To specify name, we use the keyword CONSTRAINT after the column name and data type:
To create UNIQUE constraint with an automatically generated name, we add the keyword UNIQUE after the column
name and data type:
To specify the constraint name, we use the keyword CONSTRAINT after the column name and data type:
93
DDL Statements
CREATE
To create NN constraint for a column, add NOT NULL after the column name and data type:
CREATE VIEW
The identifier after CREATE VIEW doesn’t allow us to specify a DatabaseName. We use SO ([Schema].[Object]) nam-
ing convention as the identifier.
After the keyword AS, we write the SELECT statement that creates the recordset, returned by the view.
To create a view:
94
DDL Statements
CREATE
USE LearnSQLServerIntuitively;
GO
CREATE PROC creates a new Stored Procedure. After the SP name, we specify the parameter(s) list and the SP
definition:
95
DDL Statements
CREATE
CREATE FUNCTION
After the CREATE FUNCTION, we specify the function name, the parameter(s) list and the function definition.
Scalar-valued:
Table-valued (Inline):
96
DDL Statements
CREATE
Table-valued (Multi-Statement):
97
DDL Statements
CREATE
FROM dbo.Sales
GROUP BY CustomerID
HAVING SUM(Quantity * Price) >= @SaleValue; Statement No. 3
INSERT @SalesOverValue (FirstName, LastName, SaleDate, Quantity, Price, LineTotal)
SELECT
C.FirstName Populate the return
, C.LastName table variable
, S.DateOfSale
, S.Quantity
, S.Price
, (S.Quantity * S.Price) AS LineTotal
FROM
dbo.Customers AS C
JOIN dbo.Sales AS S
ON C.CustomerID = S.CustomerID
WHERE C.CustomerID IN (SELECT CustomerID FROM @FilterCustomerID);
RETURN; Statement No. 4
END
GO Function definition (multiple statements to populate the return table)
The code (View, Function, Stored Procedure) deployed in production is repeatable - it is executed multiple
times.
To avoid the error Cannot drop the {ObjectType} '{ObjectName}', because it does not exist or you do not have
permission., we add a cleanup statement (delete if exists) before the CRAETE statement.
Before creating an object, we delete an existing object with the same name (if any).
98
DDL Statements
ALTER
ALTER Statement
ALTER DATABASE
ALTER SCHEMA
ALTER TABLE
ALTER TABLE modifies a table design. The keyword that follows is:
• ADD - adds one or multiple new columns to an existing table Columns
• ALTER COLUMN - changes the column definition of an existing column
• DROP COLUMN - deletes one or multiple existing columns
ADD
99
DDL Statements
ALTER
ALTER COLUMN
Sales Customers
Price Price
5.3542 ALTER TABLE dbo.Sales 5.354200
52.6234 ALTER COLUMN Price DECIMAL(18, 6) NOT NULL; 52.623400
203.2416 GO 203.241600
MONEY DECIMAL(18, 6)
NULL NOT NULL
DROP COLUMN
ADD CONSTRAINT
Adds constraint on a column for values that will be inserted in the future. Doesn’t affect current values that don’t
violate the constraint. If the column already contains values, these values are then validated against the constraint
to be created and in case of violation, the constraint can’t be created.
100
DDL Statements
ALTER
Customers
First- Last- DateReg-
Email
Name Name istered
ALTER TABLE dbo.Customers
Anabel Larson anabel.larson@ 1967-12-04
customer5.info
ADD CONSTRAINT DF__DateRegistered__GetDate
Anna Laurier anna.laurier@ 1966-05-17
DEFAULT (GETDATE())
customer2.net FOR DateRegistered;
Beverly NULL NULL NULL GO
Customers
Insert new row, by not First- Last-
Email
DateReg-
Name Name istered
specifying value for
Anabel Larson anabel.larson@customer5.info 1967-12-04
DateRegistered
Anna Laurier anna.laurier@customer2.net 1966-05-17
No DF
Beverly NULL NULL NULL
John Smith john.smith@customer1.com 1969-11-07
Barnie Jason bj@customer6.br NULL
101
DDL Statements
ALTER
ALTER VIEW
Changes the definition (the code) of an existing view. As the ALTER VIEW statement overwrites the definition of
the view, we need first to create a backup of the view.
When we change the design of an underlying object, we need to refresh the view:
EXEC sp_refreshview '[SchemaName].[ViewName]';
Edits the definition (and the parameters) of an existing Stored Procedure. First we back up the Stored Procedure.
ALTER FUNCTION
Edits the definition (and the parameters) of an existing Function. We back up the function before altering it.
Scalar-valued
102
DDL Statements
ALTER
RETURNS DATETIME
AS
BEGIN
DECLARE @ReturnVariable DATETIME; Edit the definition
SELECT @ReturnVariable = ...Statement that populates @ReturnVariable...;
RETURN @ReturnVariable;
END
Table-valued Inline
Table-valued Multi-Statement
RETURN (0);
END
GO
103
DDL Statements
DROP
DROP Statement
DROP DATABASE
USE [master];
GO
If we execute the statement above and the DB doesn’t exist, an error message “Cannot drop the database
'LearnSQLServerIntuitively', because it does not exist or you do not have permission.“ is returned and the
execution of the batch stops. To avoid this, we wrap the execution of the DROP with the IF condition:
USE [master];
GO
USE [master];
GO
or
104
DDL Statements
DROP
USE LearnSQLServerIntuitively;
GO
Execute DROP SCHEMA with the IF condition to prevent the code termination in case the schema doesn’t exist:
USE LearnSQLServerIntuitively;
GO
105
DDL Statements
DROP
We can’t delete schema that contains DB object(s). The error message “Cannot drop schema 'Sales' be-
! cause it is being referenced by object 'SalesDetails'.” is returned.
DROP TABLE
DROP CONSTRAINT
106
DDL Statements
DROP
DROP VIEW
USE LearnSQLServerIntuitively;
GO
or
IF EXISTS Validate if the view exists with IF EXISTS in the Object Catalog
(
SELECT [name]
FROM sys.views
WHERE
SCHEMA_NAME([schema_id]) = 'dbo'
AND [name] = 'vw_Sales'
)
BEGIN DROP VIEW dbo.vw_Sales; END
GO
USE LearnSQLServerIntuitively;
GO
107
DDL Statements
DROP
IF EXISTS Validate if the stored procedure exists with IF EXISTS in the Object Catalog
(
SELECT [name]
FROM sys.procedures
WHERE
SCHEMA_NAME([schema_id]) = 'dbo'
AND [name] = 'usp_Customers_CRUD'
)
BEGIN DROP PROC dbo.usp_Customers_CRUD; END
GO
DROP FUNCTION
USE LearnSQLServerIntuitively;
GO
IF EXISTS Validate if the function exists with IF EXISTS in the Object Catalog
(
SELECT [name]
FROM sys.objects
WHERE IF = SQL_INLINE_TABLE_VALUED_FUNCTION
[type] IN ('IF', 'FN', 'TF') FN = SQL_SCALAR_FUNCTION
AND SCHEMA_NAME([schema_id]) = 'dbo' TF = SQL_TABLE_VALUED_FUNCTION
AND [name] = 'udf_DateNoTime'
108
DDL Statements
DROP
)
BEGIN DROP FUNCTION dbo.udf_DateNoTime; END
GO
Object Type
As you noticed, the second parameter in the built-in function OBJECT_ID() is object type. We specify it to limit the
function to search only for specific object type.
SELECT DISTINCT
[type] AS ObjectType
, [type_desc] AS ObjectTypeDescription
FROM sys.all_objects
ORDER BY type_desc;
GO
109
DDL Statements
Script Objects in SSMS
The DB objects, covered by this book are database, schema, table, view, stored procedure and function.
View
In SSMS: Expand the Object Explorer to the View (right click) Script View as CREATE To (ALTER To)
File... (New Query Editor Window)
Stored Procedure
In SSMS: Expand the Object Explorer to the Stored Procedure (Database Programability Stored Proce-
dures) (right click) Script Stored Procedure as CREATE To (ALTER To) File... (New Query Editor
Window)
110
DML Stataments
As the DDL statements are the ones that manage the DB objects, the DML statements are the ones that manipu-
late the data in the DB. They are represented by CRUD as follows:
• INSERT for C (Create)
• SELECT for R (Read)
• UPDATE for U (Update)
• DELETE for D (Delete)
The source of the data is/are the table(s), view(s), table-valued function(s).
T-SQL extracts data from these objects into virtual recordsets (virtual tables, created in the background, used to
store data the T-SQL needs to manipulate) or tables (temporary or real) and manipulates it.
After all the manipulations are done, the resulting recordset is returned to the requester (SSMS or the application
that executed the T-SQL code).
To create a report that shows the sales persons, eligible for commission (Threshold >= 10) for October 1968, or-
dered by name, we extract the data from multiple sources:
111
DML Stataments
and manipulate the virtual recordset to build the resulting recordset, used for the report:
The recordsets are created and used in background. After all the manipulations are done and the data is returned
to the requester, the virtual recordsets are deleted.
The example above summarizes the material that we’ll learn in DML Statements.
112
DML Statements (Modify)
INSERT
INSERT
or
INSERT dbo.Customers
(
FirstName
, LastName
, Email
, ...
)
...Statement that creates the recordset to be inserted...;
GO
Customers
FirstName LastName Email
Anabel Larson anabel.larson@customer5.info
Anna Laurier anna.laurier@customer2.net
Beverly NULL NULL
Existing table into which we insert rows
John Smith john.smith@customer1.com
John Smith john.smith@customer3.org
113
DML Statements (Modify)
INSERT
INSERT... SELECT
This example Inserts one row. The column list is not specified so we provide values for all the columns in the table
in the same order as in the table structure:
INSERT dbo.Customers
SELECT
'Melanie' Insert in the first column
, 'Larson' Insert in the second column
, 'melanie.larson@customer4.biz'; Insert in the third column
GO
The next example Insert multiple rows and specifies the column list. The UNION clause unions two recordsets into
one:
We can SELECT rows from one data source and INSERT them into existing table:
INSERT dbo.Customers
(
LastName The number of the inserted rows has to
, FirstName match the number of the rows in the re-
, Email cordset, produced by the SELECT statement
)
SELECT
LastName
SELECT... FROM creates recordset from
, FirstName
rows in existing object (table, view or
, Email table-valued function)
FROM [Another Database].[Another Schema].Customers
GO
INSERT... VALUES
114
DML Statements (Modify)
INSERT
INSERT dbo.Customers
VALUES ('Jonathan', 'Grenier', 'jg@jgcompanyandassocies.ca');
GO
To create recordset with multiple rows, we delimit them with comma (,):
INSERT... EXEC
The columns, not specified in the column list of the INSERT clause will be populated with NULL or their default
values (if default constraint is specified for the columns).
The constraints are checked on INSERT and if there is a violation in a constraint, the row is not inserted.
We specify the names of the columns that we insert into. If we don’t, a change of the design of the object
may break the code. For example if we insert into a table with 4 columns and we didn’t specify the column
list, adding a 5-th column to the table will cause the INSERT statement to return an error 'Column name or
number of supplied values does not match table definition'.
INSERT limitations
• Number of columns: 4,096
115
DML Statements (Modify)
SELECT... INTO
SELECT... INTO
SELECT... INTO combines DDL and DML clauses into one statement. It SELECTs data from an existing object (table,
view or table-valued function) and inserts the resulting recordset into a new table, created during the execution of
the SELECT... INTO statement.
If the new table, specified in the INTO clause, already exists, the execution of the statement will return an
error “There is already an object named 'DestinationTableName' in the database.” and will be terminated.
To avoid this, we run a cleanup statement first (If DestinationTableName exists, delete it).
The columns in the destination table inherits the data types of the columns in the recordset.
Customers
First- Last-
Email
Name Name
John Smith john.smith@customer3.org
Melanie Larson melanie.larson@customer4.biz SELECT
Xavier Jameson xavier.j@customer6.com CONCAT(FirstName, ' ', LastName) AS [Name]
, Email
NVARCHAR(32) NVARCHAR(64) INTO dbo.Customers_1
VARCHAR(128)
NVARCHAR(97)
FROM dbo.Customers;
Customers_1 32 + space + 64 = 97
GO
Name Email
John Smith john.smith@customer3.org
Melanie Larson melanie.larson@customer4.biz
Xavier Jameson xavier.garcía@customer6.com
116
DML Statements (Modify)
SELECT... INTO
In the example below, the data type of the TotalValue column in the source table is TINYINT (Value: 0 to 255). The
query sums the column TotalValue and the summed value for CustomerID = 1 (280) extends the range of TINYINT
(255). The data type of TotalValue in the destination table is converted to INT.
Sales
CustomerID ItemID TotalValue SELECT
1 3 130 CustomerID
2 6 37 , ItemID
1 3 150 , SUM(TotalValue) AS Sum_TotalValue
INTO dbo.Sales_1
INT INT TINYINT FROM dbo.Sales
Sales GROUP BY
CustomerID
CustomerID ItemID Sum_TotalValue
, ItemID;
1 3 280
GO
2 6 37
If a column in the SELECT clause has no name (if we omit the alias AS Sum_TotalValue in the example
! above), the DBE will return an error “An object or column name is missing or empty. For SELECT INTO state-
ments, verify each column has a name. For other statements, look for empty alias names. Aliases defined as
“” or [] are not allowed. Change the alias to a valid name.”.
117
DML Statements (Modify)
UPDATE
UPDATE DatabaseName.SchemaName.UpdatedObjectName
SET UpdatedColumnName = NewValue; Table, view or table-valued function
GO
Customers
CustomerID FirstName LastName Email
1 Anabel Larson anabel.larson@customer5.info
2 Anna Laurier anna.laurier@customer2.net
3 Beverly NULL NULL
4 John Smith john.smith@customer1.com
5 John Smith john.smith@customer3.org
6 Melanie Larson larson_mel@customer4.biz
7 Xavier Jameson xj@customer6.com
8 Zak Smith NULL
UPDATE... SET
Customers
CustomerID FirstName LastName Email
1 Anabel Larson customer84186@customer84186.pl
2 Anna Laurier customer84186@customer84186.pl
3 Beverly NULL customer84186@customer84186.pl
4 John Smith customer84186@customer84186.pl
5 John Smith customer84186@customer84186.pl
6 Melanie Larson customer84186@customer84186.pl
7 Xavier Jameson customer84186@customer84186.pl
8 Zak Smith customer84186@customer84186.pl
Affects only the rows that correspond to the filter condition(s) in the WHERE clause.
118
DML Statements (Modify)
UPDATE
UPDATE dbo.Customers
SET Email = 'customer84186@customer84186.pl'
WHERE
FirstName = 'Anabel'
AND LastName = 'Larson';
GO
LastName = 'Larson'?
Customers FirstName = 'Anabel'?
Custo- First- Last-
Email
merID Name Name Email
1 Anabel Larson anabel.larson@customer5.info Yes AND Yes customer84186@customer84186.pl
2 Anna Laurier anna.laurier@customer2.net No AND No
3 Beverly NULL NULL No AND No
4 John Smith john.smith@customer1.com No AND No New value
5 John Smith john.smith@customer3.org No AND No
6 Melanie Larson larson_mel@customer4.biz No AND Yes
7 Xavier Jameson xj@customer6.com No AND No
8 Zak Smith NULL No AND No
Logical Operator
The following tables are used to demonstrate an UPDATE... SET... FROM... WHERE example:
Customer
First- Last- IsEligibleFor- IsLowSales-
CustomerID Email DateLastUpdated
Name Name Discount Value
1 Anabel Larson anabel.larson@customer5.info NULL NULL NULL
2 Anna Laurier anna.laurier@customer2.net NULL NULL NULL
3 Beverly NULL NULL NULL NULL NULL
4 John Smith john.smith@customer1.com NULL NULL NULL
5 John Smith john.smith@customer3.org NULL NULL NULL
6 Melanie Larson larson_mel@customer4.biz NULL NULL NULL
7 Xavier Jameson xj@customer6.com NULL NULL NULL
8 Zak Smith NULL NULL NULL NULL
PK
119
DML Statements (Modify)
UPDATE
Sales
SaleID CustomerID ItemID DateOfSale Quantity Price LineTotal
1 7 345 1967-05-13 512 5.26 2693.12
2 1 58 1965-01-12 23 5.22 120.06
3 2 173 1965-12-14 1 5.231 5.231
4 5 1542 1968-08-15 55 66.231 3642.705 ItemID = 58
5 3 58 1967-11-28 276 5.24 1446.24
DateOfSale in the
6 4 234 1964-04-07 32 126.24 4039.68 last 30 days (in-
7 2 6562 1968-10-01 27 4638.02 125226.54 cluding today)
8 5 44 1968-03-28 127 1.24 157.48
9 2 58 1967-12-03 65 5.24 340.6
10 5 727 1965-08-17 52 5.21 270.92
This example UPDATEs the value of IsEligibleForDiscount for the customers that bought ItemID = 58 in the last
30 days (including today):
UPDATE C Alias
SET
IsEligibleForDiscount = 1
, DateLastUpdated = GETUTCDATE()
FROM
dbo.Customers AS C
JOIN dbo.Sales AS S
ON C.CustomerID = S.CustomerID
WHERE
S.ItemID = 58
AND S.DateOfSale BETWEEN DATEADD(DD, -30, CAST(GETDATE() AS DATE)) AND CAST(GETDATE() AS
DATE);
GO
120
DML Statements (Modify)
UPDATE
The example below UPDATEs columns IsLowSalesValue and DateLastUpdated (in table Customers) with the values
from the returned by the subquery (SQ) recordset (ILSV and DEFDU), on the rows where the value of CustomerID
column equals the value CustID in the subquery (SQ):
UPDATE dbo.Customers
SET
IsLowSalesValue = SQ.ILSV
, DateLastUpdated = SQ.DEFDU
FROM
The subquery (between the brack-
(
ets) returns this recordset
SELECT
C.CustomerID AS CustID
CustID ILSV DEFDU
, 1 AS ILSV
1 1 1967-12-18
, GETUTCDATE() AS DEFDU
FROM
dbo.Customers AS C
JOIN dbo.Sales AS S
ON C.CustomerID = S.CustomerID
WHERE S.ItemID = 58
GROUP BY C.CustomerID
HAVING SUM(S.LineTotal) < 200
) AS SQ
WHERE CustomerID = SQ.CustID;
GO
121
DML Statements (Modify)
UPDATE
122
DML Statements (Modify)
DELETE
! DELETE statement without WHERE clause deletes all the rows in the table.
DELETE [DatabaseName].[SchemaName].[TableName]
Deletes the rows that correspond
WHERE ColumnName Operator Value;
to the filter condition(s)
GO
When we join tables and delete rows from one of the joined tables, we specify from which table we delete rows.
We can identify the rows that we need to delete by writing a query that JOINs multiple DB objects. In the example
below, we JOIN tables Customers and Sales to pick the right rows for deleting from table Sales and delete them
in table Customers.
Customer FROM can be omitted
Custo- First- Last-
Email DELETE FROM C Alias
merID Name Name
1 Anabel Larson anabel.larson@cus- FROM
tomer5.info dbo.Customers AS C
2 Anna Laurier anna.laurier@custom- JOIN dbo.Sales AS S
er2.net
ON C.CustomerID = S.CustomerID
3 Beverly NULL NULL
WHERE S.DateOfSale <= '1967-01-01';
GO
PK
FK The rows of CustomerID 1 and 2 are deleted from table Customers
Sales
Custo- Quan-
SaleID ItemID DateOfSale Price LineTotal
merID tity
Translation: Create a recordset by
1 2 345 1967-05-13 512 5.26 2693.12
joining both tables and delete from
2 1 58 1965-01-12 23 5.22 120.06
Customers the customers which sales
3 2 173 1965-12-14 1 5.231 5.231
are older than a specified date.
123
DML Statements (Modify)
DELETE
Before we execute the DELETE or UPDATE statements, we execute the same statement with the SELECT
clause, instead of the DELETE or UPDATE clause to verify the rows to be deleted/updated.
Execute first:
SELECT *
FROM dbo.Customers
WHERE FirstName = 'John';
GO
After you confirm that the statement affects the exact rows, execute:
DELETE dbo.Customers
WHERE FirstName = 'John';
GO
UPDATE dbo.Customers
SET Email = 'j@customer2.net'
WHERE FirstName = 'John';
GO
124
DML Statements (Query)
SELECT
Creates a recordset and returns it to the requester (SSMS, external application that queries the DB etc.).
The SELECT clause can be constructed from a single SELECT clause or multiple clauses - SELECT... FROM... WHERE...
Manipulates
T-SQL Data Type Returned Value or Recordset
What
Value SELECT 'John'; String (in single quotes) John
SELECT 123; Numeric 123
Expression SELECT ('John' + ' ' + 'Smith'); String and String John Smith
SELECT (123 + 456); Numeric and Numeric 579
SELECT ('John' + ' ' + CAST(123 AS String and Numeric, explicitly John 123
VARCHAR)); converted to String
SELECT (Price * 1.3) Column value and Numeric 3.9
FROM DBObjectName (Price = 3)
SELECT SellPrice - Expences Column value and column 7.18
FROM DBObjectName value (SellPrice = 9.86
Expences = 2.68)
Built-in function SELECT GETDATE(); Output: Date and Time 1965-06-05 13:33:23.390
(now)
SELECT LEN('John'); Input: String 4
Output: Numeric
User-defined SELECT dbo.udf_ Function output data type 1968-04-13 00:00:00.000
function GetFirstDayOfCurrentFiscalMonth();
Value, expres- SELECT Both columns: String FirstName LastName
sion, user-de- 'John’' AS FirstName
John Smith
fined function , 'Smith' AS LastName;
and retutns SELECT First column: String FirstName NumericValue
recordset 'John' AS FirstName Second column: Numeric
(rows and col- , (100 + 23) AS NumericValue;
John 123
umns)
SELECT First column: String First- Numer-
UDFResult
'John' AS FirstName Second column: Numeric Name icValue
, 123 AS NumericValue Third column: Function output John 123 1968-04-13
, dbo.udf_ data type 00:00:00.000
GetFirstDayOfCurrentFiscalMonth()
AS UDFResult;
Variable * SELECT @FirstName AS The data type of the variable John
VariableValue; (@FirstName = John)
*
Variables on page 199
125
DML Statements (Query)
SELECT
SELECT DISTINCT...
The keyword DISTINCT, added to the SELECT statement, changes the logic to select only distinct (unique) rows.
The rows in table Customers are unique (the combination of all the columns are unique).
Customers
First- Last-
Email
Name Name
Anabel Larson anabel.larson@customer5.info
Anna Laurier anna.laurier@customer2.net
Beverly NULL NULL
John Smith john.smith@customer1.com
John Smith john.smith@customer3.org
When we select columns FirstName and LastName, the SELECT statement generates a recordset, that is not unique
(John Smith is a duplicate).
To generate a recordset with unique rows across the FirstName and LastName rows, we add the keyword DISTINCT
to the SELECT clause:
SELECT limitations
• Number of columns in the SELECT statement: 4,096
126
DML Statements (Query)
FROM
The FROM clause points to the DB object (table - real or temporary, view, table-values function) or subquery which is
a data source for the SELECT clause.
When we select all the columns, we use SELECT * (star). If we need specific columns, we SELECT a list of columns,
delimited with comma (,).
Customer
CustomerID FirstName LastName Email
1 Anabel Larson anabel.larson@customer5.info
SELECT FirstName, LastName
2 Anna Laurier anna.laurier@customer2.net
FROM Customers;
3 Beverly NULL NULL
GO
Subquery
The subquery is a nested SELECT statement (inner) into another SELECT statement (outer).
When we have a subquery in the FROM clause the inner SELECT statement creates a recorsdet and the outer SELECT
statement selects from this recordset.
1. List the columns in the SELECT clause instead of using * (star), because:
1.1. After a change of the design of the data source the query may break.
1.2. * Selects columns that we don’t need and creates an overload of the server.
2. Use the DSO ([DatabaseName].[SchemaName].[ObjectName]) naming convention as identifier in the
FROM clause. This way we can move the SQL code to another database in the same instance on the SQL
Server® without editing it in order to point to the exact object.
127
DML Statements (Query)
JOIN
SELECT... FROM... JOIN ...or create a recordset from multiple data sources
The data in the RDBMS are related and it is in multiple tables split by subject area. Joining related tables is one of
the most important and powerful tasks in SQL.
When we select data from multiple data sources (DS) - table, view, table-valued function or subquery - we JOIN
them.
The joined objects create a virtual recordset (VR) in the background that holds all data from the data sources and
we query this VR.
We JOIN the objects ON one or multiple columns.
DS - data source
The syntax is: Aliases on page 138
SELECT
L.Col2 Alias in the SELECT clause (bind alias)
, R.Col3
Keyword (type of joining), JOIN,
, ...
right data source and alias
FROM
LeftDataSource AS L Left data source and alias in the FROM clause (assign alias)
(INNER, LEFT, RIGHT, FULL) JOIN RightDataSource AS R
ON L.Column1 = R.Column1 After the ON clause, we list the
AND JOIN condition(s) (link column(s))
( When we use both AND and OR logical op-
L.Column2 = R.Column2 erators, we separate them with brackets
Comparison operators
OR L.Column3 >= R.Column3
)
AND R.Column4 > 123 Filter the VR with value or expression
AND ...;
GO Multiple JOIN conditions joined with logical operators (AND, OR)
128
DML Statements (Query)
JOIN
Let’s join the two sources below. For the simplicity of the example, the test data is:
• Col1 is used to join the tables and values that exist in both tables such as Col1 (3,4 and 5)
• The values in Col2 and Col3 are: first character is the column index and the second character is the value in
Col1
Left data source Right data source
Col1 Col2 Col3 Col1 Col2 Col3
1 21 31 3 23 33
2 22 32 4 24 34
3 23 33 5 25 35
4 24 34 6 26 36
5 25 35 7 27 37
ON L.Col1 = R.Col1
The statement that manipulates the joined tables is actually manipulating the recordset, built with the FROM... JOIN
clause.
INNER JOIN is inner, because it creates a VR that contains only the rows that meet the join criteria(s).
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
JOIN dbo.RightDataSource AS R
ON L.Col1 = R.Col1;
GO The keyword INNER is optional and can be omitted
129
DML Statements (Query)
JOIN
ON L.Col1 = R.Col1
OUTER JOIN is outer, because it creates VR, that contains more rows than the rows that meet the JOIN condition(s)
in the ON clause.
LEFT JOIN creates VR with all the rows from the left data source and only the rows from the right data source that
meet the JOIN condition(s).
The VR fills the rows in the right table that don’t have a match on the JOIN condition(s) with NULL.
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
LEFT JOIN dbo.RightDataSource AS R
ON L.Col1 = R.Col1;
GO
The keyword OUTER is op- Left data source Right data source
tional and can be omitted Col1 Col2 Col3 Col1 Col2 Col3
1
2
21
22
31 NULL
32 NULL
NULL
NULL
NULL
NULL } Out of the JOIN condition(s)
}
Resulting VR 3 23 33 3 23 33
4 24 34 4 24 34 Meet the JOIN condition(s)
5 25 35 5 25 35
6 26 36
Not
7 27 37
manipulated
ON L.Col1 = R.Col1
130
DML Statements (Query)
JOIN
When we use the LEFT or RIGHT JOIN and filter in the WHERE clause, the JOIN type is converted from LEFT
! (RIGHT) to INNER.
To avoid this and filter only the LEFT (RIGHT) data source, we filter in the ON clause.
SELECT
T1.A
, T1.B
, T2.C
FROM
Table1 AS T1
LEFT JOIN Table2 AS T2
ON T1. Column1 = T2.Column1
AND T2.Column 2 = @Parameter1 Filter here
WHERE T2.Column 2 = @Parameter1; If we filter on the WHERE clause, the JOIN
GO is converted from LEFT (RIGHT) to INNER
The same logic as LEFT JOIN, but reversed - the VR contains all the rows from the right data source and the
matching rows from the left data source.
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
RIGHT JOIN dbo.RightDataSource AS R
ON L.Col1 = R.Col1;
GO
Left data source Right data source
Col1 Col2 Col3 Col1 Col2 Col3
1 21 31
Not manipulated
2 22 32
3 23 33 3 23 33
4 24 34 4 24 34
5 25 35 5 25 35 Resulting VR
NULL NULL NULL 6 26 36
NULL NULL NULL 7 27 37
ON L.Col1 = R.Col1
131
DML Statements (Query)
JOIN
Creates a VR with all the rows from both data sources. The unmatched values in the left and the right source are
represented with NULL. It is a combined LEFT JOIN and RIGHT JOIN.
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
FULL JOIN dbo.RightDataSource AS R
ON L.Col1 = R.Col1;
GO
Left data source Right data source
Col1 Col2 Col3 Col1 Col2 Col3
1 21 31 NULL NULL NULL
2 22 32 NULL NULL NULL
3 23 33 3 23 33
Resulting VR 4 24 34 4 24 34
5 25 35 5 25 35
NULL NULL NULL 6 26 36
NULL NULL NULL 7 27 37
ON L.Col1 = R.Col1
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
LEFT JOIN dbo.RightDataSource AS R
ON L.Col1 = R.Col1
WHERE R.Col1 IS NULL;
GO
132
DML Statements (Query)
JOIN
ON L.Col1 = R.Col1
• Rows from the right data source, that don’t have a match in the left data source (RIGHT JOIN WHERE
LeftRecordset IS NULL)
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
RIGHT JOIN dbo.RightDataSource AS R
ON L.Col1 = R.Col1
WHERE L.Col1 IS NULL;
GO
ON L.Col1 = R.Col1
• Rows from both data sources, that don’t have a match (FULL JOIN WHERE LeftRecordset OR RightRecordset
IS NULL). FULL JOIN except INNER JOIN.
133
DML Statements (Query)
JOIN
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
FULL JOIN dbo.RightDataSource AS R
ON L.Col1 = R.Col1
WHERE
L.Col1 IS NULL
OR R.Col1 IS NULL;
GO
ON L.Col1 = R.Col1
CROSS JOIN
Creates a VR, containing all the combinations of the rows in both data sources, (a.k.a. Cartesian Product).
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
CROSS JOIN dbo.RightDataSource AS R;
GO
No ON clausev
134
DML Statements (Query)
JOIN
When we join on a column, that stores NULL, the rows with NULL can’t be joined like NULL = NULL (L.Col1 =
R.Col1).
INSERT dbo.LeftDataSource
(
Col1
, Col2 The same INSERT statement to
, Col3 the right data source
)
SELECT
NULL
Left data source Right data source
, '2NULL'
Col1 Col2 Col3 Col1 Col2 Col3
, '3NULL';
1 21 31 3 23 33
GO
2 22 32 4 24 34
3 23 33 5 25 35
4 24 34 6 26 36
5 25 35 7 27 37
NULL 2NULL 3NULL NULL 2NULL 3NULL
135
DML Statements (Query)
JOIN
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
JOIN dbo.RightDataSource AS R
ON L.Col1 = R.Col1;
GO We can’t link NULL to NULL
ON L.Col1 = R.Col1
To return the linked NULL to NULL rows, we use the built-in function ISNULL() to replace NULL (unknown) with
known value and use this value to join.
SELECT
L.*
, R.*
FROM
dbo.LeftDataSource AS L
JOIN dbo.RightDataSource AS R
ON ISNULL(L.Col1, '') = ISNULL(R.Col1, '');
Link empty string to empty string
GO
136
DML Statements (Query)
JOIN
ON L.Col1 = R.Col1
Replacing NULL with a real value can create incorrect results, because NULL (unknown) is not an actual value
and we are joining unknown to unknown.
137
Aliases
The alias gives a pseudo name to a DB object, column or expression and is used to:
• Point to the exact data source and the exact column when JOINing data sources
• Makes the code neat and easy to understand
Customers Sales
Quantity
ItemID
SaleID
Custo-
merID
Custo-
DateOf- DateModi-
merID
CustomerID and DateModified exist in both tables. When we JOIN the data sources, we need to create a separate
identification of the columns with the same name:
• CustomerID from Customers • DateModified from Customers
• CustomerID from Sales • DateModified from Sales
SELECT
FirstName
Exists only in Customers
, LastName
, DateModified Exists in more than one data source
FROM
dbo.Customers
JOIN dbo.Sales
ON dbo.Customers.CustomerID = dbo.Sales.CustomerID
WHERE CustomerID = 2; Exists in more than one data source
GO
138
Aliases
To point to the exact columns in the exact objects, we add the identifiers before the column name as described in
Naming Conventions - DSO:
SELECT
FirstName
, LastName
, dbo.Customers.DateModified
Bind to the exact object and column
, dbo.Sales.DateModified
, (Quantity * Price) AS LineTotal Assign Alias for the column name
FROM
dbo.Customers
JOIN dbo.Sales
ON dbo.Customers.CustomerID = dbo.Sales.CustomerID [SchemaName].[ObjectName].[ColumnName]
WHERE (Quantity * Price) >= 500
ORDER BY LineTotal DESC;
GO
The DSO identification is too long to be added in all the clauses where needed which makes the code hard to read.
The next step is to replace the DSO identifiers with Aliases:
SELECT
No Alias. The statement will work until we add an-
FirstName
other column FirstName, selected from another source
, C.LastName
, C.DateModified AS Cust_DateModified 1. Bind to alias, assigned in the FROM clause (C, S)
, S.DateModified AS Sale_DateModified 2. Assign the alias in the SELECT clause (column name)
, (S.Quantity * S.Price) AS LineTotal
FROM
dbo.Customers AS C
Assign alias
JOIN dbo.Sales AS S
ON C.CustomerID = S.CustomerID
Can’t use aliases, assigned in the
WHERE (S.Quantity * S.Price) >= 500
SELECT clause in the WHERE clause
ORDER BY LineTotal DESC;
GO
139
Aliases
With the keyword AS we assign an alias. The keyword AS is optional and can be omitted.
With . (dot) we bind to an already assigned alias.
Don’t skip the keyword AS. It is very useful for a quick search of aliases in the development
or debugging processes.
When we JOIN data sources, we always add aliases to always point to the exact column in the exact source.
If
(
We don’t use aliases
and
Modify a data source, used by the statement (add column with the same name as a col-
umn in another data source, used by the statement)
)
{ the execution stops with the error message “Ambiguous column name”. };
We can assign column names of an alias object by adding the list next to the table alias in round braces:
SELECT FN LN
C.FN Anabel Larson
, C.LN Anna Laurier
FROM
(
C
SELECT TOP 2
FirstName LastName
FirstName Anabel Larson
, LastName Anna Laurier
FROM dbo.Customers
) AS C (FN, LN);
GO
Assign table alias and column aliases in braces
In the SELECT clause we can use assign (creates a pseudo name for the column) and bind (binds to the data source
in the FROM clause) aliases.
140
Aliases
If we skip the alias, the column header is (No column name): (No column name)
SELECT TOP 3 CONCAT(FirstName, ' ', LastName) Anabel Larson
FROM dbo.Customers Anna Laurier
GO Beverly
Create aliases without special characters, to facilitate the usage of the column names in the requesting
application.
We can assign an alias with the equal sign: • Can be used only in the SELECT clause
! • The syntax that assigns value to variable is similar:
Alias Column Identifier
The assign alias in the FROM clause creates a pseudo name for the DB object.
Bind alias in ON clause, pointing to the assign alias in the FROM clause
141
DML Statements (Query)
WHERE
After a virtual recordeset (VR) is built with the SELECT and FROM (if FROM is used) clauses, we can filter the rows with
the WHERE clause.
How it works?
1. The SELECT (and FROM) clause(s) build the source virtual recordset (SVR)
2. The filter condition(s) in the WHERE clause is/are verified on every row of the SVR
3. If all the filtered conditions return True, the row is selected in the resulting recordert (RR)
SELECT
FirstName
, LastName
FROM dbo.Customers
WHERE LastName LIKE 'La%';
GO
SELECT...
FROM...
WHERE {Left Operand} {Operator} {Right Operand};
GO
In the statement:
142
DML Statements (Query)
WHERE
• The SELECT clause builds the SVR (one row, one column and value 'ABC')
• The WHERE clause is validated once (one row in the SVR) and the result of the validation is False (17 doesn’t
equal 45)
• The RR is blank (nothing is selected)
The statement:
returns a RR with one row, one column and value 'ABC', because the filter condition in the where clause is
validated to True (17 equals 17).
143
DML Statements (Query)
WHERE
Operators on page 71
SELECT *
FROM dbo.Customers
WHERE FirstName = 'John';
GO
Operator
Left operand Right operand
Filter condition
Customers
First- Last- Row is re-
Email FirstName = 'John'?
Name Name turned?
Anabel Larson anabel.larson@customer5.info False No
Anna Laurier anna.laurier@customer2.net False No
Beverly NULL NULL False No
John Smith john.smith@customer1.com True Yes
John Smith john.smith@customer3.org True Yes
Melanie Larson melanie.larson@customer4.biz False No
Xavier Jameson xavier.garcía@customer6.com False No
Zak Smith NULL False No
Example with more than one filter conditions (AND, OR logical operators)
144
DML Statements (Query)
WHERE
SELECT *
FROM dbo.Customers
WHERE
FirstName = 'Anabel' Left filter condition
AND LastName = 'Larson'; Right filter condition
GO
Logical operator AND which joins the filter conditions
Customers
First- Last- FirstName = Logical Join LastName = Row is re-
Email
Name Name 'Anabel'? Operator 'Larson'? turned?
Anabel Larson anabel.larson@customer5.info True AND True Yes
Anna Laurier anna.laurier@customer2.net False AND False No
Beverly NULL NULL False AND False No
John Smith john.smith@customer1.com False AND False No
John Smith john.smith@customer3.org False AND False No
Melanie Larson melanie.larson@customer4.biz False AND True No
Xavier Jameson xavier.garcía@customer6.com False AND False No
Zak Smith NULL False AND False No
Filter
condition
SELECT *
FROM dbo.Customers
WHERE
FirstName = 'Anabel' Left filter condition
OR LastName = 'Larson'; Right filter condition
GO
Logical operator AND that joins the filter conditions
Customers
First- Last- FirstName = Logical Join LastName = Row is re-
Email
Name Name 'Anabel'? Operator 'Larson'? turned?
Anabel Larson anabel.larson@customer5.info True OR True Yes
Anna Laurier anna.laurier@customer2.net False OR False No
Beverly NULL NULL False OR False No
John Smith john.smith@customer1.com False OR False No
John Smith john.smith@customer3.org False OR False No
Melanie Larson melanie.larson@customer4.biz False OR True Yes
Xavier Jameson xavier.garcía@customer6.com False OR False No
Zak Smith NULL False OR False No
145
DML Statements (Query)
WHERE
More than one filter conditions, joined with both logical operator AND and OR:
SELECT *
FROM dbo.Customers Separate (in brackets) the OR opera-
WHERE tor(s) from the AND operator(s).
(
FirstName = 'Anabel' Left filter condition
OR LastName = 'Larson' Right filter condition
) Left filter condition (between the brackets)
AND Email LIKE '%.biz'; Right filter condition
GO
Email LIKE
'%.biz'?
Customers Logical Join AND
Operator Evaluation
First- Last-
Email
Name Name
Anabel Larson anabel.larson@customer5.info True OR True True AND False False No
Anna Laurier anna.laurier@customer2.net False OR False False AND False False No
Beverly NULL NULL False OR False False AND False False No
John Smith john.smith@customer1.com False OR False False AND False False No
John Smith john.smith@customer3.org False OR False False AND False False No
Melanie Larson melanie.larson@customer4.biz False OR True True AND True True Yes
Xavier Jameson xavier.garcía@customer6.com False OR False False AND False False No
Zak Smith NULL False OR False False AND False False No
The WHERE clause is used to filter not only SELECT statements; it is also used in other DML statements (UPDATE, DE-
LETE) that manipulate SVR.
146
DML Statements (Query)
ORDER BY
We can ORDER BY column(s), not included in the RR (not in the SELECT clause).
147
DML Statements (Query)
TOP
When we extend the SELECT clause with the TOP keyword, we select a recordset with Number or Percent rows.
In order to return the exact result, TOP always has to be used together with ORDER BY clause.
Customers
First- Last- DateOf-
Email
Name Name Birth
Anabel Larson anabel.larson@customer5.info 1967-05-11
Anna Laurier anna.laurier@customer2.net 1967-05-11
Beverly NULL NULL 1965-11-09
John Smith john.smith@customer1.com 1963-07-19
John Smith john.smith@customer3.org 1965-11-09
Melanie Larson melanie.larson@customer4.biz 1965-09-08
Xavier Jameson xavier.garcía@customer6.com 1965-11-09
Zak Smith NULL 1963-12-11
148
DML Statements (Query)
TOP
149
DML Statements (Query)
OFFSET... FETCH
Starting from any row but the first, we can select rows from ordered recordsets. To do this, we use the OFFSET
clause, added to the ORDER BY clause.
Name Name
8 Zak Smith NULL
By adding the FETCH clause to OFFSET, we can pick a specified number of rows, starting from the row specified in
OFFSET:
SELECT *
FROM dbo.Customers
ORDER BY
FirstName 1 ROW ONLY, X ROWS ONLY
, LastName
OFFSET 3 ROWS
Custo- First- Last- Email
FETCH NEXT 3 ROWS ONLY; merID Name Name
GO 5 John Smith john.smith@customer3.org
OFFSET and FETCH are very useful when CustomerID from 1 to 3 are skipped
we need to paginate the result. and the next 3 rows are selected
150
DML Statements (Query)
GROUP BY
By adding a GROUP BY clause, we create groups from columns and calculate values for other columns.
• Grouped columns (a.k.a. Dimensions) – the columns that define the scope for aggregation (the groups)
• Aggregated columns (a.k.a. Facts, Measures or Properties) – summarized data for the groups
First we build a virtual Next we modify the query to create groups on Col1, Col2 and Col3 and aggregate
recordset: the data in columns Col6 to Col10:
SELECT SELECT
}
Col1, Col2, Col3 Col1 AS [Column 1]
Grouped columns (Source is struc-
, Col6, Col7, Col8 , Col2
ture tables or Dimensions in OLAP)
, Col9, Col10 , Col3
}
FROM dbo.GroupBy1 , SUM(Col6) AS [Sum Col6]
Aggregated columns
GO , AVG(Col7) AS Avg_Col7
(Source is data tables
, MIN(Col8) AS Min_Col8
or Facts in OLAP.
, MAX(Col9) AS Max_Col9 A.k.a. Measures in
, COUNT(Col10) AS Count_Col10 the reporting slang)
, COUNT(DISTINCT Col10) AS CountDistinct_Col10
FROM dbo.GroupBy1
151
DML Statements (Query)
GROUP BY
GROUP BY
}
Col1
Grouped columns. We can’t
, Col2
use the alias Column 1 here Resulting recordset
, Col3;
GO
dbo.GroupBy1
SUM() AVG() MIN() MAX() COUNT() COUNT(DISTINCT)
Column 1 Col2 Col3 Sum Col6 Avg_Col7 Min_Col8 Max_Col9 Count_Col10 CountDistinct_Col10
Group 1 1 2 3 8.000 4.000000 1 7 3 2
Group 2 2 1 3 25.000 4.000000 -56 78.5 4 4
Group 3 3 2 1 72.000 10.750000 AAB BCD 4 3
Grouped columns Aggregated columns
The grouped columns in the resultset are unique (no duplicates for Col 1, Col2 and Col3).
The aggregate functions summarize the values for the aggregated columns.
• GROUP BY can’t use the bound aliases, assigned in the SELECT clause, because GROUP BY is before SE-
LECT (where the aliases are assigned) in the execution order
When we SELECT columns and GROUP BY the same columns, without aggregating other columns, the resultset is the
same as if we SELECT DISTINCT these columns.
152
DML Statements (Query)
GROUP BY
153
DML Statements (Query)
HAVING
The HAVING clause filters already aggregated data. It can’t be used without the GROUP BY clause.
The step by step example below selects the customers with total sales more or equal to 150$ for the period from
1965-10-06 to 1967-05-15.
Quantity
ItemID
SaleID
154
DML Statements (Query)
HAVING
ItemID
DateOf- First- Last- LineTo-
1. Create a recordset (JOIN tables Customers and Sales)
Sale Name Name tal
SELECT
1967-01-24 Beverly NULL 2043 5.08
S.DateOfSale
1967-05-15 John Smith 5164 3.12
, C.FirstName
1966-12-11 Beverly NULL 5293 4244.24
, C.LastName
1969-01-17 John Smith 6223 14.67
, S.ItemID
1965-10-14 Xavier Jameson 1352 74.76
, (S.Quantity * S.Price) AS LineTotal
1966-03-05 Anna Laurier 1953 150.52
FROM
1963-04-13 Beverly NULL 8613 515.02
dbo.Customers AS C
1967-09-18 Anna Laurier 5523 6995.45
JOIN dbo.Sales AS S
1962-10-13 Anabel Larson 8534 45.88
ON C.CustomerID = S.CustomerID;
1965-10-06 Anna Laurier 9375 2.58
GO
2. Filter the recordset (sales WHERE the period is BETWEEN 1965-10-06 to 1967-05-15)
SELECT
S.DateOfSale DateOf- First- Last- ItemID LineTo-
Sale Name Name tal
, C.FirstName
, C.LastName 1966-03-05 Anna Laurier 1953 150.52
, S.ItemID 1965-10-06 Anna Laurier 9375 2.58
, (S.Quantity * S.Price) AS LineTotal 1967-01-24 Beverly NULL 2043 5.08
FROM 1966-12-11 Beverly NULL 5293 4244.24
dbo.Customers AS C 1967-05-15 John Smith 5164 3.12
JOIN dbo.Sales AS S 1965-10-14 Xavier Jameson 1352 74.76
ON C.CustomerID = S.CustomerID
WHERE S.DateOfSale BETWEEN '1965-10-06' AND '1967-05-15';
GO
155
DML Statements (Query)
HAVING
4. Filter the aggregated recordset (customers HAVING total sales more or equal to 150$)
156
DML Statements (Query)
GROUPING SETS
}
NULL NULL NULL 9405.4711 } Total
NULL NULL Item 1 830.469
NULL NULL Item 2 923.3026
NULL NULL Item 3 3475.4259
NULL NULL Item 4 1704.0768 Item Customer 3 NULL NULL 615.9823
}
NULL Store 1 NULL 1831.2501 Customer 7 NULL NULL 923.3026
157
DML Statements (Query)
GROUPING SETS
UNION ALL
158
DML Statements (Query)
GROUPING SETS
SELECT
Customer Customer and Item
, NULL AS Store
, Item
, SUM(SaleValue) AS Sum_SaleValue
FROM dbo.Sales
GROUP BY
Customer
, Item
UNION ALL
159
DML Statements (Query)
ROLLUP and CUBE
When we aggregate data, we add subtotals and a grand total to the resultset with ROLLUP and CUBE.
Add ROLLUP to the GROUP BY clause to calculate subtotals and the grand total for the grouped columns:
160
DML Statements (Query)
ROLLUP and CUBE
All the rows with NULL in a column, belonging to a group (not an aggregated column) are added by ROLLUP operator.
We can use the GROUPING() function to transform NULLs to human readable values:
SELECT
CASE
WHEN GROUPING(CustomerName) = 1 CASE on page 186
THEN 'Total: Customer'
ELSE CustomerName
SalesVal-
END AS Customer Customer Item Date
ue
, CASE Total: Customer Total: Item Total: Date 12051.32
WHEN GROUPING(ItemDescription) = 1 Anabel Larson Total: Item Total: Date 135.31
THEN 'Total: Item' Anabel Larson Plastic Plain Blue Total: Date 14.67
ELSE ItemDescription
Anabel Larson Plastic Plain Blue 1969-01-17 14.67
END AS Item
Anabel Larson Wooden Horse Total: Date 120.64
, CASE
Anabel Larson Wooden Horse 1962-10-13 45.88
WHEN GROUPING(DateOfSale) = 1
Anabel Larson Wooden Horse 1965-10-14 74.76
THEN 'Total: Date of Sale'
John Smith Total: Item Total: Date 7151.67
ELSE CONVERT(VARCHAR(17), DateOfSale)
John Smith Plastic Plain Blue Total: Date 5.70
END AS DateOfSale
John Smith Plastic Plain Blue 1965-10-06 2.58
, SUM(Quantity * Price) AS SalesValue
John Smith Plastic Plain Blue 1967-05-15 3.12
FROM dbo.Sales
John Smith Rag Doll 33 cm. Total: Date 7145.97
GROUP BY ROLLUP
John Smith Rag Doll 33 cm. 1966-03-05 150.52
(
John Smith Rag Doll 33 cm. 1967-09-18 6995.45
CustomerName
Xavier Jameson Total: Item Total: Date 4764.34
, ItemDescription
Xavier Jameson Plastic Plain Blue Total: Date 5.08
, DateOfSale
Xavier Jameson Plastic Plain Blue 1967-01-24 5.08
)
Xavier Jameson Rag Doll 33 cm. Total: Date 4244.24
ORDER BY
Xavier Jameson Rag Doll 33 cm. 1966-12-11 4244.24
CustomerName
Xavier Jameson Wooden Horse Total: Date 515.02
, ItemDescription
Xavier Jameson Wooden Horse 1963-04-13 515.02
, DateOfSale;
GO
CUBE
When we add CUBE to the GROUP BY clause, we calculate subtotals and grand totals for all the combinations in the
grouped columns:
Sales SaleID CustomerName ItemDescription SaleDate Quantity Price
1 Xavier Jameson Plastic Plain Blue 1967-01-24 4 1.27
2 Anabel Larson Wooden Horse 1962-10-13 1 45.88
3 Anabel Larson Wooden Horse 1964-11-07 1 41.32
161
DML Statements (Query)
ROLLUP and CUBE
SELECT Sales-
Customer Item Date
CASE Value
WHEN GROUPING(CustomerName) = 1 Total: Customer Total: Item Total: Date 92.28
THEN 'Total: Customer' Total: Customer Total: Item 1962-10-13 45.88
ELSE CustomerName Total: Customer Total: Item 1964-11-07 41.32
END AS Customer Total: Customer Total: Item 1967-01-24 5.08
, CASE Total: Customer Plastic Plain Blue Total: Date 5.08
WHEN GROUPING(ItemDescription) = 1 Total: Customer Plastic Plain Blue 1967-01-24 5.08
THEN 'Total: Item' Total: Customer Wooden Horse Total: Date 87.20
ELSE ItemDescription Total: Customer Wooden Horse 1962-10-13 45.88
END AS Item Total: Customer Wooden Horse 1964-11-07 41.32
, CASE Anabel Larson Total: Item Total: Date 87.20
WHEN GROUPING(DateOfSale) = 1 Anabel Larson Total: Item 1962-10-13 45.88
THEN 'Total: Date of Sale' Anabel Larson Total: Item 1964-11-07 41.32
ELSE CONVERT(VARCHAR(17), DateOfSale) Anabel Larson Wooden Horse Total: Date 87.20
END AS [Date] Anabel Larson Wooden Horse 1962-10-13 45.88
, SUM(Quantity * Price) AS SalesValue Anabel Larson Wooden Horse 1964-11-07 41.32
FROM dbo.Sales Xavier Jameson Total: Item Total: Date 5.08
GROUP BY CUBE Xavier Jameson Total: Item 1967-01-24 5.08
( Xavier Jameson Plastic Plain Blue Total: Date 5.08
CustomerName Xavier Jameson Plastic Plain Blue 1967-01-24 5.08
, ItemDescription
, DateOfSale
)
ORDER BY
CustomerName
In this example CUBE expanded
, ItemDescription 3 rows to 19 rows:
, DateOfSale; • 3 rows details
GO • 15 rows subtotals
• 1 row grand total
GROUP BY:
No GROUP BY
Date
Item
Item, Date
Customer
Customer, Date
Customer, Item
Customer, Item, Date
162
DML Statements (Query)
Execution Logic
Behind the scene SQL Server® is executing the DML query statement in a different order:
Step 1: FROM… JOIN - build the virtual recordset (VR) from one or multiple data sources (DS)
Step 2: WHERE - filter the VR
Step 3: GROUP BY... HAVING - group, aggregate and filter the grouped VR
Step 4: SELECT – build the resulting VR and create aliases
Step 5: ORDER BY – order the resulting VR and use the aliases, created in the SELECT statement
Step 1: FROM Sales AS S JOIN Customers AS C ON... Step 2: WHERE C.CustomerName != 'Beverly'
Resultset
from WHERE
163
DML Statements (Query)
Execution Logic
Datasource
for GROUP BY
Resultset
Datasource from HAVING
for SELECT
164
DML Statements (Query)
Subqueries
Subquery Outer
Inner
Subquery is a query (statement), nested in another query (statement).
During the execution, the subquery creates a virtual recordset (VR) a.k.a. derived (or virtual) table.
This recordset is the data source for the outer query (statement).
Customers Sales
SaleID Custo- ItemID DateOf- Quan- Price
Custo-
merID
The subquery in the SELECT clause has to return one value (one row and one column). If it returns more than one
value, an error message “Subquery returned more than 1 value. This is not permitted when the subquery follows =,
!=, <, <= , >, >= or when the subquery is used as an expression.” is returned:
165
DML Statements (Query)
Subqueries
FROM dbo.Sales AS S
GO
The statement below uses a subquery in the SELECT clause to show the average sales for all the customers next
to the individual sales:
dbo.Customers AS C 1205.132
JOIN dbo.Sales AS S
ON C.CustomerID = S.CustomerID
• GROUP BY FirstName LastName
Sum_ Avg_SalesForAll-
Sales Customers
C.FirstName
Anna Laurier 7148.55 1205.132
, C.LastName
Beverly NULL 4764.34 1205.132
• ORDER BY
John Smith 3.12 1205.132
C.FirstName
Melanie Larson 45.88 1205.132
, C.LastName;
Xavier Jameson 89.43 1205.132
GO
The subquery in the FROM clause allows us to extract the portion of the data that we need (from multiple data
sources, aggregated, etc) and JOIN it to the other data sources in the FROM clause.
The statement below returns the customers without Email where their sales (if any) are on and after January
01, 1965:
SELECT
C.FirstName Bind alias
, C.LastName
This subquery creates a recordset
, SUM(S.Quantity * S.Price) AS Sum_Sales
with the customers without Email
FROM
(
CustomerID FirstName LastName
SELECT
3 Beverly NULL
CustomerID 8 Zak Smith
166
DML Statements (Query)
Subqueries
, FirstName
, LastName
FROM dbo.Customers Data source for the subquery
WHERE Email IS NULL
) AS C Assign alias
LEFT JOIN dbo.Sales AS S
ON C.CustomerID = S.CustomerID
Filter in the ON clause (not in the WHERE clause) to
AND S.DateOfSale >= '1965-01-01'
avoid the conversion of the JOIN from LEFT to INNER
GROUP BY
C.FirstName
, C.LastName;
GO
FirstName LastName Sum_Sales
Beverly NULL 4249.32
The subquery is LEFT JOINed to the Sales
Zak Smith NULL
table to SELECT the customers with no sales.
When we LEFT JOIN, we filter the right data source in the ON clause.
! If we apply the filter in the WHERE clause, the JOIN is transformed to
LEFT JOIN on page 130
an INNER JOIN.
167
DML Statements (Query)
Subqueries
Correlated subquery
The correlated subquery is linked to the outer query. It correlates with objects in the FROM clause.
We can use correlated subquery in the SELECT, WHERE and HAVING clauses.
Let’s add one more table that stores the results of the marketing calls to the data sources, for the example:
168
DML Statements (Query)
Subqueries
We can query the MarketingCalls table using subqueries in the SELECT clause to SELECT the count and the duration
of the marketing calls for each customer:
SELECT
C.FirstName
, C.LastName
, (
SELECT AVG(Quantity * Price) Not correlated subquery
FROM dbo.Sales
) AS Avg_SalesAllCustomers
, SUM(S.Quantity * S.Price) AS Sum_Sales
, (
SELECT COUNT(MarketingCallID)
FROM dbo.MarketingCalls AS MC
WHERE MC.CustomerID = C.CustomerID
) AS Count_MarketingCalls Correlation (link to an ob-
, ( ject in the FROM clause)
SELECT SUM(CallDurationInMin)
FROM dbo.MarketingCalls AS MC
WHERE MC.CustomerID = C.CustomerID
) AS Sum_CallDurationInMin
FROM
dbo.Customers AS C
JOIN dbo.Sales AS S
ON C.CustomerID = S.CustomerID
GROUP BY
C.FirstName
, C.LastName
, C.CustomerID
ORDER BY
C.FirstName
, C.LastName; First- Last- Avg_SalesAll- Sum_ Count_Mar- Sum_CallDu-
Name Name Customers Sales ketingCalls rationInMin
GO
Anna Laurier 1205.132 7148.55 0 NULL
Beverly NULL 1205.132 4764.34 2 46
John Smith 1205.132 3.12 1 3
Melanie Larson 1205.132 45.88 0 NULL
Xavier Jameson 1205.132 89.43 2 14
169
DML Statements (Query)
Subqueries
To SELECT customers with total sales greater than 2000: Subquery with WHERE EXISTS
SELECT
On every row of the execution of the outer state-
C.FirstName
ment, CustomerID is linked and if the filter con-
, C.LastName
dition is met (total sales equal to or is greater
FROM dbo.Customers AS C than 2000), the customer is selected (it EXISTS)
WHERE
EXISTS
(
SELECT 1
FROM dbo.Sales AS S
WHERE C.CustomerID = S.CustomerID
GROUP BY CustomerID
HAVING SUM(S.Quantity * S.Price) >= 2000 FirstName LastName
); Anna Laurier
GO Beverly NULL
SELECT
C.FirstName
, C.LastName
FROM dbo.Customers AS C
WHERE
CustomerID IN Subquery with WHERE ColumnName IN
(
SELECT CustomerID
FROM dbo.Sales AS S
WHERE C.CustomerID = S.CustomerID
GROUP BY CustomerID
HAVING SUM(S.Quantity * S.Price) >= 2000 Filter
);
GO
FirstName LastName
Anna Laurier
Beverly NULL
170
DML Statements (Query)
Subqueries
To SELECT the total sales for the customers that have single sale equal to or greater than 150:
SELECT
C.FirstName
, C.LastName
, SUM(S1.Quantity * S1.Price) AS Sum_Sales
FROM
dbo.Customers AS C
JOIN dbo.Sales AS S1
ON C.CustomerID = S1.CustomerID
GROUP BY
C.CustomerID
, S1.CustomerID
, C.FirstName
, C.LastName
HAVING
S1.CustomerID IN
(
SELECT CustomerID
FROM dbo.Sales AS S2
WHERE
C.CustomerID = S2.CustomerID
AND (S2.Quantity * S2.Price) >= 150
FirstName LastName Sum_Sales
)
Anna Laurier 7148.55
GO
Beverly NULL 4764.34
171
DML Statements (Query)
UNION, EXCEPT and INTERSECT
UNION
UNION creates a recordset by appending the result from one statement to the result of another statement.
Statement 1 Customers
This is selected
Custom-
erCode
First- Last-
Statement 2 Email
Name Name
This is selected
Anabel Larson anabel.larson@customer5.info 34
SELECT Column1, Column2, Column3 Anna Laurier anna.laurier@customer2.net 863
FROM TableName1 Beverly NULL NULL 23
Statement 1 John Smith john.smith@customer1.com 943
UNION John Smith john.smith@customer3.org 72
Melanie Larson melanie.larson@customer4.biz 7565
SELECT Column1, Column2, Column3 Xavier Jameson xavier.garcía@customer6.com 4
FROM TableName2; Zak Smith NULL 425
GO Statement 2
Employees
Let’s say we have external and internal (employees)
Employ-
eeCode
customers. We may need to UNION both tables and First- Last-
Email
output the result to a report. Name Name
SELECT
Anabel Larson anabel.larson@customer5.info E1-465
FirstName
Anna Laurier anna.laurier@customer2.net 67 C22
, LastName
John Smith john.smith@customer1.com Fa 3 58B
FROM dbo.Customers
172
DML Statements (Query)
UNION, EXCEPT and INTERSECT
SELECT
Add identification column to mark
'Top Table' AS [Type]
the source of the data and identify
, FirstName it in future statement(s)
, LastName
FROM dbo.Customers
WHERE
LEFT(FirstName, 1) BETWEEN 'J' AND 'M' Type FirstName LastName
OR FirstName IN ('Anna', 'Anabel') Top Table Anabel Larson
Bottom Table Anabel Larson
UNION ALL Bottom Table Anna Laurier
Top Table Anna Laurier
SELECT Top Table John Smith
'Bottom Table' AS [Type] Top Table John Smith
, FirstName Bottom Table John Smith
, LastName Top Table Melanie Larson
173
DML Statements (Query)
UNION, EXCEPT and INTERSECT
FROM dbo.Employees
ORDER BY is at the end of the bottom statement and
ORDER BY
orders the UNIONed resulting recordset (not the last
FirstName statement).
, LastName;
GO
EXCEPT
We can create one statement that selects data and another statement to exclude the matching data from the first
statement. The combining keyword in this case is EXCEPT. Nothing from the bottom statement is selected. It just
determines which rows from the top statement are excluded.
If we don’t select column Email, the rows for John Smith are not unique anymore and are excluded:
174
DML Statements (Query)
UNION, EXCEPT and INTERSECT
SELECT
FirstName
, LastName
FROM dbo.Customers
FirstName LastName
EXCEPT Beverly NULL
Melanie Larson
SELECT Xavier Jameson
FirstName Zak Smith
, LastName
FROM dbo.CustomersWithExceptionalities;
GO
INTERSECT
To select the rows that exist in the both statements, we use INTERSECT.
}
Statement 1 Statement 2 Matching Customers
rows FirstName LastName Email
This is selected This is selected
Anabel Larson anabel.larson@customer5.info
This is not selected
Anna Laurier anna.laurier@customer2.net
Beverly NULL NULL
SELECT John Smith john.smith@customer1.com
FirstName John Smith john.smith@customer3.org
, Email Melanie Larson melanie.larson@customer4.biz
FROM dbo.Customers Xavier Jameson xavier.garcía@customer6.com
Zak Smith NULL
INTERSECT
CustomersWithExceptionalities
SELECT FirstName LastName Email
FirstName Anabel Larson anabel.larson@customer5.info
, Email Anna Laurier anna.laurier@customer2.net
FROM dbo.CustomersWithExceptionalities; John Smith john.smith@customer1.com
GO
FirstName Email
Rows, matching in Anabel anabel.larson@customer5.info
both recordsets
Anna anna.laurier@customer2.net
John john.smith@customer1.com
175
DML Statements (Query)
PIVOT and UNPIVOT
PIVOT
Pivot is the central point of rotation. When we pivot data, we rotate, reorganize and aggregate it.
When we pivot a table, we create a matrix from the table. The matrix allows us to show a value, belonging to two
groups of attributes.
The pivot table is also known as Tablix or Cross-Tab.
The pivoting is generally used for presentation of data, related to reporting and analysis purposes.
176
DML Statements (Query)
PIVOT and UNPIVOT
SELECT
P.CustomerID, P.ItemID Rows
, P.[1966-Q1], P.[1966-Q2], P.[1966-Q3] Columns
FROM In a subquery select only the columns
( for rows (CustomerID, ItemID), columns
SELECT DISTINCT CustomerID, ItemID, Period, SalesValue (Period) and values (SalesValue)
FROM dbo.Sales
) AS S
Pivoting the above data source (the sub-
PIVOT
query, aliased with S)
(
SUM(SalesValue) Values (Aggregated)
FOR Period IN ([1966-Q1], [1966-Q2], [1966-Q3])
Only these values are transformed into
) AS P
columns (PIVOTed)
ORDER BY CustomerID, ItemID;
GO
177
DML Statements (Query)
PIVOT and UNPIVOT
Custo-
* Aggregated
ItemID 1966-Q1 1966-Q2 1966-Q3
merID
1 1 62.23 * 81.09 81.82
2 1 39.37 86.92 NULL
2 2 93.12 NULL 40.55
2 3 NULL 18.37 NULL
3 1 12.36 NULL 52.66 *
3 2 NULL 13.33 154.88 *
3 3 NULL 114.11 NULL
After we PIVOT the table, we can easily compare the sales in the each period side by side.
UNPIVOT
UNPIVOT is the opposite of PIVOT. It transforms the data from columns to rows. In the example below,
we use the table that we just PIVOTed.
SELECT
Subquery to select only the
CustomerID
data that we need to UNPIVOT
, ItemID
, Period
, SaleValue The UNPIVOTed result is:
FROM
( Custo- Sales-
ItemID Period
merID Value
SELECT
1 1 1966-Q1 62.23 *
CustomerID, ItemID
1 1 1966-Q2 81.09
, [1966-Q1], [1966-Q2], [1966-Q3]
1 1 1966-Q3 81.82
FROM dbo.Sales
2 1 1966-Q1 39.37
) AS P
2 1 1966-Q2 86.92
UNPIVOT
2 2 1966-Q1 93.12
(
2 2 1966-Q3 40.55
SaleValue
2 3 1966-Q2 18.37
FOR Period IN ([1966-Q1], [1966-Q2], [1966-Q3])
3 1 1966-Q1 12.36
) AS U
3 1 1966-Q3 52.66 *
ORDER BY
3 2 1966-Q2 13.33
CustomerID
Combine multiple columns 3 2 1966-Q3 154.88 *
, ItemID
into a new column (Period) 3 3 1966-Q2 114.11
, Period;
GO
* Can’t undo the aggregation
! UNPIVOT is not able to fully reverse the data as it was before the PIVOTing. We can’t undo the aggregation.
178
Conditional Execution
IF
IF... ELSE are keywords of the Control-of-Flow Language which manipulates the execution of the code. T-SQL is
executed from top to bottom. Based on a specified condition, we can skip the execution of a block of code.
Execution steps:
1. The IF condition is verified
2. If the verification returns True, the block of code between BEGIN and END keywords that follows is
executed
3. ELSE keyword adds another block of code that is executed when the IF condition returns False.
The ELSE statement is optional
Always use BEGIN and END, regardless if the block contains one or more than one statements.
IF (7 > 17) The condition is not met (False) and the block of T-SQL that follows is not executed
BEGIN
PRINT 'This BEGIN... END block is executed when condition is True';
PRINT '7 is greater that 17';
END
GO
IF... ELSE
The block of code, defined by ELSE is executed if the verification of the condition returns False.
IF (DATENAME(DW, GETDATE()) = 'Saturday') GETDATE returns the date and time now
BEGIN PRINT 'Today is Saturday'; END DATENAME returns the name of the day in the week
ELSE
Formating the code
If one statement then BEGIN and END on the same line
179
Conditional Execution
IF
Nested IF
We can verify one condition in case when another condition is met by nesting one IF condition into another.
In this example if the condition is verified to False, ELSE, including nested IF is executed:
IF (7 > 17)
BEGIN PRINT '(7 > 17) is True'; END
ELSE
BEGIN
IF (8 > 17)
BEGIN PRINT '(8 > 17) is True'; END
ELSE
BEGIN PRINT '(8 > 17) is False'; END
END
GO
Independent IFs
180
Conditional Execution
IF
IF EXISTS
The statement in the condition creates a recordset. If a row exists in the recordset, the verification of the condition
returns True.
Customers
FirstName LastName Email
Anabel Larson anabel.larson@customer5.info
Anna Laurier anna.laurier@customer2.net
Beverly NULL NULL
IF EXISTS The reason to select 1 is because We don’t need any actual val-
( ue. We just need to know if a row exists in the recordset
SELECT 1
FROM dbo.Customers
Result
WHERE Email = 'anna.laurier@customer3.net'
anna.laurier@customer3.net
) does not EXISTS
BEGIN SELECT 'anna.laurier@customer3.net EXISTS' AS Result; END
ELSE
BEGIN SELECT 'anna.laurier@customer3.net does not EXISTS' AS Result; END
GO
RETURN
Terminates the execution of the code (the rest of the code after RETURN is not executed).
IF (7 > 17)
BEGIN SELECT 'True' AS Result; END
ELSE
BEGIN
SELECT 'False' AS Result;
RETURN; The execution of the code is terminated here
END
SELECT 'This is not executed, because RETURN is reached'; The code after RETURN is not executed
GO
Result
False
181
Conditional Execution
IIF
1. Table Sales contains the sales by customer and item. Column IsItemDiscounted determines how we
manipulate the data and how we JOIN the tables
2. Table ItemsStandarsDiscounted is intermediate and links the standard item (the ones that we usually
sell) to the discounted item (the same item with reduced price for one or another reason)
3. Table Items contains the attributes for the items. For the purposes of this example we need only Price
We can’t implement the IF condition in DML statement’s clause (SELECT, FROM, WHERE, GROUP BY, ORDER BY) direct-
ly:
SELECT
S.CustomerID
, S.ItemID
, S.IsItemDiscounted
IF condition
, S.Quantity
, I.Price
, IF (S.IsItemDiscounted = 0)
BEGIN (S.Quantity * I.Price) END Incorrect syntax near the keyword 'IF'.
ELSE Incorrect syntax near 'S'.
BEGIN (S.Quantity * (I.Price * 0.85)) END Incorrect syntax near 'S'.
AS SalesValue
FROM
dbo.Sales AS S
JOIN dbo.Items AS I To accomplish this task, we need to use the
ON S.ItemID = I.ItemID; built-in function IIF
GO The syntax is:
182
Conditional Execution
IIF
The IIF condition verifies if the item is discounted and calculates the standard or the discounted (85%) sales value:
SELECT
S.CustomerID
, S.ItemID
, S.IsItemDiscounted
, S.Quantity
, I.Price
, IIF((S.IsItemDiscounted = 0), (S.Quantity * I.Price), (S.Quantity * (I.Price * 0.85))) AS
SalesValue
FROM
dbo.Sales AS S
JOIN dbo.Items AS I
ON S.ItemID = I.ItemID;
GO
The conditional executions in the FROM... JOIN (ON) clause gives us the chance to create a zig-zag JOIN.
This means that, based on condition, we link to one or another columns.
SELECT
S.CustomerID
, S.ItemID
, S.IsItemDiscounted
183
Conditional Execution
IIF
, S.Quantity
, ISD.ItemID
, ISD.DiscountedItemID
, I.ItemID
, I.Price
, (S.Quantity * I.Price) AS SavesValue
FROM
dbo.Sales AS S
JOIN dbo.ItemsStandardDiscounted AS ISD
ON S.ItemID = ISD.ItemID Zig-zag JOINing
JOIN dbo.Items AS I
ON IIF((S.IsItemDiscounted = 0), ISD.ItemID, ISD.DiscountedItemID) = I.ItemID;
GO
In the first row ItemID = 11 is linked to ItemID = 11. Hense IsItemDiscounted is 1, DiscountedItemID = 111 is
linked to ItemID = 111 and the price of ItemID = 111 (4.99) is used in the calculation (52 * 4.99 = 259.48)
In the last row ItemID = 11 is linked to ItemID = 11. Hense IsItemDiscounted is 0, ItemID = 11 is linked to ItemID
= 11 and the price of ItemID = 11 (5.23) is used in the calculation (1 * 5.23 = 5.23)
We can filter on the calculated column SalesValue and select the rows where SalesValue is greater than 200:
SELECT AS SalesValue
S.CustomerID FROM
, S.ItemID dbo.Sales AS S
, S.IsItemDiscounted JOIN dbo.Items AS I
, S.Quantity ON S.ItemID = I.ItemID
, I.Price WHERE IIF((S.IsItemDiscounted = 0), (S.Quantity *
, IIF((S.IsItemDiscounted = 0), (S.Quantity I.Price), (S.Quantity * (I.Price * 0.85))) > 200;
* I.Price), (S.Quantity * (I.Price * 0.85))) GO
184
Conditional Execution
IIF
1 11 1 52 5.23 231.166000
3 11 1 62 5.23 275.621000
1 22 0 552 15.88 8765.760000 (552 * 15.88) = 8765.76
SELECT
IIF((S.IsItemDiscounted = 0), ISD.ItemID, ISD.DiscountedItemID) AS ItemID
, COUNT(DISTINCT CustomerID) AS Count_Customers
FROM
dbo.Sales AS S
JOIN dbo.ItemsStandardDiscounted AS ISD
ON S.ItemID = ISD.ItemID
The same as in the
GROUP BY IIF((S.IsItemDiscounted = 0), ISD.ItemID, ISD.DiscountedItemID);
SELECT clause
GO
, S.ItemID
, SUM(IIF((S.IsItemDiscounted = 0), (S.Quantity *
I.Price), (S.Quantity * (I.Price * 0.85)))) AS SalesValue
FROM
dbo.Sales AS S
JOIN dbo.Items AS I
ON S.ItemID = I.ItemID
GROUP BY
S.CustomerID
, S.ItemID CustomerID ItemID SalesValue
HAVING SUM(IIF((S.IsItemDiscounted = 0), (S.Quantity * 1 22 8765.760000
I.Price), (S.Quantity * (I.Price * 0.85)))) > 250; 3 11 275.621000
GO
185
Conditional Execution
CASE
The CASE expression is an extension of the logic of the IF condition. CASE combines multiple conditions into one.
Structure
• CASE and END distinct the CASE expression from the rest of the code
• WHEN defines the conditions to be verified
• THEN defines the code that is executed if the WHEN condition evaluates to True
• ELSE defines the code that is executed if none of the WHEN conditions evaluates to True
Execution
1. The WHEN conditions are verified in the order that they appear
2. When the condition verification returns False, the execution jumps to the next condition
3. When the condition’s verification is True, the code defined with the keyword THEN is executed and the
execution of the CASE terminates
4. If none of the conditions validates to True, the code defined with the keyword ELSE is executed
We can write the CASE expression in two variations: simple and complex.
We can calculate the discount, based on the boolean value in HasDiscount column with simple CASE expression:
186
Conditional Execution
CASE
When we use the complex variation of the CASE expression, we can involve one or more condition like:
WHEN ('A' = 'A' AND (13 > 7 OR ColumnName = 'Y')) THEN...
SELECT
CustomerName
Both operands and the operator are after WHEN
, ItemDescription
, YEAR(SaleDate) AS Year_SaleDate
, CASE
WHEN YEAR(SaleDate) = @CurrentYear THEN 'Current Year' @CurrentYear = 1969
WHEN YEAR(SaleDate) = (@CurrentYear - 1) THEN 'Last Year'
ELSE 'Older than last year'
END AS Case_Year_SaleDate
, SUM(Quantity * Price) AS SalesValue
FROM dbo.Sales
GROUP BY
CustomerName
, ItemDescription
, YEAR(SaleDate)
, CASE
WHEN YEAR(SaleDate) = @CurrentYear THEN 'Current Year'
WHEN YEAR(SaleDate) = (@CurrentYear - 1) THEN 'Last Year'
ELSE 'Older than last year'
END
ORDER BY The same as in the SELECT clause
CustomerName
, ItemDescription;
GO
187
Conditional Execution
CASE
We can use CASE in the FROM clause when we need to equalize values in the ON clause.
A simple example is joining two data sources where values in Table1 are 0 and 1, but the corresponding values in
Table2 are 'Y' and 'N':
...
FROM
Table1 AS T1
JOIN Table1 AS T2
ON CASE
WHEN T1.HasDiscount = 1 THEN 'Y'
ELSE 'N'
END = T2.HasDiscount
...
Let’s add one more table that contains the thresholds, related to the marketing strategies:
The items are on the rows and three groups are in the columns. We generated these groups in the last example.
Let’s move the last example into a subquery and join the new table with the subquery:
SELECT
S.CustomerName
, S.ItemDescription
188
Conditional Execution
CASE
, S.Year_SaleDate
, S.Case_Year_SaleDate
, S.SalesValue
, ID.CurrentYear
, ID.LastYear Subquery (from the previous exam-
, ID.OlderThanLastYear ple) to create the sales data
FROM
(
Anabel Larson 3 Wooden Horse 1965 Older than last year 3837.68
John Smith 1 Plastic Plain Blue 1965 Older than last year 20.58
John Smith 2 Rag Doll 33 cm. 1966 Older than last year 398.56
Xavier Jameson 2 Rag Doll 33 cm. 1966 Older than last year 4244.24
) AS S
JOIN dbo.ItemDiscounts AS ID
ON S.ItemID = ID.ItemID
WHERE
S.SalesValue >=
CASE
WHEN S.Case_Year_SaleDate = 'Current Year' THEN ID.CurrentYear
WHEN S.Case_Year_SaleDate = 'Last Year' THEN ID.LastYear
ELSE ID.OlderThanLastYear
END
ORDER BY Zig-zag filtering
S.CustomerName
, S.ItemDescription;
GO
We can use the same zig-zag logic in the FROM (ON) clause to JOIN on different columns, based on a condition.
189
Conditional Execution
CASE
When we use CASE in the GROUP BY clause, it has to be the same as the corresponding CASE in the SELECT clause.
This is not mandatory. We may have a logic that SELECTs and GROUPs BY differently.
In the below example, we used CASE to add a calculated column (Case_Discount) to explain the simple CASE ex-
pression.
We can filter the aggregated virtual recordset on this column by adding CASE expression in the HAVING clause:
SELECT
CustomerName
, ItemDescription
, HasDiscount
, SUM(Quantity * Price) AS SalesValue
, CASE HasDiscount
WHEN 1 THEN (SUM(Quantity * Price) * 0.85)
ELSE SUM(Quantity * Price)
END AS Case_Discount
FROM dbo.Sales
GROUP BY
CustomerName
, ItemDescription
, HasDiscount
HAVING
CASE HasDiscount
WHEN 1 THEN (SUM(Quantity * Price) * 0.85)
ELSE SUM(Quantity * Price)
END <= 500
ORDER BY
CustomerName CustomerName ItemDescription HasDiscount SalesValue Case_Discount
, ItemDescription; Anabel Larson Plastic Plain Blue 0 14.67 14.670000
GO John Smith Plastic Plain Blue 0 20.58 20.580000
John Smith Rag Doll 33 cm. 0 398.56 398.560000
Xavier Jameson Plastic Plain Blue 1 5.08 4.318000
Xavier Jameson Wooden Horse 1 515.02 437.767000
We can order the resultset by moving the rows with discount at the top:
190
Conditional Execution
CASE
SELECT
CustomerName
, ItemDescription
, HasDiscount
, SUM(Quantity * Price) AS SalesValue
, CASE HasDiscount
WHEN 1 THEN (SUM(Quantity * Price) *
0.85)
ELSE SUM(Quantity * Price)
END AS Case_Discount
FROM dbo.Sales
GROUP BY
CustomerName
, ItemDescription
, HasDiscount
ORDER BY
CASE HasDiscount
WHEN 1 THEN 0 The sales that have discount are marked with 0
ELSE 1
END
All the other rows are marked with 1 and are
, CustomerName
ordered after the discounted sales
, ItemDescription;
GO
Customer- ItemDescrip-
HasDiscount SalesValue Case_Discount
Name tion
}
Anabel Larson Wooden Horse 1 3837.68 3262.028000
Xavier Jameson Plastic Plain Blue 1 5.08 4.318000
HasDiscount = 1
Xavier Jameson Rag Doll 33 cm. 1 4244.24 3607.604000
Xavier Jameson Wooden Horse 1 515.02 437.767000
}
Anabel Larson Plastic Plain Blue 0 14.67 14.670000
John Smith Plastic Plain Blue 0 20.58 20.580000 HasDiscount = 0
John Smith Rag Doll 33 cm. 0 398.56 398.560000
191
Sessions
In a real life scenario we may have a parametrized stored procedure (SP) that creates the data for a report.
The steps in the SP are:
1. Extract the data for the chosen fiscal period (parameter)
2. Insert the data into a new table
3. Manipulate the new table
4. Extract the final data for the report from the new table
5. Delete the new table
If multiple users execute the same SP simultaneously, there is a risk that they manipulate each others data.
To isolate the data for each user, SQL Server® associates a session for each user’s execution.
192
Sessions
• All the commands that run the same Stored Procedure, create their own Isolated Data (real or temp table,
variable, etc.)
• Unique resultset is returned to each User
The built-in function @@SPID returns the SessionID of the current session:
Session in SSMS
We can test different statements in different sessions by executing them in different tabs in SSMS:
193
Tables
Permanent Table
The permanent table is stored on the hard drive(s) of the server and exists permanently until it is dropped with the
DDL DROP TABLE statement.
Query the Object Catalog and list the permanent tables in a database:
The temporary existence and the isolation are the reasons why we use temp tables.
194
Tables
After the local or global temp table is created, we can manipulate it with the DDL and DML statements that we
learned so far.
Once we no longer need the temp table, we run the clean up code to release the disk space that was used by the
temp table.
As the table variable is created in the memory and is alive at the time of the execution of the batch, it is not nec-
essary to handle the clean up manually.
195
Tables
! We can’t use the SELECT... INTO statement to load data into table variable.
SELECT Schema-
TableName DateCreated
SCHEMA_NAME([schema_id]) AS SchemaName Name
, name AS TableName dbo ##GlobalTempTable1 1966-04-18
11:13:41.870
, create_date AS DateCreated
dbo #LocalTempTable1__...___000000000002 1966-04-18
FROM tempdb.sys.tables
12:05:05.097
ORDER BY
SchemaName
, TableName;
GO
196
Tables
The local temp table is local, because it is accessible only by the session that creates it.
SELECT *
TableType
FROM #LocalTempTable;
Local
GO
SELECT *
FROM ##GlobalTempTable; TableType
GO Global
197
Tables
SELECT * Step 2: Execute in any tab other than the first in SSMS
FROM ##GlobalTempTable;
GO Invalid object name '##GlobalTempTable'.
Table variable
DECLARE @TableVariable1...
INSERT @TableVariable1...
GO GO ends the batch and the table variable doesn’t exist anymore
SELECT *
FROM @TableVariable1; The table variable doesn’t exist anymore
GO
Must declare the table variable “@TableVariable1”.
198
Variables
The variable:
• Stored in the memory of the server is:
• Single value of specific data type
• Multiple values of multiple data types (table variable)
• Exists at the time of the execution of the batch where it is created
• Is accessible only inside the batch where it is created
• Has unique name, starting with the at symbol (@)
Before
Computer memory
Create
Computer memory
Assign
Computer memory
199
Variables
Use
Computer memory
Computer memory
Operating System
The variable is not in the memory anymore
Other
Variable name
200
Variables
! We are not allowed to wrap the variable name in square brackets ([]) in order to use the characters above.
The data type that a variable can hold which is any SQL Server® data type, except TEXT, NTEXT and IMAGE (these
data types are obsolete and not suggested to be used).
! TEXT, NTEXT and IMAGE data types are obsolete and suggested not to be used.
201
Variables
• = • *=
• += • /=
• -= • %=
• Else, we manually convert the assigned value to the data type of the variable
Assign value
The SELECT statement can assign values to more than one variable:
SELECT
@VariableNumeric = 456
, @VariableString = 'DEF';
202
Variables
When we assign a value from the recordset, we’ll assign it as many times as the number of the rows in the
recordset.
The last assigned value is the last queried value of the recordset.
Assign:
• Constant value 123 to @VariableNumeric
• The value of ColumnString from Variables to @VariableString:
SELECT
@VariableNumeric = 123 Constant value
, @VariableString = ColumnString Value, obtained from a recordset (Data-
FROM dbo.Variables; baseName.SchemaName.TableName)
GO
Value 123 will be assigned 7 times to @VariableNumeric, because there are 7 rows in table Variables.
On every row, returned by the SELECT statement, the value of the column ColumnString is assigned to @
VariableString.
The last value of column ColumnString 'G' is assigned.
To avoid any additional load of the server, we need to make sure that the recordset returns one row:
SELECT
@VariableNumeric = MIN(ColumnNumeric)
, @VariableString = MAX(ColumnString)
FROM dbo.Variables;
GO
Now the recordset contains 1 (the minimum value in column ColumnNumeric) and 'G' (the maximum value in
column ColumnString).
Assigning a value from the multi-row recordset with the SET statement returns an error:
203
Variables
DECLARE
@CommissionPercentage DECIMAL(5, 3) Remove the time from the DATE-
, @Today DATE = CONVERT(CHAR(10), GETDATE(), 120); TIME data type, returned by the
built-in function GETDATE()
SELECT @CommissionPercentage = CommisionPercentage Assign a value to the variable(s)
FROM dbo.CommissionPercentage
WHERE DateFiscal = @Today;
204
Variables
Table variable
In the example below, we (INNER) JOIN table variable to filter the recordset.
INSERT @TableVariable
( Assign values (INSERT)
FiscalYear
, FiscalMonth FiscalYear FiscalMonth
) 1967 11
205
Variables
@VariableNumeric is created in the first batch and can’t be used in the next batch.
There are system functions, with names starting with 2 at symbols (@@). They are not variables.
! SELECT
@@VERSION AS [Database Version]
, @@SPID AS [Session ID]
, @@SERVERNAME AS [Server Name]
, @@LANGUAGE AS [Language];
GO
206
Loops
WHILE
WHILE Loops
The WHILE statement is a loop i.e. a block of statement(s) in which the execution is repeated as long as the verifica-
tion of condition returns True.
DECLARE Variables
@Counter TINYINT = 1 Counts the loops
, @LoopedValue INT = 1; Stores and prints in the looped value
207
Loops
WHILE
When the condition is verified for the first time, the value of both variables is 1 and the verification of the condition
returns True (1 is less or equal to 5).
Inside the BEGIN... END block the value of @LoopedValue (1) is printed and the values of both variables are increased
by 1. Now the value of the both variables is 2.
The condition is verified again and returns True (2 is less or equal to 5)
The value of @LoopedValue (2) is printed and the values of both variables are increased by 1. Now the value of the
both variables is 3.
…
The condition is verified again and returns False (6 is not less or equal to 5)
The execution of the WHILE loop is terminated.
To print the alphabet, we use the built-in function CHAR() that converts an integer value to its corresponding ASCII
code.
The same variable @LoopedValue is used for data manipulation and counting purposes:
SET @LoopedValue += 1;
END ...
GO
Example: The condition can not be met after the execution is inside the BEGIN... END block (Infinite Loop)
DECLARE @Counter INT = 1 Starting from 1 and decreasing @Counter by 1 (1, 0, -1, -2...)), the condition is
always True. (the value of @Counter is always less than 50)
WHILE (@Counter <= 50)
BEGIN The condition will never be met and the WHILE loop will be repeated until the
PRINT @Counter; execution time-out is reached.
To change the time-out in SSMS only for the current tab, right-click in the Query
SELECT @Counter -= 1; Window Query Options… Execution General Execution time-
END out:
GO
! As this statement is an infinite loop, cancel it as soon as possible.
208
Loops
WHILE
When we change the query time-out and execute again with the same WHILE statement, after 1 second an error
message is returned:
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not
responding.
CONTINUE and BREAK control the execution inside the BEGIN... END block. They are used together with another con-
dition.
To stop the looping when a certain condition is met, we can use the IF statement inside the BEGIN... END block:
DECLARE
@MaxID INT Select the maximum value of
column ID (4) into variable WHILE1
, @ID INT = 1;
ID Column1 Column2
1 100 A
SELECT @MaxID = MAX(ID)
2 101 B
FROM dbo.WHILE1;
3 102 C
4 103 D
209
Loops
WHILE
SELECT @ID += 1; Transfer the data for the row where the ID
matches the current @ID into the temp table
CONTINUE;
END
ELSE If @ID is not less or equal to 2, BREAK the execution
BEGIN BREAK; END
END
#WHILEResult1
SELECT * Result
SELECT the data 100 - A
FROM #WHILEResult1;
from the temp table
GO 101 - B
210
Common Table Expressions
(CTE)
Common Table Expression (CTE) is a virtual recordset (VR), used in the subsequent DML statement.
1. CTE
Customers
CustomerID FirstName LastName Email DateTimeRegistered IsActive
1 Anabel Larson anabel.larson@customer5.info 1968-07-02 03:22:20.133 1
2 Anna Laurier anna.laurier@customer2.net 1969-12-14 19:24:49.527 NULL
3 Beverly NULL NULL 1969-06-02 22:09:48.943 NULL
4 John Smith john.smith@customer1.com 1968-04-12 06:16:25.337 1
5 John Smith john.smith@customer3.org 1963-04-11 01:55:38.420 NULL
6 Melanie Larson melanie.larson@customer4.biz 1965-11-18 16:34:48.753 1
7 Xavier Jameson xavier.garcía@customer6.com 1961-12-11 01:49:52.020 1
8 Zak Smith NULL 1967-12-03 06:15:50.117 NULL
211
Common Table Expressions
(CTE)
Sales
SaleID CustomerID ItemID DateTimeOfSale Quantity Price
1 3 2043 1969-09-05 12:47:05.467 4 1.27
2 4 5164 1969-12-18 02:15:36.470 2 1.56
3 3 5293 1969-11-10 20:18:49.610 77 55.12
4 5 6223 1964-01-10 21:13:18.150 3 4.89
5 7 1352 1961-12-12 02:28:34.363 89 0.84
6 2 1953 1969-12-22 07:39:41.243 53 2.84
7 3 8613 1969-12-24 18:34:13.777 22 23.41
8 2 5523 1969-12-27 16:43:01.750 55 127.19
9 1 8534 1969-12-25 08:33:58.513 1 45.88
10 2 9375 1969-12-15 09:39:11.403 6 0.43
As the CTE is a recordset, when we use it in the SELECT clause, we put it in a subquery, that has to return a single
value (one row and one column):
CTE name and aliases (CID, IA), created explicitly and
WITH CTE_SELECT (CID, IA) AS in the same order as the columns in the CTE definition
(
SELECT CTE definition
CTE_SELECT
CustomerID
CID IA
, 'Yes'
1 Yes
FROM dbo.Customers
4 Yes
WHERE IsActive = 1
6 Yes
)
7 Yes
SELECT
S.CustomerID
, C.FirstName
, C.LastName
, (SELECT IA FROM CTE_SELECT WHERE CID = S.CustomerID) AS IsActive
, MAX(S.DateTimeOfSale) AS Max_DateOfSale
, SUM(S.Quantity * S.Price) AS SalesValue
FROM
dbo.Sales AS S
Subquery to add the status
JOIN dbo.Customers AS C of the Customers
ON S.CustomerID = C.CustomerID
GROUP BY
S.CustomerID
, C.FirstName
, C.LastName;
GO
212
Common Table Expressions
(CTE)
We can use a CTE in the FROM clause the same way we use any other recordset (data source).
The CTE creates a VR with data for the last 3 registered customers. The next statement is using the CTE to SELECT
the sales for these customers:
CTE name. Aliases are not specified here and the column
WITH CTE_FROM AS names as they are defined in the CTE definition are used
(
SELECT TOP 3 CustomerID, FirstName, LastName, DateTimeRegistered AS DTR
FROM dbo.Customers
ORDER BY DateTimeRegistered DESC
)
Custo-
merID
First- Last-
SELECT DTR
Name Name
C.CustomerID
, C.FirstName 2 Anna Laurier 1969-12-14 19:24:49.527
, C.LastName 3 Beverly NULL 1969-06-02 22:09:48.943
, C.DTR AS DateTimeRegistered 1 Anabel Larson 1968-07-02 03:22:20.133
, MAX(S.DateTimeOfSale) AS Max_DateTimeOfSale
, SUM(S.Quantity * S.Price) AS SalesValue
FROM
dbo.Sales AS S
JOIN CTE_FROM AS C
ON S.CustomerID = C.CustomerID
GROUP BY
C.CustomerID
, C.FirstName
, C.LastName
, C.DTR
ORDER BY If more than one customer bought in the
MAX(S.DateTimeOfSale) DESC same day, sort by SalesValue descending
, SUM(S.Quantity * S.Price) DESC;
GO
213
Common Table Expressions
(CTE)
FROM dbo.Customers 3
)
SELECT
C.CustomerID
, C.FirstName
, C.LastName
, MAX(S.DateTimeOfSale) AS Max_DateTimeOfSale
, SUM(S.Quantity * S.Price) AS SalesValue
FROM
dbo.Sales AS S
JOIN dbo.Customers AS C
ON S.CustomerID = C.CustomerID
WHERE
C.CustomerID IN
(
SELECT CustomerID
FROM CTE_WHERE
)
GROUP BY
C.CustomerID
, C.FirstName
, C.LastName
ORDER BY
MAX(S.DateTimeOfSale) DESC
, SUM(S.Quantity * S.Price) DESC;
Custo-
merID
First- Sales-
GO LastName Max_DateTimeOfSale
Name Value
214
Common Table Expressions
(CTE)
We can split multiple CTEs with comma (,) and use them in the subsequent DML statement.
215
Common Table Expressions
(CTE)
Recursive CTE
Recursive CTE is one that calls itself. In the example below we build a parent-child relation with recursive CTE.
GroupsParentChild
ParentID ChildID ParentToChild ChildToParent
NULL 3 Parent None - Child 3 Child 3 - Parent None
NULL 7 Parent None - Child 7 Child 7 - Parent None
1 8 Parent 1 - Child 8 Child 8 - Parent 1
2 4 Parent 2 - Child 4 Child 4 - Parent 2
3 1 Parent 3 - Child 1 Child 1 - Parent 3
3 2 Parent 3 - Child 2 Child 2 - Parent 3
6 9 Parent 6 - Child 9 Child 9 - Parent 6
7 5 Parent 7 - Child 5 Child 5 - Parent 7
7 6 Parent 7 - Child 6 Child 6 - Parent 7
8 10 Parent 8 - Child 10 Child 10 - Parent 8
Every child belongs to a parent. The child which parent is NULL is the top level. We can show the dependencies in 2
directions:
• Parent to child
• Child to parent
As we don’t know how many levels we have, we need to build the dependency list of the parent-child hierarchy
dynamically:
'A' AS SourceStatement
ParentToChild
, 0 AS [Level]
ParentID
ChildID
Level
, ParentID
, ChildID
, ParentToChild
FROM dbo.GroupsParentChild
A 0 NULL 3 Parent None - Child 3
WHERE ParentID IS NULL
A 0 NULL 7 Parent None - Child 7
NULL is the top level
216
Common Table Expressions
(CTE)
UNION ALL UNION ALL combines the anchor and the recursive statement(s)
ParentID
SourceS-
ChildID
Level
Values 10 and 4 don’t exist in column ParentID and the recursions are A - Anchor
terminated. The deepest level (3) is reached and the execution is finished. R - Recursive
217
Common Table Expressions
(CTE)
Child to parent
We can build the parent-child relation in the opposite direction – from child to parent:
DECLARE @ChildID INT = 10;
WITH CTE_Recursive AS
(
SELECT
'A' AS SourceStatement
, 0 AS [Level]
, ChildToParent
, ChildID
, ParentID
FROM dbo.GroupsParentChild
WHERE ChildID = @ChildID Start from the child
UNION ALL
SELECT
'R' AS SourceStatement
, (C.[Level] + 1) AS [Level]
, GPC.ChildToParent
, GPC.ChildID
, GPC.ParentID
FROM dbo.GroupsParentChild AS GPC
JOIN CTE_Recursive AS C
ON C.ParentID = GPC.ChildID Reverse the link logic
)
SELECT *
FROM CTE_Recursive;
GO
The recursion No. 3 reaches the top level (ParentID = NULL) and the execution terminates.
Changing the value of the variable @ChildID to 9, selects the relation for ChildID = 9 (9 6 7)
218
Common Table Expressions
(CTE)
As the deepest level in the last example is 2, this statement is executed successfully:
WITH CTE_Recursive...
SELECT *
FROM CTE_Recursive
OPTION (MAXRECURSION 2);
GO
If we change the value of MAXRECURSION to 1, an error message “The statement terminated. The maximum
recursion 1 has been exhausted before statement completion.” is returned.
CTE Scope
The CTE can be used only by the DML statement that is after the WITH clause. If the statement that follows is using
the CTE, an error message “Invalid object name 'CTE_Name'.“ is returned.
! The statement preceding the CTE (the WITH clause) must end with semicolon (;)
219
Views
Customers
Custo- First- Last-
Email
merID Name Name
1 Anabel Larson anabel.larson@customer5.info
vw_Customers
2 Anna Laurier anna.laurier@customer2.net
3 Beverly NULL NULL
4 John Smith john.smith@customer1.com
5 John Smith john.smith@customer3.org
220
Views
We SELECT the data that the View provides like any other data source (table, table variable, table-valued function
etc.)
Customers
Custo- First- Last-
Email
merID Name Name
1 Anabel Larson anabel.larson@customer5.info
2 Anna Laurier anna.laurier@customer2.net vw_Customers
3 Beverly NULL NULL
4 John Smith john.smith@customer1.com
5 John Smith john.smith@customer3.org
SELECT * vw_Customers
Customers
Custo- First- Last-
Email
merID Name Name
1 Anabel Larson anabel.larson@customer5.info
2 Anna Laurier anna.laurier@customer2.net vw_Customers
3 Beverly NULL NULL
4 John Smith john.smith@customer1.com
5 John Smith john.smith@customer3.org
221
Views
• JOIN DB objects
Customers Sales
Custo- First- Last-
Quantity
Email
ItemID
SaleID
Custo-
merID
merID Name Name
Price
DateOfSale
1 Anabel Larson anabel.larson@customer5.info
2 Anna Laurier anna.laurier@customer2.net
1 3 1004 1967-10-17 21:03:40.813 3 1.24
3 Beverly NULL NULL
2 1 5273 1968-05-08 04:07:40.647 5 2.43
4 John Smith john.smith@customer1.com
3 4 5432 1965-09-04 22:51:01.710 2 12.44
5 John Smith john.smith@customer3.org
4 3 8541 1968-12-17 07:20:54.940 7 5.88
1 5 5 9514 1968-03-04 10:06:34.767 1 28.19
vw_Sales
LineTo-
Custo-
merID
Name
Name
First-
Last-
Price
ON C.CustomerID = S.CustomerID
tal
DateOfSale
WHERE
YEAR(S.DateOfSale) = 1968 5 John Smith 1968-03-04 1 28.19 28.19
AND MONTH(S.DateOfSale) = 03; 10:06:34.767
222
Views
FROM vw_Sales
The column Discount in table Customers presents the discount in percent and it is used in the calculation of the
LineTotal.
Customers Sales
Quantity
Custo-
merID
ItemID
SaleID
Custo-
First- Last-
merID
Price
Email Discount DateOfSale
Name Name
vw_SalesWithDiscounts
223
Views
vw_SalesWithDiscounts
Custome- Customer- LineTotalDis-
DateOfSale Quantity Price Discount LineTotal
rID Name count
3 Beverly 1967-10-17 21:03:40.813 3 1.24 0.217 3.72 2.9127600
1 Anabel Larson 1968-05-08 04:07:40.647 5 2.43 0.111 12.15 10.8013500
4 John Smith 1965-09-04 22:51:01.710 2 12.44 0.068 24.88 23.1881600
3 Beverly 1968-12-17 07:20:54.940 7 5.88 0.217 41.16 32.2282800
5 John Smith 1968-03-04 10:06:34.767 1 28.19 0.000 28.19 28.1900000
Employees
EmployeeID FirstName LastName DepartmentID Salary User1 User2
1 Adam Short 2 5000
2 Jeremy Scott 1 15000
3 Anna Branton 3 2000
User3
An error message “The server principal “User4” is not able to access the database “LearnSQLServerIntui-
tively” under the current security context.” is returned to the users that are not permitted to manipulate the
View.
Advantages (Pros):
• Facilitate the correlations between the business logic and the data
• Facilitate the DB development by “masking” complex code
• Apply security rules
• Saves space that should be used if we store the result of a view in a table
• We can edit the code of the view without the need to edit the code of the dependent DB object(s)
Disadvantages (Cons):
• When we use the view as a table, we add the complexity of the view to the code. This can lead to bad
performance
224
Views
CREATE VIEW
The DDL statement CREATE VIEW is wrapping the definition of the View (the SELECT statement that builds the
virtual recordset that the view SELECTs):
225
Views
ALTER VIEW
SELECT *
FROM sys.views;
GO
SELECT *
FROM sys.objects
WHERE [type] = 'V';
GO
226
Functions
The function:
• Is a DB object, stored in the DBE
• Is encapsulated and reusable code that extends the functionality of T-SQL
• Is created by us (User-defined functions) or installed together with SQL Server®
(Built-in and System functions)
• Requires or does not require input parameter(s) (parametrized function) with or without default values
• Outputs single value (scalar-valued function) or recordset (table-valued function)
• Returns the same (deterministic function) or different (non deterministic function) value(s) on every
execution
We can define a calculation that is often used, encapsulate it in a function and call it when needed.
udf__CalculateRebate
Input Output
(Function, data manipulation)
RebatePercent = 0.8 (5324.73 * 0.8) 4,259.784
SalesValue = 5324.73
Input Parameters
When we create a function, we define if:
• It requires input parameter(s) or not
• A function that removes the time from DATETIME data type (1963-04-17 18:23:45.517 to 1963-04-17
227
Functions
Output
The function returns a result, which type define the function as:
• Scalar-valued – returns single value
• Inline Table-valued – returns multiple values (table), created with a single statement
• Multi-statement Table-valued – returns multiple values (table), created with multiple statements
The result of a function may be the same or not the same on every execution.
Deterministic
The result of How much is 2 + 3? is always the same (5):
SELECT (2 + 3) AS Calculation;
GO
228
Functions
The built-in function that concatenates customer’s FirstName and LastName is also deterministic:
Returns the same result on every execution, unless the data in the source DB object is changed.
Non Deterministic
The results of
• What is the time now?
• Give me a random number
are different on every execution of the functions:
SELECT
GETDATE() AS DateTimeNow
, RAND() AS Random;
GO
DateTimeNow Random
1967-07-03 11:08:07.370 0.982723166477615
type type_desc
SELECT DISTINCT [type], type_desc
FN SQL_SCALAR_FUNCTION
FROM sys.objects
IF SQL_INLINE_TABLE_VALUED_FUNCTION
WHERE [type] IN ('FN', 'TF', 'IF');
TF SQL_TABLE_VALUED_FUNCTION
GO
229
Functions
Multi-statement Ta-
Scalar-valued Inline Table-valued
ble-valued
Input parameters Yes, No Yes, No Yes, No
DEFAULT values Yes, No Yes, No Yes, No
Output Single value of a specified data Recordset (multiple col- Recordset (multiple col-
type umns, multiple data types) umns, multiple data types)
Function’s definition
Is wrapped in BEGIN... END Yes No Yes
block?
Temp Tables in the function’s definition
Local and Global (#Local- No No No
TempTable, ##GlobalTempTable)
Table Variable (@TableVari- Yes No Yes
able)
Statements in the function’s definition
Is the definition single state- No Yes No
ment?
SELECT Yes Yes Yes
INSERT, UPDATE, DELETE Only manipulate table variable, No Only to manipulate the ta-
created in the definition ble variable that is INSERTed
Execution
The function can be called in statement
SELECT Yes Yes (in subquery) Yes (in subquery)
FROM Yes (in the ON clause) Yes Yes
WHERE Yes Yes (in subquery) Yes (in subquery)
GROUP BY Yes No No
ORDER BY Yes Yes Yes
Is executed on every row? Yes No No
Steps RETURN (Value) RETURN (T-SQL to generate 1. Create @TableVariable
or the returned recordset); 2. Execute multiple
1. DECLARE @ReturnVariable; statements to populate the
2. SET @ReturnVariable = ...; @TableVariable
3. RETURN @ReturnVariable; 3. RETURN
230
Functions
Scalar-valued Functions
We can create a function udf_Divide() and use it everywhere we need to divide. It accepts two input parameters
(@Dividend and @Divisor) and returns a single value - which is the result of the division. When the divisor is NULL
or 0, the returned value is NULL.
Function parameters
CREATE FUNCTION dbo.udf_Divide(@Dividend DECIMAL(38, 6), @Divisor DECIMAL(38, 6))
RETURNS DECIMAL(38, 6) Data type of the value that the function returns Parameter name and data type
AS AS is optional and can be omitted
BEGIN
RETURN (CASE WHEN ISNULL(@Divisor, 0) != 0 THEN (@Dividend / @Divisor) END); Function Definition
END
GO
To use a scalar-valued function, we add it in the SELECT, ON (in FROM), WHERE, GROUP BY or ORDER BY clause.
Division
SELECT dbo.udf_Divide(15, 0) AS Division;
NULL
GO
Test divide:
Division
SELECT dbo.udf_Divide(15, 0.35) AS Division;
42.857142
GO
231
Functions
We can create a user-defined scalar-valued function that returns the last sale value for a specified customer:
The code below uses udf_LastSaleValueForCustomerID() and udf_Divide()functions. The result of udf_
LastSaleValueForCustomerID() is passed as parameter in the udf_Divide() function:
SELECT
C.CustomerID
, C.FirstName
, C.LastName
, AVG(Quantity * Price) AS Avg_SalesValue
, dbo.udf_LastSaleValueForCustomerID(C.CustomerID) AS Last_SaleValue
232
Functions
, dbo.udf_Divide
(
AVG(Quantity * Price)
, dbo.udf_LastSaleValueForCustomerID(C.CustomerID) @Divident (Avg_SalesValue)
)
AS Percentage
FROM
@Divisor (Last_SaleValue) - Nested functions
dbo.Customers AS C
JOIN dbo.Sales AS S
ON C.CustomerID = S.CustomerID (Avg_SalesValue / Last_SaleValue) = Percentage
GROUP BY
C.CustomerID
, C.FirstName Custo- First- Last- Avg_Sales- Last_Sa- Percent-
merID Name Name Value leValue age
, C.LastName
3 Beverly NULL 22.44 41.16 0.545189
ORDER BY CustomerID;
4 John Smith 24.88 24.88 1.000000
GO
5 John Smith 20.17 12.15 1.660082
To define the DEFAULT value of a parameter, we add it to the parameter name and data type.
We can use NULL as DEFAULT and handle the default value (NULL) in the function’s definition:
CREATE FUNCTION dbo.udf_DateNoTime(@DateTime DATE = NULL) The default value for @DateTime
RETURNS DATE parameter is NULL
AS
BEGIN
DECLARE @Date DATE;
RETURN @Date;
END
GO
233
Functions
If the keyword DEFAULT is omitted, the DEFAULT value is not passed and an error message “An insufficient
! number of arguments were supplied for the procedure or function dbo.udf_DateNoTime.“ is returned.
SELECT
SaleID
, CustomerID
, ItemID
, DateTimeOfSale
, dbo.udf_DateNoTime(DateTimeOfSale) AS DateOfSale
FROM dbo.Sales
ORDER BY CustomerID; Date and time Date only
GO
SaleID CustomerID ItemID DateTimeOfSale DateOfSale
1 3 1004 1967-10-17 04:37:37.487 1967-10-17
4 3 8541 1968-12-17 08:46:12.740 1968-12-17
3 4 5432 1965-09-04 22:13:09.130 1965-09-04
5 5 9514 1968-03-04 10:28:11.877 1968-03-04
2 5 5273 1968-05-08 20:44:01.887 1968-05-08
The DEFAULT value can not be additionally manipulated in the function’s definition:
234
Functions
Calculation
Default value for the first parameter
NULL
An example of a scalar-valued function that has no parameter and returns today’s date (no time):
RETURN @DateNow;
END
GO
Table-valued Functions
235
Functions
Inline
The inline table-valued function is constructed by one statement.
Collect the totals of SaleValue for the customers and calculate the percentage of the current sale of the total
sales:
SELECT
C.CustomerID
Scalar-valued function
, C.FirstName
, C.LastName
, dbo.udf_DateNoTime(S.DateTimeOfSale) AS DateOfSale
, (S.Quantity * S.Price) AS SaleValue
, SSV.Sum_SaleValue
, dbo.udf_Divide
( Scalar-valued function
(S.Quantity * S.Price)
, SSV.Sum_SaleValue
)
AS Percentage
FROM
236
Functions
dbo.Customers AS C
JOIN dbo.Sales AS S
Table-valued function
ON C.CustomerID = S.CustomerID
JOIN dbo.udf_SumSalesValue() AS SSV
ON C.CustomerID = SSV.CustomerID
ORDER BY CustomerID; Implement the table-valued function
GO by JOINing it in the FROM clause:
Sum_
Custo- First- Last- SaleV- Percent-
DateOfSale SaleV-
merID Name Name alue age
alue
3 Beverly NULL 1967-10-17 3.72 44.88 0.082887 3.72 / 44.88 = 0.082887
3 Beverly NULL 1968-12-17 41.16 44.88 0.917112
4 John Smith 1965-09-04 24.88 24.88 1.000000
5 John Smith 1968-05-08 12.15 40.34 0.301189
5 John Smith 1968-03-04 28.19 40.34 0.698810
Multi-Statement
Creates a recordset by executing multiple statements. Function udf_EligibleCustomers() returns CustomerID
and Sum_SalesValue for customers that have a total SaleValue greater then the parametrized threshold value.
237
Functions
(
SELECT CustomerID
FROM @EligibleCustomers
)
GROUP BY CustomerID;
RETURN
END
GO
CustomerID Sum_SalesValue
Call the function with @ThresholdValue = 30: 3 44.88
5 40.34
SELECT *
FROM dbo.udf_EligibleCustomers(30);
GO
Implement the function udf_EligibleCustomers in the FROM clause and calculate the DiscountedValue only for
the eligible customers (the ones who have total SalesValue above the threshold):
SELECT
C.CustomerID
, C.FirstName
, C.LastName
, dbo.udf_DateNoTime (S.DateTimeOfSale) AS DateOfSale
, (S.Quantity * S.Price) AS SalesValue
, EC.Sum_SalesValue The discounted
value is 90%
, CASE
WHEN EC.Sum_SalesValue IS NOT NULL THEN ((S.Quantity * S.Price) * 0.9)
END AS DiscountedValue
FROM
Only the eligible customers
dbo.Customers AS C
JOIN dbo.Sales AS S
ON C.CustomerID = S.CustomerID
LEFT JOIN dbo.udf_EligibleCustomers(41) AS EC
ON C.CustomerID = EC.CustomerID
ORDER BY CustomerID; Table-valued function. Pa-
GO rameter to filter the data.
238
Functions
Sum_
Custo- First- Last- Sales- Discount-
DateOfSale Sales-
merID Name Name Value edValue
Value
3 Beverly NULL 1967-10-17 3.72 44.88 3.34800
3 Beverly NULL 1968-12-17 41.16 44.88 37.04400
4 John Smith 1965-09-04 24.88 NULL NULL
5 John Smith 1968-03-04 28.19 NULL NULL
5 John Smith 1968-05-08 12.15 NULL NULL
Functions Nesting
Nest one function inside another. The inner function is executed first and its result is used as a parameter for the
outer function.
Result
SELECT RIGHT(LEFT('This is My String', 14), 6) AS Result;
My Str
GO
Position 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
String T h i s i s M y S t r i n g
LEFT(String, 14) Left 14 characters
RIGHT(String, 6) Right 6 characters
Limitations:
• Nesting maximum 32 levels
• User-fefined functions: Maximum 2100 input parameter
• No special characters in the name
239
Stored Procedures
We can add the following rules, that are specific for SPs to the Naming Convention rules:
• SP (and every other DB object) name is a maximum of 128 characters
• Avoid sp_ prefix. It is used by system SPs
• Add the group where the SP belongs as prefix:
• main_ - general use
• rep_ - reporting
• etl_ - ETL
• task_ - called by SQL Job Agent, Windows scheduled job etc
• Add the action (what the SP is doing) as suffix:
• _Select - SELECTs data
• _Insert - INSERTs data
• _Update - UPDATEs data
• _Delete - UPDATEs data
240
Stored Procedures
Customers
Custo- First- Last-
Email DateTimeRegistered
merID Name Name
1 Anabel Larson anabel.larson@customer5.info 1966-08-24 05:58:58.983
2 Anna Laurier anna.laurier@customer2.net 1967-09-09 16:43:46.510
3 Beverly NULL NULL 1967-10-15 16:57:11.237
4 John Smith john.smith@customer1.com 1966-07-12 17:20:31.277
5 John Smith john.smith@customer3.org 1964-09-02 17:27:07.667
Items
UnitOf-
ItemID ItemCode ItemDescription Price
Measure
1 ID52-P1 Interactive Doll - Pink Pcs 12.42
2 EG5-WW58 Word Whammer Kg 49.38
3 BT-D521 Bath Toy Duck Box 0.58
4 BT-S512 Bath Toy Forms (set) Pcs 5.68
5 B-P12_G Bike for Girls (Purple) Pcs 124.86
Sales
Custo- Quan-
SaleID ItemID Price DateTimeOfSale
merID tity
1 3 1 3 1.24 1967-10-17 04:37:37.487
2 5 3 5 2.43 1968-05-08 20:44:01.887
3 4 2 2 12.44 1965-09-04 22:13:09.130
4 3 4 7 5.88 1968-12-17 08:46:12.740
5 5 5 1 28.19 1968-03-04 10:28:11.877
6 2 2 12 12.28 1966-08-05 15:23:47.233
7 1 3 5 2.28 1969-11-14 07:18:52.667
Before creating a new SP, delete the SP with the same name (if it exists):
241
Stored Procedures
CREATE PROC is followed by the parameter(s) list, if the SP is parametrized. In the BEGIN… END block is the SP
definition – the code that the SP executes. The RETURN keyword inside the SP definition terminates the execution
of the SP.
To create a SP that has no input parameters and selects all customers, ordered from the last to the first registered:
SELECT
FirstName
, LastName
, Email
, DateTimeRegistered
FROM dbo.Customers
DSO naming convention is allowed in
ORDER BY DateTimeRegistered DESC;
the stored procedure’s definition
END
GO
242
Stored Procedures
First- Last-
Email DateTimeRegistered
Name Name
Beverly NULL NULL 1967-10-15 16:57:11.237
Anna Laurier anna.laurier@customer2.net 1967-09-09 16:43:46.510
Anabel Larson anabel.larson@customer5.info 1966-08-24 05:58:58.983
John Smith john.smith@customer1.com 1966-07-12 17:20:31.277
John Smith john.smith@customer3.org 1964-09-02 17:27:07.667
Parametrized SP
The parameters are variables that give us the option to pass input values into the SP definition.
The parameters add flexibility to the logic of the SP.
To add parameters to a SP, we add the parameter(s) list after the CREATE PROC DDL statement and we define their
names and data types.
To create a SP that apply the CRUD DML statements Prefix type of the DB object
on the Customers table, we execute the CREATE PROC SPName - Name of the manipulated object
DDL statement: Suffix Action
243
Stored Procedures
244
Stored Procedures
First we check the parameters. In usp_Customers_CRUD the only check is if a value for the parameter @CustomerID
is passed for Read, Update or Delete actions (line 14). If the result it True (no value):
• RAISERROR() - returns custom error message
• RETURN - terminates the execution of the SP
In this simple SP we have just a few consecutive statements and we execute only three of them:
line 11
and lines 16, 17 (if @CustomerID is omitted for Read, Update, Delete actions)
and line 22 (on Create action)
or line 38 (on Read action)
or line 49 (on Update action)
or line 59, 62 (on Delete action)
With the IF condition we decide which portion of the code is to be executed. The @Action parameter determines
the CRUD (Create, Read, Update or Delete a row in a table) cases:
• To Create a new customer, we need values for the parameters @FirstName, @LastName and @Email.
@CustomerID is not needed, it is created automatically (Column CustomerID in Customers table is
IDENTITY(1, 1) i.e. auto incremental). In column DateOfRegistration we insert now
• The Read action SELECTs the row where CustomerID = @CustomerID from table Customers.
When @CustomerID is NULL, the right operand in the filtering condition is replaced with CustomerID
(CustomerID = CustomerID) and all the customers are selected.
• The Update action updates the attributes (the columns FirstName, LastName, Email) for @CustomerID
• The Delete action executes two statements:
• The first one deletes the rows for @CustomerID from the table Sales. This is needed, because we can’t
delete a Primary Key if a Foreign Key exists.
• The second statement deletes @CustomerID from table Customers.
245
Stored Procedures
If we EXECute a SP without specifying value(s) for the parameters which don’t have DEFAULT values:
EXEC dbo.usp_Customers_CRUD;
GO
the error message “Procedure or function 'usp_Customers_CRUD' expects parameter '@Action', which was not
supplied.” is returned.
1. Pass the parameter(s) value(s) in the exact order as they are defined in the parameter(s) list:
EXEC dbo.usp_Customers_CRUD
'R' The value for Parameter 1 is in Position 1
, 5 The value for Parameter 2 is in Position 1
, DEFAULT
, DEFAULT
, DEFAULT; The value for Parameter 5 is in Position 1
GO
If we use the DEFAULT value for the last parameter, we can skip the DEFAULT keyword:
EXEC dbo.usp_Customers_CRUD
'R' Parameter 1 in Position 1
, 5; Parameter 2 in Position 2
GO
We can execute a SP without the keyword EXEC, but this is not a good practice, because:
• The code is not easy to understand
• We can’t search the batch for a SP by the keyword EXEC
dbo.usp_Customers_CRUD
'R'
, 5;
GO
246
Stored Procedures
For any other than the last parameter, we can’t skip the keyword DEFAULT, because the next parameter takes the
previous position:
EXEC dbo.usp_Customers_CRUD
'C' @Action
If we skip this DEFAULT, the next parameter is used in position 2
, DEFAULT @CustomerID
(the value 'Antoine' is passed for parameter @CustomerID
, 'Antoine' @FirstName
, 'Kern'; @LastName
The last parameter @Email can be omitted
GO
SELECT *
FROM dbo.Customers;
GO
When we EXECute a SP with parameter(s) name and value(s), we do not have to list them in order:
EXEC dbo.usp_Customers_CRUD
@CustomerID = 5
, @Action = 'R';
GO
To test the custom error, we EXECute the SP without passing a value for @CustomerID:
Execute this in a new SSMS window, starting from line 1. This helps the error message to return the correct
! line number.
247
Stored Procedures
The error message points us to line 17. To debug the SP, we need to examine line 17.
We do this by EXECuting the system SP sp_helptext. The parameter that it accepts is ObjectName. The
returned recordset is the object (Function, Stored Procedure) definition:
Text
16 ...
17 RAISERROR('Parameter @CustomerID is NULL', 11, 1);
18 ...
To UPDATE a row, we provide values for the parameters that edit the values for the columns - FirstName, LastName
and Email:
EXEC dbo.usp_Customers_CRUD
@Action = 'U'
, @FirstName = 'Jonathan'
, @LastName = 'Gourmand'
, @Email = 'jonathang@customer52341.org'
, @CustomerID = 3;
GO
SELECT *
FROM dbo.Customers;
GO
248
Stored Procedures
Customers
CustomerID FirstName LastName Email DateTimeRegistered
1 Anabel Larson anabel.larson@customer5.info 1966-08-24 05:58:58.983
2 Anna Laurier anna.laurier@customer2.net 1967-09-09 16:43:46.510
3 Jonathan Gourmand jonathang@customer52341.org 1967-10-15 16:57:11.237
4 John Smith john.smith@customer1.com 1966-07-12 17:20:31.277
5 John Smith john.smith@customer3.org 1964-09-02 17:27:07.667
6 Antoine Kern NULL 2016-05-16 21:32:43.563
The way this SP is designed, if we omit a value for one of the parameters @FirstName, @LastName or
! @Email, its DEFAULT value (NULL) is used.
To DELETE all the rows for CustomerID = 3 in table Sales and the row in table Customers, we EXEC:
EXEC dbo.usp_Customers_CRUD
@Action = 'D'
, @CustomerID = 3;
GO
SELECT *
FROM dbo.Sales;
GO
Sales
SaleID CustomerID ItemID Quantity Price DateTimeOfSale
2 5 3 5 2.43 1968-05-08 20:44:01.887
3 4 2 2 12.44 1965-09-04 22:13:09.130
5 5 5 1 28.19 1968-03-04 10:28:11.877
6 2 2 12 12.28 1966-08-05 15:23:47.233
7 1 3 5 2.28 1969-11-14 07:18:52.667
SELECT *
FROM dbo.Customers;
GO CustomerID = 3 is deleted from
tables Sales and Customers.
249
Stored Procedures
Customers
CustomerID FirstName LastName Email DateTimeRegistered
1 Anabel Larson anabel.larson@customer5.info 1966-08-24 05:58:58.983
2 Anna Laurier anna.laurier@customer2.net 1967-09-09 16:43:46.510
4 John Smith john.smith@customer1.com 1966-07-12 17:20:31.277
5 John Smith john.smith@customer3.org 1964-09-02 17:27:07.667
6 Antoine Kern NULL 2016-05-16 11:08:46.963
Output Parameter(s)
The OUTPUT parameter(s) return value(s) to the caller (SSMS, external application, etc.) of the SP.
The SP usp_Sales returns a summarized or detailed recordset for specified customer, item and sale period.
Create usp_Sales:
250
Stored Procedures
251
Stored Procedures
252
Stored Procedures
96. , C.LastName
97. ORDER BY
98. C.FirstName
99. , C.LastName;
100.
101. SELECT @ReturnParameter_OUT =
'Grain: '
+ @Grain
+ '; Rows: '
+ CAST(@@ROWCOUNT AS VARCHAR);
102. END
103. --** Get the report - End **--
104. END
105. GO
On line 8 we add the parameter @ReturnParameter_OUT with a DEFAULT value of NULL (to be able to EXECute the
SP without the OUTPUT functionality) and OUT keyword that specifies the parameter as OUTPUT.
On lines 74 and 101 we assign a value to the OUTPUT parameter - the number of rows in the recordset, returned by
the SP.
The built-in function @@ROWCOUNT returns the number of the rows, manipulated by the last statement.
EXEC rep.usp_Sales
Details (@Action parameter is not speci-
@CustomerID = 5;
fied and the DETAILS value is used)
GO
253
Stored Procedures
EXEC rep.usp_Sales
@Grain = 'S' CustomerName SalesValue
, @CustomerID = 4; John Smith 24.88
GO
To handle the value, returned by the OUTPUT parameter(s), we need to follow this 3-steps logic:
Step 1: Create variable (@Out_ReturnParameter) that stores the value of the OUTPUT parameter:
DECLARE
@Out_ReturnParameter NVARCHAR(64) Variable that stores the value of the OUTPUT parameter
, @DateTimeExecuted DATETIME = GETDATE(); Variable that stores the value of now.
Inserted into the log table in Step 3
Step 2: EXECute the SP and populate the variable @Out_ReturnParameter:
EXEC rep.usp_Sales
@CustomerID = 5
, @ReturnParameter_OUT = @Out_ReturnParameter OUT; Assign a value to @Out_ReturnParameter
254
Stored Procedures
)
SELECT
GETDATE()
, @Out_ReturnParameter;
GO
Edit Step 2 - the value of @CustomerID to 4, @Grain to 'S' and EXECute the batch again:
EXEC rep.usp_Sales
@Grain = 'S'
CustomerName SalesValue
, @CustomerID = 4
John Smith 24.88
, @ReturnParameter_OUT = @Out_ReturnParameter OUT;
To verify the hahdling of the OUTPUT parameter in Step 3, we query the Log table:
SELECT *
FROM dbo.StoredProceduresLogs;
GO
SP usp_SalesAndLog EXECutes another SP (usp_Sales) and inserts the value of the OUTPUT parameter
(@ReturnParameter_OUT) in the log table. It collects and bypasses the parameters, needed to EXECute the inner SP.
Create usp_SalesAndLog that reproduces the 3-steps logic, explained above. Step 2 bypasses the parameters from
the outer SP to the inner SP:
255
Stored Procedures
EXEC rep.usp_SalesAndLog
@CustomerID = 4 CustomerName SalesValue
, @Grain = 'S' John Smith 24.88
GO
SELECT *
FROM dbo.StoredProceduresLogs;
GO
LogID DateTimeExecuted Details
1 1968-05-17 11:15:59.973 Grain: D; Rows: 2
2 1968-06-05 23:48:05.633 Grain: S; Rows: 1
3 1969-05-17 11:25:34.630 Grain: S; Rows: 1
256
Stored Procedures
INSERT... EXEC
We can insert a recordset, created by a SP into a table, by using the INSERT… EXEC statement.
Collect the results of the EXECutions of usp_SalesAndLog into table SPExecResults in schema rep:
EXECute again for @CustomerID = 2 and verify the content of table SPExecResults:
SELECT *
FROM rep.SPExecResults;
GO
CustomerName SalesValue DateTimeExecuted
John Smith 24.88 1967-05-17 11:43:45.060
Anna Laurier 147.36 1969-06-09 20:05:45.757
ALTER PROC
To edit a SP:
• Export the current SP definition: (In SSMS Object Explorer) Databases (DatabaseName)
Programmability Stored Procedures (right click) (StoredProcedureName)
Script Stored Procedure as ALTER To File ...
• Edit the:
• Parameters list (if we need to add an additional parameter or edit/delete an existing one)
• The SP definition
• Execute the ALTER PROC statement (F5) to save the changes in the DBE
Before the ALTER or DROP SP, we save a backup, exported as explained above (...CREATE To...)
257
Stored Procedures
SELECT
FirstName
, LastName
, Email
, DateTimeRegistered
FROM dbo.Customers
WHERE CustomerID = @CustomerID; Use the parameter
END
GO
DROP PROC
We delete the Stored Procedure with the DROP PROC DDL statement.
SP Limitations
• Number of parameters per SP: 2,100
258
Extended Cheat Sheet
Naming Conventions
Prefix
t, t_, tbl, tbl_ - Table
v, v_, vw, vw_ - View
fn, fn_, ufn, ufn_, udf, udf_, usf, utf - Function
sp, usp_ (Do not use sp_ - built in SP) - Stored Procedure
Hierarchy
[ServerName].[DatabaseName].[SchemaName].[ObjectName] SDSO
[DatabaseName].[SchemaName].[ObjectName] DSO
[SchemaName].[ObjectName] SO
[ObjectName] O
SSMS
Script Object
• In Object Explorer expand Databases
• Expand DatabaseName
• Expand Tables, Views or Programmability (Stored Procedures or Functions (Scalar-valued, Table-valued))
• Right click on the ObjectName
• Script (Table, View, Function, Stored Procedure) as
• Pick the type of the exported script (DDL or DML)
• New Query Window, File, Clipboard
Shortcuts
F5 Execute F3 Find next
Alt + Break Cancel running execution Shift + F1 (mark keyword) Search in Books Online
Ctrl + F5 Parse Ctrl + Tab Move to the next tab
Ctrl + R Toggle Results and Messages Ctrl + Shift + Tab (↑, ↓) Jump to tab
F4 Properties Ctrl + N New query tab
Ctrl + M Include actual execution plan Ctro + O Open file
Ctrl + L Estimated execution plan Ctrl + F4 Close the current tab
Alt + F1 (mark ObjectName) EXEC sp_help Alt + F4 Exit SSMS
Ctrl + F (Ctrl + H) Find and Replace Ctrl + S Save
259
Extended Cheat Sheet
Comments
/*
Multiple lines comment - row 1
Multiple lines comment - row 2
*/
Collations
Server Level
SELECT *
FROM sys.fn_helpcollations();
DB level
CREATE DATABASE DatabaseName
COLLATE SQL_Latin1_General_CP1_CI_AS;
Column level
SELECT [collation_name] AS ColumnCollation
FROM sys.columns
WHERE
OBJECT_NAME([object_id]) = 'TableName'
AND [name] = 'ColumnName';
260
Extended Cheat Sheet
Overwrite collation
SELECT ColumnName COLLATE SQL_Latin1_General_CP1_CI_AS
FROM TableName;
SELECT
T1.ColumnName2
, T2.ColumnName2
FROM
DatabaseName1.SchemaName1.TableName1 AS T1
JOIN DatabaseName2.SchemaName2.TableName2 AS T2
ON T1.ColumnName1 COLLATE SQL_Latin1_General_CP1_CI_AS = T2.ColumnName1;
261
Extended Cheat Sheet
SELECT SERVERPROPERTY('PropertyName');
Databases
SELECT
DB_NAME(database_id) AS [Database Name]
, name AS [File Name]
, physical_name AS [File Path]
, (size * 8) / 1024 AS [Size (MB)]
FROM sys.master_files;
Database files
SELECT *
FROM DatabaseName.sys.database_files;
262
Extended Cheat Sheet
Schemas
Tables
263
Extended Cheat Sheet
264
Extended Cheat Sheet
CHECK (CK)
CONSTRAINT CK__TableName_ColumnName__CKDescription
CHECK (ColumnName > 0)
UNIQUE (UQ)
CONSTRAINT UQ__TableName_ColumnName
UNIQUE
265
Extended Cheat Sheet
ColumnName1 INT
, ColumnName2 MONEY;
Constraints
ADD CONSTRAINT – add new constraint
ALTER TABLE DatabaseName.SchemaName.TableName
ADD CONSTRAINT DF__TableName_ColumnName__DFDescription
DEFAULT (GETDATE())
FOR ColumnName;
266
Extended Cheat Sheet
SELECT
ColumnName1
, ColumnName2
FROM DatabaseName2.SchemaName2.TableName2;
INSERT... UNION
INSERT DatabaseName.SchemaName.TableName
(
ColumnName1
, ColumnName2
)
SELECT 1, 'Value1'
UNION ALL SELECT 2, 'Value2';
INSERT... VALUES
INSERT DatabaseName.SchemaName.TableName
(
ColumnName1
, ColumnName2
)
VALUES
(1, 'Value1')
, (2, 'Value2');
UPDATE DatabaseName1.SchemaName1.TableName1
SET
ColumnName3 = A.Sum_Col3
, ColumnName4 = A.Avg_Col4
FROM
(
SELECT
ColumnName1 AS Col1
, ColumnName2 AS Col2
, SUM(ColumnName3) AS Sum_Col3
, AVG(ColumnName4) AS Avg_Col4
FROM DatabaseName2.SchemaName2.TableName2
267
Extended Cheat Sheet
GROUP BY
ColumnName1
, ColumnName2
) AS A
WHERE
ColumnName1 = A.Col1
AND ColumnName2 = A.Col2;
DELETE T1
FROM
DatabaseName1.SchemaName1.TableName1 AS T1
JOIN DatabaseName2.SchemaName2.TableName2 AS T2
ON T1.ColumnName1 = T2.ColumnName1;
WHERE T2.ColumnName2 = FilterValue;
Views
SELECT *
FROM DatabaseName.sys.objects
WHERE [type] IN ('V');
268
Extended Cheat Sheet
, ColumnName2
FROM DatabaseName.SchemaName.TableName;
! No ORDER BY in Views
SELECT
SELECT 123;
SELECT (123 + 456);
SELECT '123 ' + 456;
SELECT '123 ' + CAST(456 AS VARCHAR);
FROM
SELECT
ColumnName1
, ColumnName2
FROM DatabaseName.SchemaName.TableName;
JOIN
SELECT
T1.ColumnName2
, T2.ColumnName2
FROM
DatabaseName1.SchemaName1.TableName1 AS T1
JOIN DatabaseName2.SchemaName2.TableName2 AS T2 -- JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN
ON T1.ColumnName1 = T2.ColumnName1;
WHERE
269
Extended Cheat Sheet
SELECT 'A'
WHERE @VariableName = FilterValue;
SELECT
T1.ColumnName2
, T2.ColumnName2
FROM
DatabaseName1.SchemaName1.TableName1 AS T1
JOIN DatabaseName2.SchemaName2.TableName2 AS T2
ON T1.ColumnName1 = T2.ColumnName1;
WHERE
T1.ColumnName3 = FilterValue1
AND T2.ColumnName4 <= FilterValue2
AND
Separate OR from AND in brackets
(
T1.ColumnName5 > (@ParameterValue1 + @ParameterValue2)
OR T1.ColumnName6 BETWEEN FilterValue4 AND FilterValue5
);
GROUP BY
SELECT
ColumnName1
, ColumnName2
, SUM(ColumnName3) AS Sum_ColumnName3
, MIN(ColumnName4) AS Min_ColumnName4
, MAX(ColumnName5) AS Max_ColumnName5
, AVG(ColumnName6) AS Avg_ColumnName6
, COUNT(ColumnName7) AS Count_ColumnName7
, COUNT(DISTINCT ColumnName8) AS CountDistinct_ColumnName8
FROM DatabaseName.SchemaName.TableName
GROUP BY
ColumnName1
, ColumnName2;
GROUPING SETS
...
GROUP BY GROUPING SETS
(
(ColumnName1, ColumnName2) Group 1
, (ColumnName3, ColumnName4, ColumnName5) Group 2
, () Grand Total
270
Extended Cheat Sheet
ROLLUP
...
GROUP BY ROLLUP
(
ColumnName1
, ColumnName2
)
CUBE
...
GROUP BY CUBE
(
ColumnName1
, ColumnName2
)
HAVING
SELECT
ColumnName1
, ColumnName2
, MIN(ColumnName3) AS Min_ColumnName3
FROM DatabaseName.SchemaName.TableName
GROUP BY
ColumnName1
, ColumnName2
HAVING SUM(ColumnName4) >= FilterValue;
ORDER BY
SELECT
ColumnName1
, ColumnName2 AS Col2
, ColumnName3
FROM DatabaseName.SchemaName.TableName;
ORDER BY
ColumnName1 Column name
, Col2 DESC Alias. Descending
, 3; Column index
TOP
271
Extended Cheat Sheet
Order of execution:
Step 1: FROM… JOIN - build the virtual recordset (VR) from one or multiple data sources (DS)
Step 2: WHERE - filter the VR
Step 3: GROUP BY... HAVING - group, aggregate and filter the grouped VR
Step 4: SELECT – builds the resulting VR and create aliases
Step 5: ORDER BY – order the resulting VR and use the aliases created in the SELECT statement
Subqueries
Non-Correlated Subquery
SELECT
ColumnName1
, (
SELECT MIN(ColumnName2) Single value
FROM DatabaseName1.SchemaName1.TableName1
) AS ColumnName2 In the SELECT clause
FROM DatabaseName2.SchemaName2.TableName2;
272
Extended Cheat Sheet
SELECT
T1.ColumnName2
, T2.Sum_ColumnName2
FROM
DatabaseName1.SchemaName1.TableName1 AS T1
JOIN
(
SELECT DISTINCT
ColumnName1
, SUM(T2.ColumnName2) AS Sum_ColumnName2
FROM DatabaseName2.SchemaName2.TableName2
) AS T2 In the FROM clause
ON T1.ColumnName1 = T2.ColumnName1;
SELECT
ColumnName1
, ColumnName2
FROM DatabaseName1.SchemaName1.TableName1
WHERE
ColumnName3 IN In the WHERE clause (IN)
(
SELECT TOP 10 ColumnName1
FROM DatabaseName2.SchemaName2.TableName2
ORDER BY ColumnName2 DESC
);
SELECT
ColumnName1
, ColumnName2
FROM DatabaseName1.SchemaName1.TableName1
WHERE
EXISTS In the WHERE clause (EXISTS)
(
SELECT 1 Not needed to select data
FROM DatabaseName2.SchemaName2.TableName2
WHERE ColumnName1 = @VariableValue
);
Correlated Subquery
SELECT
T2.ColumnName2
273
Extended Cheat Sheet
, (
SELECT MIN(T1.ColumnName2)
FROM DatabaseName1.SchemaName1.TableName1 AS T1
WHERE T1.Column1 = T2.Column1
) AS Min_ColumnName2 In the SELECT clause
FROM DatabaseName2.SchemaName2.TableName2 AS T2;
SELECT
T1.ColumnName2
, T1.ColumnName3
FROM DatabaseName1.SchemaName1.TableName1 AS T1
WHERE
T1.ColumnName3 IN In the WHERE clause (IN)
(
SELECT T2.ColumnName1
FROM DatabaseName2.SchemaName2.TableName2 AS T2
WHERE T2.ColumnName1 = T1.ColumnName1 Correlation
GROUP BY T2.ColumnName1
HAVING SUM(T2.ColumnName2) >= FilterValue
);
SELECT
T1.ColumnName2
, T1.ColumnName3
FROM DatabaseName1.SchemaName1.TableName1 AS T1
WHERE
EXISTS In the WHERE clause (EXISTS)
(
SELECT 1
FROM DatabaseName1.SchemaName1.TableName1 AS T2
WHERE T2.ColumnName1 = T1.ColumnName1 Correlation
GROUP BY T2.ColumnName1
HAVING SUM(T2.ColumnName2) >= FilterValue
);
Functions
Object Catalog
SELECT *
FROM DatabaseName.sys.objects
WHERE [type] IN ('AF', 'FN', 'FS', 'FT', 'IF', 'TF');
274
Extended Cheat Sheet
Scalar-valued
DDL: DROP FUNCTION
IF (OBJECT_ID('SchemaName.udf_FunctionName', 'FN') IS NOT NULL)
BEGIN DROP FUNCTION SchemaName.udf_FunctionName; END Before SQL Server® 2016
275
Extended Cheat Sheet
Table-valued (Inline)
DDL: DROP FUNCTION
IF (OBJECT_ID('SchemaName.udf_FunctionName', 'IF') IS NOT NULL)
BEGIN DROP FUNCTION SchemaName.udf_FunctionName; END Before SQL Server® 2016
Table-valued (Multi-statement)
DDL: DROP FUNCTION
IF (OBJECT_ID('SchemaName.udf_FunctionName', 'TF') IS NOT NULL)
BEGIN DROP FUNCTION SchemaName.udf_FunctionName; END Before SQL Server® 2016
276
Extended Cheat Sheet
...Statement 2;...
RETURN;
END
Stored Procedures
Object Catalog
SELECT *
FROM DatabaseName.sys.procedures;
SELECT *
FROM DatabaseName.sys.objects
WHERE type IN ('P', 'PC', 'RF', 'X');
277
Extended Cheat Sheet
)
AS
BEGIN
IF (@ParameterName2 IS NULL)
BEGIN
...Statement(s) to sets DEFAULT value to @ParameterName2...;
END
...Statement 2...;
...Statement 3...;
END
OUTPUT patameter
DECLARE @ParameterName1_Out INT; Step 1: Create a variable to store the
value returned by the OUT parameter
EXEC PROC DatabaseName.SchemaName.usp_StoredProcedureName Step 2: EXECute and
@ParameterName1 = 123 populate the variable
, @ParameterName2
, @ParameterName3 = @ParameterName1_Out OUT;
Conditional Execution
IF
IF (5 > 17) Condition (False)
BEGIN PRINT 'The condition is True'; END
ELSE
BEGIN PRINT 'The condition is False'; END
278
Extended Cheat Sheet
Nested IF
IF (5 < 17) Condition 1 (True)
BEGIN
Condition 2 (False)
IF (6 = 7)
BEGIN PRINT 'Condition 1 is True, Condition 2 is True'; END
ELSE
BEGIN PRINT 'Condition 1 is True, Condition 2 is False'; END
END
ELSE
BEGIN PRINT 'Condition 1 is False'; END
IIF
SELECT IIF(ColumnName1 > 5, 'Greater than 5', 'Not greater than five') AS IIF_Result
FROM DatabaseName.SchemaName.TableName;
CASE
SELECT
ColumnName1
, ColumnName2
, CASE ColumnName2
WHEN 5 THEN 'Five'
WHEN 6 THEN 'Six'
ELSE 'Not 5 and 6'
END AS Case_Result
FROM DatabaseName.SchemaName.TableName;
SELECT
ColumnName1
, ColumnName2
, CASE
WHEN (ColumnName2 BETWEEN 5 AND 6 AND ColumnName1 = 52) THEN 'BETWEEN 5 AND 6 (52)'
WHEN ColumnName2 BETWEEN 5 AND 6 THEN 'BETWEEN 5 AND 6'
ELSE 'Ignored'
END AS Case_Result
FROM DatabaseName.SchemaName.TableName;
279
Extended Cheat Sheet
ColumnName1
, ColumnName2
FROM DatabaseName1.SchemaName1.TableName1
)
SELECT
C.Col1
, C.Col2
, T1.ColumnName2
FROM
CTE AS C
JOIN DatabaseName2.SchemaName2.TableName2 AS T1
ON C.Col1 = T1.ColumnName1;
280
Extended Cheat Sheet
UNION ALL UNION ALL combines the anchor and the recursive statement(s)
UNION ALL
SELECT
(C.[Level] + 1) AS [Level]
281
Extended Cheat Sheet
, P.ChildColumnName
, P.ParentColumnName
FROM DatabaseName.SchemaName.TableName AS P
JOIN CTE_Recursive AS C
ON C.ParentColumnName = P.ChildColumnName
)
SELECT *
FROM CTE_Recursive;
Variables
DECLARE @VariableName1 INT = 123; Create and assign a value to the variable
SELECT *
FROM DatabaseName.SchemaName.TableName
WHERE ColumnName = @VariableName1; Use the variable
Table variable
DECLARE @TableVariable TABLE Create table variable
(
ColumnName1 INT
, ColumnName2 DATETIME2
);
SELECT *
FROM Use the table variable
DatabaseName2.SchemaName2.TableName2 AS T1
JOIN @TableVariable AS TV
ON T1.ColumnName1 = TV.ColumnName1
AND T1.ColumnName2 = TV.ColumnName2;
282
Extended Cheat Sheet
Loops (WHILE)
PRINT 'Finished!';
Data Types
Numerics Binary
Exact BINARY
TINYINT, SMALLINT, INT, BIGINT VARBINARY
NUMERIC, DECIMAL (DEC) IMAGE - Obsolete. Replaced with BINARY(MAX)
MONEY, SMALLMONEY Date and time
BIT SMALLDATETIME, DATETIME, DATETIME2
Approximate DATE
FLOAT, REAL TIME
DATETIMEOFFSET
Strings
Character Non-Unicode Other
CHAR UNIQUEIDENTIFIER
VARCHAR TIMESTAMP - Obsolete. Replaced with ROWVERSION.
TEXT - Obsolete. Replaced with VARCHAR(MAX) XML
Character Unicode SQL_VARIANT
NCHAR TABLE
NVARCHAR HIYERERARCHYID
NTEXT - Obsolete. Replaced with NVARCHAR(- CURSOR
MAX) Spacial
GEOGRAPHY
GEOMETRY
Conversions
283
Extended Cheat Sheet
SELECT
ColumnName
, CONVERT(DATETIME ColumnName)
FROM DatabaseName.SchemaName.TableName;
TRY_ converts from one data type to another and return NULL instead of an error message
SELECT TRY_CAST('ABC' AS INT);
SELECT TRY_CONVERT(INT, 'ABC');
SELECT TRY_PARSE('10/48/1968' AS DATETIME USING 'en-US') AS [TRY_PARSE];
SELECT STR(123.789);
Operators
Arithmetic Operators
+ Addition
- Subtraction
* Multiplication
/ Division
% Modulo
Compound Operators
+= Add the value of the right operand to the left operand
-= Subtracts the value of the right operand from the left operand
*= Multiplies the operands and sets the result to the left operand
/= Divides the operands and sets the result to the left operand
%= Sets the result of the modulo to the left operand
Assignment Operator
Equal sign (=)
Comparison Operators
= Equals to
284
Extended Cheat Sheet
Logical Operators
AND True if both operands return True
OR True if one of the operands return True
IN True if the operand is in the list (the other operand)
LIKE True if the operand matches a pattern (the other operand)
BETWEEN True if the operand is in the BETWEEN range
EXISTS True if the query returns one or more rows
NOT Reverse the meaning of the operator
Set Operators
UNION - combines two recordsets into one
SELECT ColumnName1, ColumnName2, ColumnName3
FROM DatabaseName1.SchemaName1.TableName1
UNION ALL -- or UNION to return duplicate rows
SELECT ColumnName1, ColumnName2, ColumnName3
FROM DatabaseName2.SchemaName2.TableName2
EXCEPT - remove the matching rows from the top recordset
SELECT ColumnName1, ColumnName2, ColumnName3
FROM DatabaseName1.SchemaName1.TableName1
EXCEPT
SELECT ColumnName1, ColumnName2, ColumnName3
FROM DatabaseName2.SchemaName2.TableName2
INTERSECT - return only the matching rows in the both recordsets
SELECT ColumnName1, ColumnName2, ColumnName3
FROM DatabaseName1.SchemaName1.TableName1
INTERSECT
SELECT ColumnName1, ColumnName2, ColumnName3
FROM DatabaseName2.SchemaName2.TableName2
285
Extended Cheat Sheet
3VL from
...
WHERE ColumnName1 NOT LIKE '%StringToSearch%';
3VL to
...
WHERE
(
ColumnName1 NOT LIKE '%StringToSearch%'
OR ColumnName1 IS NULL
);
Pivoting
PIVOT Columns
SELECT Rows Values
P.ColumnNameRows1 AS Row1, P.ColumnNameRows2 AS Row2
, P.[ValueInColumns1] AS Col1, P.[ValueInColumns2] AS Col2
FROM
286
Extended Cheat Sheet
(
SELECT
ColumnNameRows1, ColumnNameRows2
, ColumnNameColumns
, ColumnNameValues
FROM DatabaseName.SchemaName.TableName
) AS S Subquery for Rows, Columns and Values only
PIVOT
(
SUM(ColumnNameValues)
FOR ColumnNameColumns IN ([ValueInColumns1], [ValueInColumns2])
) AS P
ORDER BY P.ColumnNameRows1, P.ColumnNameRows2;
UNPIVOT
SELECT
U.ColumnNameRows1, U.ColumnNameRows2
, U.ColumnNameColumns
, U.ColumnNameValues
FROM
(
SELECT
ColumnNameRows1, ColumnNameRows2
, ValueInColumns1, ValueInColumns2
FROM DatabaseName.SchemaName.TableName
) AS P Subquery to select only data to be UNPIVOTed
UNPIVOT
(
ColumnNameValues
FOR ColumnNameColumns IN ([ValueInColumns1], [ValueInColumns2])
) AS U
ORDER BY
U.ColumnNameRows1, U.ColumnNameRows2
, U.ColumnNameColumns;
287
t
From the basics, ideal for beginners with their own PC, to more complex ideas for big
business, Learn Microsoft® SQL Server® Intuitively teaches you one step at a time, in an
easy-to-follow and simple format, written in a language that you will understand.
Learn Microsoft® SQL Server® Intuitively is the only book you will ever need to help
you navigate these complex processes. Due to its unique visual approach you will find it
easier than ever to completely understand the database programming and how it can work
better for you.
Get your copy of Learn Microsoft® SQL Server® Intuitively now and make the most out
of your home PC or business.
Peter Lalovsky lives and works in Montreal, Canada. He started his career in the IT field 17 years
ago as a prepress designer and an IT specialist.
In the last decade, guided by his passion towards Microsoft® SQL Server®, he worked on mul-
tiple projects as a T-SQL, .NET and BI developer (SSRS, SSIS, SSAS). Nowadays he works as a
database consultant at his company zPL Concept.
His interest in all spheres of IT has led Peter to become a Microsoft® Certified Professional and
prompted him to write the book – Learn Microsoft® SQL Server® Intuitively – which is a hands on and practical
manual, in which he hopes to help others with his in-depth knowledge of the subject.
He saves and shares his practical tips and advice ISBN 9780995245105