Database used: AdventureWorks (modified version) Technology: Microsoft SQL Server · Transact-SQL (T-SQL)
Table of Contents
- 2.1 Overview and context
- 2.2 AdventureWorks Database
- 2.3 SQL Terminology
- 2.4 Demo: SELECT instruction
- 2.5 Demo: Filtering with WHERE
- 3.1 Aggregate Functions
- 3.2 Group data (GROUP BY)
- 3.3 Filtering groups (HAVING)
- 3.4 Sort results (ORDER BY)
- 3.5 Module Summary
- 4.1 Joins Overview
- 4.2 Cross Join
- 4.3 Inner Join
- 4.4 Demo: Inner Join
- 4.5 Outer Joins (LEFT, RIGHT, FULL)
- 4.6 Self Join
- 4.7 Module Summary
- 5.1 Text manipulation functions
- 5.2 Demo: Text manipulation functions
- 5.3 Data Types & CAST
- 5.4 NULL value management
- 5.5 Demo: ISNULL, COALESCE, NULLIF
- 5.6 Module Summary
- 6.1 Subqueries
- 6.2 Demo: Subqueries
- 6.3 Common Table Expressions
- 6.4 Demo: CTEs
- 6.5 Pivoting and unpivoting data (PIVOT / UNPIVOT)
- 6.6 Demo: PIVOT and UNPIVOT
- 6.7 Module Summary
- 7.1 Introduction to Window Functions
- 7.2 The OVER clause
- 7.3 Ranking Functions
- 7.4 Demo: ROW_NUMBER()
- 7.5 Demo: RANK() and DENSE_RANK()
- 7.6 Demo: NTILE()
- 7.7 Aggregation functions with windowing
- 7.8 Demo: Window Aggregate Functions
- 7.9 Offset Functions
- 7.10 Demo: LAG, LEAD, FIRST_VALUE, LAST_VALUE
- 7.11 Module Summary
1. Course Overview
SQL (Structured Query Language) is the programming language used to communicate with relational databases. It has been around for over 50 years and remains omnipresent in all areas related to data.
This course teaches how to retrieve data from relational databases using Transact-SQL (T-SQL), the SQL dialect used by Microsoft SQL Server products and services.
Topics covered
| Theme | Description |
|---|---|
| Single-table queries | Filter and aggregate data from a single table |
| Multi-table queries | Combine data with JOIN, subqueries and CTEs |
| Window functions | Analyze data with window functions |
Target audience
- Data Analyst
- Business Analyst
- Product Manager
Note: This course does not cover creating databases or inserting or deleting data.
Prerequisites
- Familiarity with the SQL Server Management Studio (SSMS) interface
- Relational Database Basics
2. Selecting Data
2.1 Overview and context
SQL is a very popular programming language that helps uncover hidden information in data. Companies collect immense volumes of data which they organize and store in relational databases.
Fundamental concepts
- Relational database: organization of data into tables (relations)
- Table: represents a type of entity (e.g.:
Product,Customer) - Columns (attributes): characteristics of the entity (e.g.:
ProductName,Price) - Lines (tuples/rows): an instance of the entity
- RDBMS (RDBMS): software for importing, organizing and managing data
- Examples: Microsoft SQL Server, Oracle Database, MySQL
- Schema: logical container for database objects
- View and Stored Procedure: other objects that can be created in a database
SQL Tools by RDBMS
| RDBMS | Graphical interface |
|---|---|
| Microsoft SQL Server | SQL Server Management Studio (SSMS) |
| Oracle Database | Oracle SQL Developer |
| MySQL | MySQL Workbench |
SQL dialects
- Standard SQL: follows ANSI and ISO standards (latest standard: SQL 2016)
- T-SQL (Transact-SQL): Microsoft SQL Server dialect with additional features
- PL/SQL: Oracle dialect with procedural capabilities
The concepts learned with T-SQL allow you to quickly adapt to other SQL variants.
2.2 AdventureWorks Database
The AdventureWorks database is provided by Microsoft, slightly modified for the educational purposes of this course. It represents a fictitious multinational company that sells bicycles and accessories.
Main tables
| Table | Description |
|---|---|
Customer | Customer information (name, occupation, income, etc.) |
Orders | Orders placed by customers (number, amount, customer key) |
OrderDetails | Product details in each order |
Product | Product information (name, category, price) |
Employee | Information about employees and their managers |
SalesTerritory | Sales territory by geographic area |
Date | Time dimension table (calendar years) |
Relational diagram (simplified)
Customer (CustomerKey PK)
|-- 1:N --> Orders (OrderKey PK, CustomerKey FK)
|-- 1:N --> OrderDetails (ProductKey FK)
|-- N:1 --> Product (ProductKey PK)
Restore database
The database is provided as a .bak backup file. To restore it in SSMS:
- Right click on Databases in the Object Explorer
- Select Restore Database
- Choose Device and navigate to the
AdventureWorksDW.bakfile - Click OK to restore
2.3 SQL Terminology
SQL statement categories
| Category | Acronym | Description | Instructions |
|---|---|---|---|
| Data Definition Language | DOF | Definition of objects | CREATE, ALTER, DROP |
| Data Manipulation Language | DML | Requests and modifications | SELECT, INSERT, DELETE, MERGE |
| Data Control Language | DCL | Permission management | GRANT, REVOKE |
This course focuses on DML, particularly the
SELECTstatement.
Logical execution order of a SQL query
Although the code is written in the following syntactic order:
SELECT -- 5e
FROM -- 1er
WHERE -- 2e
GROUP BY -- 3e
HAVING -- 4e
ORDER BY -- 6e (dernier)
The order of logical processing by the SQL engine is:
- FROM — Determines the data source (table(s))
- WHERE — Filter individual rows (before grouping)
- GROUP BY — Groups rows by common values
- HAVING — Filter groups (after grouping)
- SELECT — Selects or calculates the columns to return
- ORDER BY — Sorts the final result
Important: A clause only has access to information already processed by previous clauses in logical order. This is why aliases defined in
SELECTcannot be used inWHEREorHAVING.
Three-valued predicate logic
T-SQL uses ternary logic: an expression can be evaluated to:
TRUEFALSEUNKNOWN(when a NULL value is involved)
SQL style rules
- SQL is case-insensitive:
SELECT=select - Ending statements with a semicolon (
;) is recommended - Spaces, newlines and tabs are equivalent for the engine
- Only the
SELECTclause is mandatory in a statement
Fully Qualified Name
The complete shape of an object has four parts:
[Server].[Database].[Schema].[Object]
-- Exemple :
[MyServer].[AdventureWorks].[dbo].[Customer]
SQL Server allows top parts to be omitted if using the current connection and database.
2.4 Demo: SELECT Statement
Select specific columns
-- Colonnes spécifiques
SELECT FirstName, LastName, Education, Occupation
FROM Customer;
Select all columns with *
-- Toutes les colonnes (à éviter sur de grandes tables)
SELECT *
FROM Customer;
Best practice: Avoid
SELECT *on large tables to improve performance and readability.
Aliasing columns (Aliasing)
-- Concaténation et alias
SELECT FirstName + ' ' + LastName AS FullName, Occupation
FROM Customer;
Limit results with TOP
-- 50% des enregistrements
SELECT TOP 50 PERCENT FirstName + ' ' + LastName AS FullName, Occupation
FROM Customer;
-- Les 10 premiers enregistrements
SELECT TOP 10 FirstName + ' ' + LastName AS FullName, Occupation
FROM Customer;
Eliminate duplicates with DISTINCT
-- Valeurs uniques de la colonne Occupation
SELECT DISTINCT Occupation
FROM Customer;
Without
DISTINCT, all rows are returned, including duplicates. The Customer table contains over 80,000 records.
2.5 Demo: Filtering with WHERE
The WHERE clause allows you to filter rows by applying logical conditions.
Comparison operators
-- Différent de 'Sam'
SELECT * FROM Customer WHERE FirstName <> 'Sam';
-- Égal à 'Sam'
SELECT * FROM Customer WHERE FirstName = 'Sam';
-- Commence par 'Sam' (wildcard %)
SELECT * FROM Customer WHERE FirstName LIKE 'Sam%';
Logical operators AND, OR, NOT
-- AND : les deux conditions doivent être vraies
SELECT *
FROM Customer
WHERE FirstName LIKE 'Sam%'
AND YearlyIncome >= 50000 AND YearlyIncome <= 100000;
-- BETWEEN : équivalent à >= ET <=
SELECT *
FROM Customer
WHERE FirstName LIKE 'Sam%'
AND YearlyIncome BETWEEN 50000 AND 100000;
-- IN : liste de valeurs spécifiques
SELECT *
FROM Customer
WHERE FirstName LIKE 'Sam%'
AND YearlyIncome IN (20000, 30000);
-- OR avec parenthèses pour forcer la priorité
SELECT *
FROM Customer
WHERE FirstName LIKE 'Sam%'
AND (YearlyIncome < 50000 OR YearlyIncome > 100000);
Warning: Without parentheses,
ORhas lower priority thanAND, which may produce unexpected results.
Summary of filter operators
| Operator | Description |
|---|---|
= | Equality |
<> or != | Difference |
>, <, >=, <= | Numerical comparisons |
LIKE | Pattern matching with wildcards (%, _) |
BETWEEN x AND y | Value range (inclusive) |
IN (val1, val2, ...) | List of values |
AND | Both conditions must be true |
OR | At least one condition must be true |
NOT | Negation of a condition |
3. Aggregating Data
3.1 Aggregate Functions
Aggregate functions operate on a set of rows and return a single synthetic value.
Main aggregation functions
| Function | Description | Ignore NULL? |
|---|---|---|
COUNT(*) | Count all rows | No |
COUNT(column) | Count non-NULL values | Yes |
SUM(column) | Sum of values | Yes |
AVG(column) | Average values | Yes |
MAX(column) | Maximum value | Yes |
MIN(column) | Minimum value | Yes |
Important: All aggregate functions except
COUNT(*)ignore NULL values. This may impact average calculations.
Impact of NULLs on AVG
Consider 5 products with stocks: 10, NULL, 20, 30, 20
-- Résultat : 16 (SUM = 80, COUNT(*) = 5)
SELECT SUM(Stock) / COUNT(*) AS AvgManual FROM ...
-- Résultat : 20 (SUM = 80, COUNT(Stock) = 4, les NULL sont exclus)
SELECT AVG(Stock) AS AvgBuiltIn FROM ...
Use DISTINCT in aggregations
-- Compter les valeurs distinctes (exclut NULL)
SELECT COUNT(DISTINCT Stock) -- Résultat : 3 (valeurs distinctes : 10, 20, 30)
-- Somme des valeurs distinctes
SELECT SUM(DISTINCT Stock) -- Résultat : 60 (10 + 20 + 30)
Behavior of MIN / MAX depending on data type
| Type | MAX returns | MIN returns |
|---|---|---|
| Digital | Largest number | Smallest number |
| Date | Most recent date | Oldest date |
| Character | Last alphabetical value | First alphabetical value |
Scalar functions
Unlike aggregate functions, scalar functions operate on each row individually:
UPPER(str)— Convert to uppercaseLOWER(str)— Convert to lowercaseYEAR(date),MONTH(date)— Extract year, monthFORMAT(value, 'format')— Formats a value for display
T-SQL offers more than 200 scalar functions in the categories: strings, dates/times, mathematics, logic.
Scalar and aggregate functions can be used in SELECT, ORDER BY and HAVING.
3.2 Grouping data (GROUP BY)
The GROUP BY clause allows you to group rows sharing common values, then to apply aggregation functions on these groups.
GROUP BY Rules
- Any column in
SELECTthat is not an aggregate function must appear inGROUP BY— otherwise an error occurs. - The order of results is not guaranteed without
ORDER BY.
Example: Statistics by category and color
SELECT Category,
Color,
COUNT(*) AS Products,
FORMAT(MAX(ListPrice), '0') AS Maximum_Price,
FORMAT(MIN(ListPrice), '0') AS Minimum_Price,
FORMAT(AVG(ListPrice), '0') AS Average_Price
FROM Product
GROUP BY Category, Color;
The
Producttable contains 397 products. A group is created for each unique combination ofCategoryandColor.
3.3 Filtering groups (HAVING)
The HAVING clause filters groups created by GROUP BY, usually based on the result of an aggregation.
Difference between WHERE and HAVING
| Criterion | WHERE | HAVING |
|---|---|---|
| Operates on | Individual lines | Line groups |
| Executed | Before GROUP BY | After GROUP BY |
| Can use aggregations | No | Yes |
| Can use aliases SELECT | No | No (SELECT not yet executed) |
Example: Filter groups with average price > 100
SELECT Category,
Color,
COUNT(*) AS Products,
FORMAT(MAX(ListPrice), '0') AS Maximum_Price,
FORMAT(MIN(ListPrice), '0') AS Minimum_Price,
FORMAT(AVG(ListPrice), '0') AS Average_Price
FROM Product
GROUP BY Category, Color
HAVING AVG(ListPrice) > 100 AND Category = 'Bikes';
Warning: You must repeat the calculation (
AVG(ListPrice)) inHAVING, because the alias (Average_Price) does not yet exist at this stage of execution. This query returns 5 groups.
3.4 Sort results (ORDER BY)
The ORDER BY clause is the last to be executed in a query. It controls the order in which the result is displayed.
Default behavior
Without ORDER BY, SQL Server is free to return rows in any order (tables represent sets with no intrinsic order).
With ORDER BY, the result becomes an (ordered) cursor.
Syntax and options
-- Tri simple ascendant (par défaut)
SELECT Product, Subcategory, Color, ListPrice
FROM Product
ORDER BY Color;
-- Tri descendant avec multi-colonnes
SELECT Product, Subcategory, Color, ListPrice
FROM Product
ORDER BY Color, ListPrice DESC, Category;
Bad practice: Using ordinal positions (
ORDER BY 3, 4 DESC) — it’s fragile and difficult to read.
TOP with ORDER BY
-- Top 10 produits les plus chers de couleur noire
SELECT TOP 10 Product, Subcategory, Color, ListPrice
FROM Product
ORDER BY Color, ListPrice DESC, Category;
-- WITH TIES : inclut les ex-æquos
SELECT TOP 10 WITH TIES Product, Subcategory, Color, ListPrice
FROM Product
ORDER BY Color, ListPrice DESC, Category;
WITH TIESguarantees that all products with the same price as the last one included in the TOP are also returned. This can produce more than 10 lines.
OFFSET FETCH: paging results
-- Ignorer les 5 premières lignes, puis récupérer les 5 suivantes
SELECT Product, Subcategory, Color, ListPrice
FROM Product
ORDER BY Color, ListPrice DESC, Category
OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY;
OFFSET FETCHrequires anORDER BYclause. This is the ANSI standard method for pagination.
Special feature: Alias in ORDER BY
ORDER BY is the only clause where aliases defined in SELECT can be used, because it is executed after SELECT.
3.5 Module Summary
| Concept | Clause | Role |
|---|---|---|
| Aggregate | COUNT, SUM, AVG, MAX, MIN | Calculate statistics on a set |
| Group | GROUP BY | Create subsets by common values |
| Filter groups | HAVING | Filter after grouping |
| Sort | ORDER BY | Control display order |
| Limit sorted | TOP, OFFSET FETCH | Retrieve a sorted subset |
4. Joining Data
4.1 Overview of joins
Joins allow you to create a complete view by combining data from different tables.
Database normalization
Relational databases use normalization to minimize duplication:
- Customer information and orders are in separate tables
- Tables are linked by relationships (e.g.: 1:N between
CustomerandOrders)
Logical processing phases of a join
During each join, three logical processing phases are applied:
- Cartesian product — Each row in table A is combined with each row in table B
- Filtering by join predicate — Only rows satisfying the
ONcondition are kept - Adding outer rows — For outer joins, unmatched rows are added with
NULL
The number of phases executed determines the join type obtained.
4.2 Cross Join
The Cross Join only executes phase 1 (Cartesian product). Each row in table A is combined with each row in table B.
Table A (2 lignes) × Table B (3 lignes) = 6 lignes résultantes
SQL-92 syntax (recommended)
SELECT CustomerKey, ProductKey
FROM Customer
CROSS JOIN Product;
SQL-89 Syntax (legacy)
SELECT CustomerKey, ProductKey
FROM Customer, Product;
Both syntaxes produce the same result. SQL-92 syntax is preferred because it is less error prone.
Use cases
- Generate number tables
- Create large volumes of data for testing
- Limited use in common analyzes
Joining
CustomerandProductin AdventureWorks produces over 7 million rows.
4.3 Inner Join
The Inner Join executes phases 1 and 2: Cartesian product followed by filtering. It only returns rows with matches in both tables.
Venn diagram representation
[ Customer ]
( intersection )
[ Orders ]
The Inner Join only returns the intersection: customers who have placed orders.
SQL-92 syntax (standard)
SELECT *
FROM Customer AS c
INNER JOIN Orders AS o
ON c.CustomerKey = o.CustomerKey;
SQL-89 syntax (old, predicate in WHERE)
SELECT *
FROM Customer AS c, Orders AS o
WHERE c.CustomerKey = o.CustomerKey;
Important points
INNERis optional:JOINalone is equivalent toINNER JOIN- The order of the tables in
FROMdoes not impact the result - It is possible to join more than two tables simultaneously
- The
ONcondition can use different logical operators (=,<>,>, etc.)
4.4 Demo: Inner Join
Inspect tables
-- Inspecter les données
SELECT TOP 5 * FROM Customer;
SELECT TOP 5 * FROM Orders;
Basic join
-- Effectuer l'inner join
SELECT *
FROM Customer AS c
INNER JOIN Orders AS o
ON c.CustomerKey = o.CustomerKey;
Without aliases, an ambiguity error occurs for columns with the same name in both tables (
CustomerKey). Thecandoaliases solve this problem.
Aggregation after join
-- Nombre de commandes et montant par client
SELECT c.CustomerKey, c.FirstName,
COUNT(o.OrderKey) AS Orders,
SUM(o.SalesAmount) AS Amount
FROM Customer AS c
INNER JOIN Orders AS o
ON c.CustomerKey = o.CustomerKey
GROUP BY c.CustomerKey, c.FirstName
ORDER BY Orders DESC;
The customer with ID 11566 (April) has placed the most orders: 25 orders.
4.5 Outer Joins (LEFT, RIGHT, FULL)
The Outer Joins execute the 3 phases of logical processing. In addition to common rows, they include unmatched rows from the preserved table(s).
Types of Outer Joins
| Type | Preserved table | Returned rows |
|---|---|---|
LEFT OUTER JOIN | Left table | All lines left + right matches |
RIGHT OUTER JOIN | Right table | All lines right + left matches |
FULL OUTER JOIN | The two tables | All rows from both tables |
The
OUTERkeyword is optional.LEFT JOIN=LEFT OUTER JOIN.
Example: Customers without orders
-- Liste des clients qui n'ont passé aucune commande
SELECT c.CustomerKey, c.FirstName, o.OrderKey, o.SalesAmount
FROM Customer AS c
LEFT OUTER JOIN Orders AS o
ON c.CustomerKey = o.CustomerKey
WHERE o.OrderKey IS NULL;
Comparison COUNT(*) vs COUNT(column)
-- Impact du COUNT avec les outer joins
SELECT c.CustomerKey, c.FirstName,
COUNT(*) AS Count_All_Rows,
COUNT(o.OrderKey) AS Count_Orders
FROM Customer AS c
LEFT OUTER JOIN Orders AS o
ON c.CustomerKey = o.CustomerKey
WHERE o.OrderKey IS NULL
GROUP BY c.CustomerKey, c.FirstName;
COUNT(*)includes NULL rows (returns 1 per customer with no orders), whileCOUNT(o.OrderKey)returns 0 becauseOrderKeyis NULL for customers with no orders.
4.6 Self Join
A Self Join consists of joining a table with itself. It supports all types of joins (CROSS, INNER, OUTER).
Mandatory rule
Table aliasing is mandatory in a self join to distinguish the two instances of the same table.
Typical use case: Employee/manager hierarchy
-- Liste des employés avec le nom de leur manager
SELECT e1.FirstName, e1.LastName, e2.FirstName, e2.LastName
FROM Employee e1
LEFT OUTER JOIN Employee e2
ON e1.EmployeeKey = e2.ManagerKey;
The
Employeetable stores both employees and their managers (ManagerKey). The left outer join includes employees without a manager.
4.7 Module Summary
| Join Type | Executed phases | Returned rows |
|---|---|---|
CROSS JOIN | Cartesian only | All combinations |
INNER JOIN | Cartesian + filtering | Common lines only |
LEFT OUTER JOIN | The 3 phases | All left lines + matches |
RIGHT OUTER JOIN | The 3 phases | All straight lines + matches |
FULL OUTER JOIN | The 3 phases | All rows from both tables |
SELF JOIN | Depends on type | Same table joined with itself |
5. Cleaning Data
5.1 Text manipulation functions
Data quality is an essential prerequisite for any reliable analysis. A quality dataset has several characteristics:
| Characteristic | Description |
|---|---|
| Completeness | No critical missing values |
| Consistency | Same format for the same data |
| Accuracy | Correct and precise data |
| Uniqueness | No duplicates |
| Standardization | Uniform formats |
| Relevance | Data useful for analysis |
Common Data Quality Issues
- Duplicates: same record present several times
- Inconsistent data: typos, upper/lower case, unnecessary spaces
- Incorrect data: wrong data types
- Missing data: NULL or absent values
T-SQL provides built-in functions to improve the appearance of data in results without changing the underlying data.
String manipulation functions
| Function | Syntax | Description |
|---|---|---|
TRIM | TRIM([chars FROM] str) | Remove spaces/characters at start and end |
LTRIM | LTRIM(str [, chars]) | Delete at start (left) |
RTRIM | RTRIM(str [, chars]) | Delete at the end (right) |
UPPER | UPPER(str) | Converts to uppercase |
LOWER | LOWER(str) | Converts to lowercase |
REPLACE | REPLACE(str, old, new) | Replaces a substring |
CONCAT | CONCAT(str1, str2, ...) | String concatenation |
CONCAT_WS | CONCAT_WS(sep, str1, str2, ...) | Concatenation with separator |
LEFT | LEFT(str, n) | Extract n characters from the left |
RIGHT | RIGHT(str, n) | Extract n characters from the right |
SUBSTRING | SUBSTRING(str, start, len) | Extract a substring |
LEN | LEN(str) | Chain Length |
PATINDEX | PATINDEX(pattern, str) | Position of the first occurrence of the pattern |
Wildcards for LIKE and PATINDEX
| Wildcard | Description |
|---|---|
% | Zero or more characters |
_ | Any single character |
[abc] | Any character from the list |
[^abc] | Any character not in the list |
5.2 Demo: Text manipulation functions
-- Nettoyage de données textuelles : codes pays et noms de pays
SELECT
UPPER(LTRIM(t.CountryCode)) AS CountryCode,
REPLACE(TRIM(BOTH '*' FROM t.CountryName), 'Cansda', 'Canada') AS CountryName,
COUNT(c.CustomerKey) AS Customers
FROM SalesTerritory AS t
JOIN Customer AS c
ON t.GeographyKey = c.GeographyKey
GROUP BY LTRIM(t.CountryCode), t.CountryName;
This script corrects: superfluous spaces (
LTRIM), upper/lower case inconsistency (UPPER), asterisks at the beginning and end (TRIM), and the spelling mistake “Cansda” → “Canada” (REPLACE).
-- Concaténation, extraction et recherche de motifs
SELECT
FirstName + ' ' + LastName AS FullName,
CONCAT(FirstName, ' ', LastName) AS FullNameConcat,
CONCAT_WS(' ', FirstName, LastName) AS FullNameConcatWS,
LEFT(LastName, 1) AS Initial,
RIGHT(LastName, 1) AS LastLetter,
SUBSTRING(LastName, 3, 2) AS Subset
FROM Customer
WHERE PATINDEX('Sam%', FirstName) > 0
AND PATINDEX('[wy]%', LastName) > 0;
PATINDEXreturns the position (1-based) of the first occurrence of the pattern. If the pattern is not found, returns 0.
5.3 Data types and conversion (Data Types & CAST)
Categories of data types in SQL Server
| Category | Kinds |
|---|---|
| Exact numeric | INT, BIGINT, SMALLINT, NUMERIC, DECIMAL, MONEY |
| Approximate numeric | FLOAT, REAL |
| Character | CHAR, VARCHAR, NCHAR, NVARCHAR, TEXT |
| Date/Time | DATE, TIME, DATETIME, DATETIME2 |
| Binary | BINARY, VARBINARY |
| Other | BIT, UNIQUEIDENTIFIER, XML |
Implicit vs explicit conversion
- Implicit conversion: SQL Server automatically converts according to data type precedence rules (e.g.:
'text' + 123→'texte123') - Explicit conversion: use of dedicated functions
Conversion functions
| Function | Behavior in case of failure |
|---|---|
CAST(expr AS type) | Throw an error |
CONVERT(type, expr) | Throw an error |
PARSE(str AS type) | Throw an error |
TRY_CAST(expr AS type) | Returns NULL |
TRY_CONVERT(type, expr) | Returns NULL |
TRY_PARSE(str AS type) | Returns NULL |
Example: Correct incorrect sorting due to data type
-- Sans CAST : tri alphabétique incorrect (800 > 2000 comme chaîne)
SELECT Product, StandardCost
FROM Product
WHERE Subcategory = 'Road Bikes'
ORDER BY StandardCost DESC;
-- Avec CAST : tri numérique correct
SELECT
Product,
CAST(StandardCost AS FLOAT) AS StandardCost
FROM Product
WHERE Subcategory = 'Road Bikes'
ORDER BY StandardCost DESC;
5.4 Handling NULL values
NULL represents absence of value and should not be confused with 0 or an empty string.
Identify NULLs
Due to ternary logic, we cannot use = NULL:
-- INCORRECT
WHERE Status = NULL
-- CORRECT
WHERE Status IS NULL
WHERE Status IS NOT NULL
Options to handle NULLs
- Exclude NULLs from analysis (with
IS NOT NULL) - Replace NULLs with other values
NULL handling functions
| Function | Syntax | Description |
|---|---|---|
ISNULL | ISNULL(expr, replacement_value) | Replaces NULL with the replacement value |
COALESCE | COALESCE(expr1, expr2, ...) | Returns the first non-NULL argument |
NULLIF | NULLIF(expr1, expr2) | Returns NULL if both arguments are equal |
5.5 Demo: ISNULL, COALESCE, NULLIF
-- ISNULL() : remplacer NULL par 'Past' dans le statut
SELECT TOP 10
Product,
EndDate,
Status,
ISNULL(Status, 'Past') AS Stage
FROM Product;
-- NULLIF() : remplacer 0 par NULL dans StartDate
SELECT
Product,
NULLIF(StartDate, 0) AS StartDate
FROM Product
WHERE StartDate = 0;
-- COALESCE() : utiliser le premier nom non-NULL disponible
SELECT TOP 10
p.ProductKey,
p.Product,
COALESCE(Product, Model, SKU) AS ProductName,
SUM(o.OrderQuantity) AS OrderQuantity
FROM OrderDetails o
LEFT JOIN Product p
ON o.ProductKey = p.ProductKey
GROUP BY p.ProductKey, p.Product, COALESCE(Product, Model, SKU)
ORDER BY OrderQuantity DESC;
COALESCEevaluates arguments from left to right and returns the first that is not NULL. If all are NULL, return NULL.
5.6 Module Summary
| Problem | T-SQL solution |
|---|---|
| Superfluous spaces | TRIM, LTRIM, RTRIM |
| Inconsistent case | UPPER, LOWER |
| Spelling mistakes | REPLACE |
| Unwanted characters | TRIM(BOTH 'char' FROM str) |
| Concatenation | CONCAT, CONCAT_WS, + |
| Substring Extraction | LEFT, RIGHT, SUBSTRING |
| Pattern Search | PATINDEX, LIKE |
| Wrong data type | CAST, CONVERT, TRY_CAST |
| NULL Values | ISNULL, COALESCE, NULLIF |
6. Subqueries and CTEs
6.1 Subqueries
A subquery is a query nested inside another query. It offers an alternative approach to combining data and performing intermediate manipulations.
Terminology
| Term | Description |
|---|---|
| Outer query | The main query whose results are returned |
| Inner query / Subquery | The internal query whose results are used by the external query |
| Self-contained subquery | Subquery independent of external query (executed only once) |
| Correlated subquery | Subquery that depends on the outer query (executed for each row) |
Classification by result type
| Type | Description | Valid location |
|---|---|---|
| Scalar subquery (single value) | Returns a single value | WHERE, SELECT, HAVING |
| Multi-valued subquery | Return multiple values | WHERE ... IN (...) |
| Table-valued subquery | Returns a rowset | Clause FROM |
self-contained subqueries are easier to debug because they can be executed independently.
6.2 Demo: Subqueries
Scalar subquery in WHERE
-- Filtrer les commandes dont la quantité dépasse la moyenne
-- Méthode statique (déconseillée)
SELECT OrderKey, Quantity
FROM Orders
WHERE Quantity > 2;
-- Méthode avec sous-requête (recommandée)
SELECT OrderKey, Quantity
FROM Orders
WHERE Quantity > (SELECT AVG(Quantity) FROM Orders)
AND CustomerKey IN (SELECT CustomerKey FROM Customer WHERE Occupation = 'Management')
ORDER BY OrderKey;
The
INoperator is required (and not=) when the subquery returns multiple values.
Correlated subquery for running total (Running Total)
-- Calcul du total cumulatif (running total) avec une sous-requête corrélée
SELECT
OrderKey,
Quantity,
(SELECT SUM(Quantity)
FROM Orders AS o2
WHERE o2.OrderKey <= o1.OrderKey
AND Quantity > (SELECT AVG(Quantity) FROM Orders)
AND CustomerKey IN (SELECT CustomerKey FROM Customer WHERE Occupation = 'Management')) AS RunningTotal
FROM Orders AS o1
WHERE Quantity > (SELECT AVG(Quantity) FROM Orders)
AND CustomerKey IN (SELECT CustomerKey FROM Customer WHERE Occupation = 'Management')
ORDER BY OrderKey;
Important: In a correlated subquery, all filters in the external query must be repeated in the subquery to work on the same data set.
6.3 Common Table Expressions
CTEs (Common Table Expressions) create a virtual temporary table defined before the main query with the WITH clause.
Advantages of CTEs vs subqueries
| Appearance | Subquery | CTE |
|---|---|---|
| Readability | Nested code, less readable | Modular code, more readable |
| Reusability | Must be repeated | May be referenced multiple times |
| Debugging | More difficult | Easy (test CTE independently) |
| Performance | May be suboptimal | Executed once, stored in memory |
CTE Rules
- Sorting impossible: the result of a CTE cannot be sorted (
ORDER BYprohibited in the CTE) - Unique column names: each column must have a distinct name
- No nesting: a CTE cannot contain other CTEs
- Multiple CTEs: several CTEs can be declared with a single
WITH, separated by commas - Cross-referencing: CTEs can reference each other
Syntax
WITH NomDuCTE AS
(
-- Définition de la requête CTE
SELECT ...
FROM ...
),
DeuxiemeCTE AS
(
SELECT ...
FROM ...
)
-- Requête principale qui utilise les CTEs
SELECT ...
FROM NomDuCTE
LEFT JOIN DeuxiemeCTE ON ...;
6.4 Demo: CTEs
-- CTE pour calculer le pourcentage des ventes par client par rapport au total de l'employé
WITH EmployeeSales AS
(
SELECT EmployeeKey, SUM(SalesAmount) AS TotalEmployeeSales
FROM Orders
GROUP BY EmployeeKey
),
EmployeeCustomerSales AS
(
SELECT EmployeeKey, CustomerKey, SUM(SalesAmount) AS EmployeeCustomerSales
FROM Orders
GROUP BY EmployeeKey, CustomerKey
)
SELECT
cte1.EmployeeKey,
CustomerKey,
FORMAT(EmployeeCustomerSales, 'N0') AS CustomerSales,
FORMAT(TotalEmployeeSales, 'N0') AS TotalEmployeeSales,
FORMAT(EmployeeCustomerSales / TotalEmployeeSales, 'P2') AS PrctSales
FROM EmployeeCustomerSales AS cte1
LEFT JOIN EmployeeSales AS cte2
ON cte1.EmployeeKey = cte2.EmployeeKey
ORDER BY PrctSales DESC;
FORMAT(value, 'N0')formats with thousands separators, 0 decimal places.FORMAT(value, 'P2')formats as a percentage with 2 decimal places.
6.5 Pivoting and unpivoting of data (PIVOT / UNPIVOT)
PIVOT: Transform rows into columns
PIVOT summarizes data by transforming row values into columns. The three internal stages are:
- Data grouping
- Dispersion on the columns
- Aggregation of values
Syntax PIVOT
SELECT col1, [val1], [val2], [val3]
FROM source_table
PIVOT(
AGGREGATE_FUNCTION(valeur_column)
FOR colonne_à_pivoter IN ([val1], [val2], [val3])
) AS alias_table_pivotée;
If the target column names are irregular (start with a number, contain spaces), surround them with brackets
[].
UNPIVOT: Transform columns into rows
UNPIVOT performs the opposite operation: transforms column headers into row values.
UNPIVOT Syntax
SELECT col1, colonne_valeurs, colonne_noms
FROM source_table
UNPIVOT(
colonne_valeurs FOR colonne_noms IN ([col1], [col2], [col3])
) AS alias;
PIVOTandUNPIVOTare operators specific to T-SQL (not available in all SQL dialects).
6.6 Demo: PIVOT and UNPIVOT
-- CTE de base : quantité par territoire et par année
WITH TerritoryYearQuantity AS
(
SELECT
o.SalesTerritoryKey,
d.CalendarYear,
SUM(Quantity) AS Quantity
FROM Orders o
LEFT JOIN Date d
ON o.OrderDateKey = d.DateKey
GROUP BY o.SalesTerritoryKey, d.CalendarYear
)
-- PIVOT : transformer les années en colonnes
SELECT
SalesTerritoryKey,
[2010], [2011], [2012], [2013], [2014]
FROM TerritoryYearQuantity
PIVOT(SUM(Quantity) FOR CalendarYear IN ([2010], [2011], [2012], [2013], [2014])) AS PivotedData;
-- UNPIVOT : retransformer les colonnes d'années en lignes
WITH PivotedData AS
(
SELECT SalesTerritoryKey,
[2010], [2011], [2012], [2013], [2014]
FROM (
SELECT o.SalesTerritoryKey, d.CalendarYear, SUM(Quantity) AS Quantity
FROM Orders o
LEFT JOIN Date d ON o.OrderDateKey = d.DateKey
GROUP BY o.SalesTerritoryKey, d.CalendarYear
) AS SourceTable
PIVOT (SUM(Quantity) FOR CalendarYear IN ([2010], [2011], [2012], [2013], [2014])) AS PivotTable
)
SELECT *
FROM PivotedData
UNPIVOT(Quantity FOR CalendarYear IN ([2010], [2011], [2012], [2013], [2014])) AS unpvt;
6.7 Module Summary
| Concept | Utility | Key advantage |
|---|---|---|
| Self-contained subquery | Independent intermediate calculations | Easy to test/debug |
| Correlated subquery | Calculations dependent on each external line | Access to external query data |
| CTE | Temporary tables for complex queries | Readability, reusability, performance |
| PIVOT | Convert rows to columns | Tabular presentation of data |
| UNPIVOT | Convert columns to rows | Normalization of rotated data |
7. Window Functions
7.1 Introduction to Window Functions
Window Functions allow you to perform calculations on a set of lines (window) without losing the individual detail of each line — unlike GROUP BY which aggregates and makes the detail disappear.
GROUP BY limitation
With GROUP BY, it is impossible to display simultaneously:
- Detailed values of each command
- Total per customer
- The grand total
Solution with Window Functions
-- Sans window functions : complex et redondant
-- Avec window functions : simple et efficace
SELECT CustomerKey, OrderKey, SalesAmount,
SUM(SalesAmount) OVER() AS GrandTotal,
SUM(SalesAmount) OVER(PARTITION BY CustomerKey) AS CustomerTotal
FROM Orders;
Advantages of Window Functions
- Simpler code and easy to debug
- More efficient than subqueries (initial window = full query result)
- Detail preserved: each line remains visible
Categories of Window Functions in SQL Server
| Category | Functions | Description |
|---|---|---|
| Aggregate | SUM, COUNT, AVG, MAX, MIN | Aggregation calculations with window |
| Ranking | ROW_NUMBER, RANK, DENSE_RANK, NTILE | Line classification |
| Offset | LAG, LEAD, FIRST_VALUE, LAST_VALUE | Accessing values from other rows |
| Statistical | PERCENTILE_CONT, PERCENTILE_DISC | Statistical functions |
Window Functions are executed after all clauses except
ORDER BY. They cannot therefore be used directly inWHERE— you must use a CTE or a subquery.
7.2 The OVER clause
The OVER clause is the essential component of Window Functions. It defines the window specification.
Three elements of the OVER clause
fonction() OVER(
[PARTITION BY colonne(s)] -- 1. Partitionnement
[ORDER BY colonne(s)] -- 2. Ordonnancement
[ROWS/RANGE BETWEEN ...] -- 3. Cadrage (Framing)
)
1. PARTITION BY — Window partitioning
Divides the result into distinct partitions. The function is applied independently in each partition.
-- Fenêtre = toutes les lignes avec le même CustomerKey
SUM(SalesAmount) OVER(PARTITION BY CustomerKey)
-- Fenêtre = tout le résultat (OVER vide)
SUM(SalesAmount) OVER()
2. ORDER BY — Order in the window
Establishes the logical order of rows within each partition. Required for ranking functions and framing.
3. ROWS BETWEEN — Window framing
Refines the score lines by setting start and end points.
| Delimiter | Description |
|---|---|
UNBOUNDED PRECEDING | From the beginning of the score |
N PRECEDING | N lines before the current line |
CURRENT ROW | The current line |
N FOLLOWING | N lines after the current line |
UNBOUNDED FOLLOWING | Until the end of the score |
-- Total cumulatif : depuis le début jusqu'à la ligne courante
SUM(SalesAmount) OVER(
PARTITION BY CustomerKey
ORDER BY OrderKey
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)
WINDOW clause (reuse)
To avoid repeating the same window specification, the WINDOW clause allows you to give it a name:
SELECT ProductKey, Product, Category, ListPrice,
ROW_NUMBER() OVER W1 AS RowNumber,
RANK() OVER W1 AS Rnk,
DENSE_RANK() OVER W1 AS Dense_Rnk
FROM Product
WINDOW W1 AS (PARTITION BY Category ORDER BY ListPrice DESC)
7.3 Ranking Functions
Ordering functions require an ORDER BY in the OVER clause.
Comparison of the four functions
Example with products sorted by descending price (price: 100, 100, 80, 70, 70):
| Line | Price | ROW_NUMBER | RANK | DENSE_RANK | NTILE(2) |
|---|---|---|---|---|---|
| 1 | 100 | 1 | 1 | 1 | 1 |
| 2 | 100 | 2 | 1 | 1 | 1 |
| 3 | 80 | 3 | 3 | 2 | 1 |
| 4 | 70 | 4 | 4 | 3 | 2 |
| 5 | 70 | 5 | 4 | 3 | 2 |
ROW_NUMBER()
- Assign a unique number to each line, sequential, with no ties
- If the scheduling values are not unique → result non-deterministic
- For a deterministic result, add an additional column in
ORDER BY
RANK()
- Assigns the same rank to ties
- Next row skips occupied positions (
1, 1, 3, 4, 4, 6) RANK= number of lower values + 1
DENSE_RANK()
- Assigns the same rank to ties
- No skip in numbering (
1, 1, 2, 3, 3, 4) DENSE_RANK= number of lower distinct values + 1
NTILE(n)
- Divides lines into n groups numbered 1 to n
- If the number of rows is not divisible by n, the first tiles receive an extra row
7.4 Demo: ROW_NUMBER()
-- Créer une liste avec le premier ordre de chaque client (via CTE)
WITH OrdersNumbered AS
(
SELECT CustomerKey, OrderKey, SalesAmount,
ROW_NUMBER() OVER(PARTITION BY CustomerKey ORDER BY SalesAmount) AS Row_Number
FROM Orders
)
-- Premier ordre de chaque client
SELECT * FROM OrdersNumbered WHERE Row_Number = 1;
-- Deuxième ordre de chaque client
SELECT * FROM OrdersNumbered WHERE Row_Number = 2;
-- Lignes entre les positions 5 et 10 (pagination)
SELECT * FROM OrdersNumbered WHERE Row_Number BETWEEN 5 AND 10;
Important:
ROW_NUMBERcannot be filtered directly inWHEREbecause Window Functions execute inSELECT. You must use a CTE or a subquery to then filter.
7.5 Demo: RANK() and DENSE_RANK()
-- Top 3 des produits les plus chers par catégorie (en tenant compte des ex-æquos)
WITH cte AS
(
SELECT ProductKey, Product, Category, ListPrice,
ROW_NUMBER() OVER W1 AS RowNumber,
RANK() OVER W1 AS Rnk,
DENSE_RANK() OVER W1 AS Dense_Rnk
FROM Product
WINDOW W1 AS (PARTITION BY Category ORDER BY ListPrice DESC)
)
SELECT *
FROM cte
WHERE Dense_Rnk <= 3;
Using
DENSE_RANK <= 3ensures that all products in the top 3 distinct prices are included, even if multiple products share the same price.
7.6 Demo: NTILE()
-- Diviser les commandes d'un employé en 9 groupes selon le montant
SELECT
OrderKey,
SalesAmount,
NTILE(9) OVER(ORDER BY EmployeeKey) AS Groups
FROM Orders
WHERE EmployeeKey = 292;
For employee 292 with 902 orders and 9 tiles requested, the first 2 groups contain 101 rows instead of 100 (
902 = 9×100 + 2, so the first 2 tiles receive an extra row).
7.7 Aggregation functions with windowing
Window Aggregate Functions are the same functions as grouping aggregates (SUM, COUNT, AVG, MAX, MIN), but used with OVER instead of GROUP BY.
Key Difference
| Appearance | Group Aggregate (GROUP BY) | Window Aggregate (OVER) |
|---|---|---|
| Window Definition | GROUP BY clause | OVER clause |
| Results | One line per group | All individual lines preserved |
| Individual detail | Lost | Preserved |
Support for OVER elements
Window Aggregate Functions support:
PARTITION BY: restricts the window to identical linesORDER BY: used for framing- Framing (
ROWS BETWEEN): determines the start and end points
Aggregate nesting
It is possible to nest a group aggregate function in a Window Function:
-- Agrégat de groupe : total par client
-- Window function : grand total de tous les clients
SELECT CustomerKey,
SUM(SalesAmount) AS CustomerTotal, -- Group aggregate
SUM(SUM(SalesAmount)) OVER() AS GrandTotal -- Window aggregate sur les groupes
FROM Orders
GROUP BY CustomerKey;
7.8 Demo: Window Aggregate Functions
-- Prix moyen par sous-catégorie (PARTITION BY)
SELECT
ProductKey,
Product,
Subcategory,
ListPrice,
AVG(ListPrice) OVER(PARTITION BY Subcategory) AS AvgPrice
FROM Product
ORDER BY Subcategory DESC;
-- Grand total, total client et total cumulatif
SELECT CustomerKey, OrderKey, SalesAmount,
-- Grand total de toutes les commandes
SUM(SalesAmount) OVER() AS GrandTotal,
-- Total par client
SUM(SalesAmount) OVER(PARTITION BY CustomerKey) AS CustomerGrandTotal,
-- Total cumulatif par client (running total)
SUM(SalesAmount) OVER(
PARTITION BY CustomerKey
ORDER BY OrderKey
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS CustomerRunningTotal,
-- Valeur de la commande précédente
MAX(SalesAmount) OVER(
PARTITION BY CustomerKey
ORDER BY OrderKey
ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING
) AS PreviousValue,
-- Valeur de la commande suivante
MAX(SalesAmount) OVER(
PARTITION BY CustomerKey
ORDER BY OrderKey
ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING
) AS NextValue
FROM Orders
ORDER BY CustomerKey, OrderKey;
OVER()empty → window = all query result (grand total)OVER(PARTITION BY CustomerKey)→ window = all orders from the same customerROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW→ cumulative total since the first row of the customer
7.9 Offset Functions
Offset Functions return line values located at a defined distance from the current line in the window.
LAG and LEAD
| Function | Management | Description |
|---|---|---|
LAG(expr, offset, default) | Back | Line value N positions forward |
LEAD(expr, offset, default) | Forward | Line value N positions after |
- Offset: number of lines before/after (default: 1)
- Default: value returned if the requested line does not exist (default: NULL)
FIRST_VALUE and LAST_VALUE
| Function | Description | Recommended framing |
|---|---|---|
FIRST_VALUE(expr) | First window value | ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW |
LAST_VALUE(expr) | Last Window Value | ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING |
Note: Without explicit framing,
LAST_VALUEuses by defaultROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, which returns the current value rather than the last one. You must always specify the framing forLAST_VALUE.
7.10 Demo: LAG, LEAD, FIRST_VALUE, LAST_VALUE
-- Valeurs précédente, suivante, première et dernière par client
SELECT
CustomerKey,
OrderKey,
SalesAmount AS CurrentValue,
-- 2 lignes avant la ligne courante, 0 si absent
LAG(SalesAmount, 2, 0) OVER(PARTITION BY CustomerKey ORDER BY OrderKey) AS PreviousValue,
-- 1 ligne après la ligne courante (offset par défaut = 1)
LEAD(SalesAmount) OVER(PARTITION BY CustomerKey ORDER BY OrderKey) AS NextValue,
-- Première valeur dans la partition du client
FIRST_VALUE(SalesAmount) OVER(
PARTITION BY CustomerKey
ORDER BY OrderKey
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS FirstValue,
-- Dernière valeur dans la partition du client
LAST_VALUE(SalesAmount) OVER(
PARTITION BY CustomerKey
ORDER BY OrderKey
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
) AS LastValue
FROM Orders
WHERE CustomerKey IN ('11566', '11242')
ORDER BY CustomerKey, OrderKey;
LAGproducesNULLfor the first line (no previous line)LEADproducesNULLfor last line (no next line)- Specify an offset > 1 to access more distant lines
7.11 Module Summary
| Function | Category | Main role |
|---|---|---|
ROW_NUMBER() | Ranking | Unique and sequential numbering |
RANK() | Ranking | Ranking with jumps for ties |
DENSE_RANK() | Ranking | Ranking without jumps for ties |
NTILE(n) | Ranking | Division into n balanced groups |
SUM() OVER(...) | Aggregate | Running sums, totals, running totals |
AVG() OVER(...) | Aggregate | Averages per window |
MAX/MIN() OVER(...) | Aggregate | Max/min values in window |
LAG() | Offset | Value of a previous row |
LEAD() | Offset | Value of next line |
FIRST_VALUE() | Offset | First value in window |
LAST_VALUE() | Offset | Last value in window |
8. Demo files
Module 02 — Selecting Data
| File | Description |
|---|---|
| 02/demos/DemoFiles/SelectingDataScript.sql | SELECT, TOP, DISTINCT, aliasing |
| 02/demos/DemoFiles/FitleringDataScript.sql | WHERE, LIKE, BETWEEN, IN, AND, OR |
Module 03 — Aggregating Data
| File | Description |
|---|---|
| 03/demos/DemoFiles/GroupByScript.sql | GROUP BY with COUNT, MAX, MIN, AVG, FORMAT |
| 03/demos/DemoFiles/HavingScript.sql | HAVING to filter groups |
| 03/demos/DemoFiles/OrderByScript.sql | ORDER BY, TOP WITH TIES, OFFSET FETCH |
Module 04 — Joining Data
| File | Description |
|---|---|
| 04/demos/DemoFiles/InnerJoinScript.sql | INNER JOIN with aggregation |
| 04/demos/DemoFiles/OuterJoinScript.sql | LEFT OUTER JOIN, NULL check |
| 04/demos/DemoFiles/CrossJoinScript.sql | CROSS JOIN (Cartesian product) |
| 04/demos/DemoFiles/SelfJoinScript.sql | SELF JOIN for employee/manager hierarchy |
Module 05 — Cleaning Data
| File | Description |
|---|---|
| 05/demos/DemoFiles/WorkingWithTextScript.sql | TRIM, UPPER, REPLACE, CONCAT, LEFT, RIGHT, SUBSTRING, PATINDEX |
| 05/demos/DemoFiles/CastScript.sql | CAST for type conversion |
| 05/demos/DemoFiles/HandlingNULLsScript.sql | ISNULL, NULLIF, COALESCE |
Module 06 — Subqueries and CTEs
| File | Description |
|---|---|
| 06/demos/DemoFiles/Subqueries.sql | Scalar, multi-valued and correlated subqueries |
| 06/demos/DemoFiles/CTEs.sql | Multiple CTEs with join and FORMAT |
| 06/demos/DemoFiles/PivotingData.sql | PIVOT with basic CTE |
| 06/demos/DemoFiles/UnpivotingData.sql | UNPIVOT on pivoted data |
Module 07 — Window Functions
| File | Description |
|---|---|
| 07/demos/DemoFiles/ROW_Number.sql | ROW_NUMBER with PARTITION BY and filtering via CTE |
| 07/demos/DemoFiles/RANK.sql | RANK, DENSE_RANK with named WINDOW clause |
| 07/demos/DemoFiles/NTILE.sql | NTILE to divide into groups |
| 07/demos/DemoFiles/AggregateFunctions.sql | SUM OVER with grand total, running total, previous/next |
| 07/demos/DemoFiles/OffsetFunctions.sql | LAG, LEAD, FIRST_VALUE, LAST_VALUE |
9. Prerequisites and environment
Required software
| Software | Role |
|---|---|
| Microsoft SQL Server | Database Engine |
| SQL Server Management Studio (SSMS) | Graphical interface for writing and executing T-SQL queries |
Restoring the demo database
The AdventureWorksDW database is provided as a AdventureWorksDW.bak file.
Restore steps in SSMS:
- Open SSMS and connect to SQL server
- In the Object Explorer, right click on Databases
- Select Restore Database…
- In the restore window, choose Device as the source
- Add the
AdventureWorksDW.bakfile via the … button - Click OK to start the restoration
Useful SSMS Shortcuts
| Shortcut | Action |
|---|---|
F5 | Run query (or selection) |
Ctrl+E | Run query |
Ctrl+K, Ctrl+C | Comment on the selection |
Ctrl+K, Ctrl+U | Uncomment the selection |
Connection to server
- Server name is often auto-populated for a local instance
- Authentication: Windows Authentication for a local instance, or SQL Server Authentication for a remote instance
Search Terms
querying · data · sql · server · microsoft · databases · functions · window · syntax · join · columns · order · subqueries · ctes · database · pivot · unpivot · aggregate · aggregation · categories · clause · comparison · conversion · group