name: inverse
layout: true
class: center, middle, inverse
.indexlink[[<i class="fa fa-arrow-circle-up"></i>](#) [<i class="fa fa-list-ul"></i>](#index) <a href="#" class="color"><i class="fa fa-tint"></i></a>]
---
name: top
layout: true
class: top, left
.indexlink[[<i class="fa fa-arrow-circle-up"></i>](#) [<i class="fa fa-list-ul"></i>](#index) <a href="#" class="color"><i class="fa fa-tint"></i></a>]
---
name: normal
layout: true
class: left, middle
.indexlink[[<i class="fa fa-arrow-circle-up"></i>](#) [<i class="fa fa-list-ul"></i>](#index) <a href="#" class="color"><i class="fa fa-tint"></i></a>]
---
template: inverse
# SQL
## Data Query Language
Gonçalo Gonçalves
[gflcg@fe.up.pt](mailto:gflcg@fe.up.pt)
.footnote[-- original slides by <a target=_blank href="http://www.fe.up.pt/~arestivo">André Restivo</a>]
---
name: index
template: inverse
# Index
.index[
<!-- .indexpill[[Introduction](#intro)] -->
.indexpill[[Selecting Data](#select)]
.indexpill[[Choosing Columns](#columns)]
.indexpill[[Filtering Rows](#rows)]
.indexpill[[Set Operators](#set)]
.indexpill[[Joining Tables](#joining)]
.indexpill[[Aggregating Data](#aggregating)]
.indexpill[[Sorting Rows](#sorting)]
.indexpill[[Limiting Data](#limiting)]
.indexpill[[Text Operators](#text)]
.indexpill[[Nested Queries](#nested)]
]
---
template: inverse
name: select
# Selecting Data
---
# SELECT and FROM
* **SELECT** and **FROM** are the most basic SQL query operators.
* They allow us to specify which tables (FROM) and columns (SELECT) we want to retrieve from the database.
* The result of an SQL query is also a table.
---
# Selecting all columns
To select all columns from a table we can use an *
```sql
SELECT * FROM Employee;
```
.sqltable[
|id|name|salary|taxes|dep_num
|-|-|
|1 | John Doe | 1000 | 200 | 1
|2 | Jane Doe | 800 | 100 | 2
|3 | John Smith | 1200 | 350 | 2
|4 | Jane Roe | 1000 | 200 | 3
|5 | Richard Roe | 900 | 0 | NULL
]
.box_info[Selects all columns from table employee]
---
template: inverse
name: columns
# Choosing columns
---
# Choosing columns
We can select only some columns
```sql
SELECT id, name FROM Employee;
```
.sqltable[
|id|name
|-|-|
|1 | John Doe |
|2 | Jane Doe |
|3 | John Smith |
|4 | Jane Roe |
|5 | Richard Roe |
]
.box_info[Selects columns id and name from table employee]
---
# Column operations
We can also perform any operations between columns
```sql
SELECT id, name, salary - taxes FROM Employee;
```
.sqltable[
|id|name|salary - taxes
|-|-|
|1 | John Doe | 800
|2 | Jane Doe | 700
|3 | John Smith | 850
|4 | Jane Roe | 800
|5 | Richard Roe | 900
]
.box_info[Selects columns id, name and the difference between salary and taxes from table employee]
---
# Renaming columns
Any column can be renamed using the **AS** operator
```sql
SELECT id AS num, name, salary - taxes AS net_salary FROM Employee;
```
.sqltable[
|num|name|net_salary
|-|-|
|1 | John Doe | 800
|2 | Jane Doe | 700
|3 | John Smith | 850
|4 | Jane Roe | 800
|5 | Richard Roe | 900
]
.box_info[Renaming column id as num and the difference between salary and taxes to net_salary]
---
template: inverse
name: rows
# Filtering Rows
---
# WHERE
* The **WHERE** command allows us to filter which rows we want in our result table according to a condition.
* The condition can use any comparison operator (<, >, <=, <>, ...) and can be composed using AND, OR and NOT.
---
# Example
```sql
SELECT * FROM Employee WHERE dep_num = 2 OR salary <= 900;
```
.sqltable[
|id|name|salary|taxes|dep_num
|-|-|
|2 | Jane Doe | 800 | 100 | 2
|3 | John Smith | 1200 | 350 | 2
|5 | Richard Roe | 900 | 0 | NULL
]
.box_info[Employees from department 2 or a salary lower or equal to 900]
---
# Example
To test if a value is null, we have to use the special **IS NULL** operator.
```sql
SELECT * FROM Employee WHERE dep_num IS NULL;
```
.sqltable[
|id|name|salary|taxes|dep_num
|-|-|
|5 | Richard Roe | 900 | 0 | NULL
]
.box_info[Employees from department 2 or a salary lower or equal to 900]
Use **IS NOT NULL** to select rows where a certain attribute is not null.
---
# Removing duplicates
We can remove duplicates from the final result by using the DISTINCT operator
```sql
SELECT DISTINCT salary FROM Employee;
```
.sqltable[
|salary
|-|-|
|1000
|800
|1200
|900
]
.box_info[Selects the different salaries in the database]
---
template: inverse
name: set
# Set operators
---
# Set operators
Two tables are compatible for being used in a set operation if they have the same **number of columns** and the **type** of each column is **compatible**:
* The **UNION** between two tables (R1 and R2) is a table that includes all lines that are present in R1 or R2.
* The **INTERSECT**ion between two tables (R1 and R2) is a table that includes all lines that are present in R1 and R2.
* The difference (**EXCEPT**) between two tables (R1 and R2) is a table that includes all lines that are present in R1 but not in R2.
With all these operators, **duplicate** rows are **eliminated** automatically.
---
# Union, Intersection and Difference

---
# Example
```sql
SELECT * FROM Employee WHERE salary >= 1000;
```
.sqltable[
|id|name|salary|taxes|dep_num
|-|-|
|1 | John Doe | 1000 | 200 | 1
|3 | John Smith | 1200 | 350 | 2
|4 | Jane Roe | 1000 | 200 | 3
]
```sql
SELECT * FROM Employee WHERE dep_num = 2;
```
.sqltable[
|id|name|salary|taxes|dep_num
|-|-|
|2 | Jane Doe | 800 | 100 | 2
|3 | John Smith | 1200 | 350 | 2
]
---
# Union
```sql
SELECT * FROM Employee WHERE salary >= 1000;
UNION
SELECT * FROM Employee WHERE dep_num = 2;
```
.sqltable[
|id|name|salary|taxes|dep_num
|-|-|
|1 | John Doe | 1000 | 200 | 1
|2 | Jane Doe | 800 | 100 | 2
|3 | John Smith | 1200 | 350 | 2
|4 | Jane Roe | 1000 | 200 | 3
]
.box_info[Employees that have a salary larger or equal to 1000 or work on department 2]
---
# Intersection
```sql
SELECT * FROM Employee WHERE salary >= 1000;
INTERSECT
SELECT * FROM Employee WHERE dep_num = 2;
```
.sqltable[
|id|name|salary|taxes|dep_num
|-|-|
|3 | John Smith | 1200 | 350 | 2
]
.box_info[Employees that have a salary larger or equal to 1000 and work on department 2]
---
# Difference
```sql
SELECT * FROM Employee WHERE salary >= 1000;
EXCEPT
SELECT * FROM Employee WHERE dep_num = 2;
```
.sqltable[
|id|name|salary|taxes|dep_num
|-|-|
|1 | John Doe | 1000 | 200 | 1
|4 | Jane Roe | 1000 | 200 | 3
]
.box_info[Employees that have a salary larger or equal to 1000 and do not work on department 2]
---
template: inverse
name: joining
# Joining Tables
---
# Cartesian product
* The cartesian product allows us to combine rows from **different tables**.
* To use it, we just have to indicate which tables we want to combine using commas to separate them.
* The result is a table containing the columns of all tables and **all possible combinations** of rows.
* Also known as **CROSS JOIN**.
---
# Example
```sql
SELECT * FROM Employee;
```
.smaller.sqltable[
|id|name|salary|taxes|dep_num
|-|-|
|1 | John Doe | 1000 | 200 | 1
|2 | Jane Doe | 800 | 100 | 2
|3 | John Smith | 1200 | 350 | 2
|4 | Jane Roe | 1000 | 200 | 3
|5 | Richard Roe | 900 | 0 | NULL
]
```sql
SELECT * FROM Department;
```
.smaller.sqltable[
|num|name
|-|-|
|1 | Marketing
|2 | Sales
|3 | Production
]
---
.no-top-margin[
# Cartesian product
]
.small-code[
```sql
SELECT * FROM Employee, Department;
-- or
SELECT * FROM Employee CROSS JOIN Department;
```
]
.smaller.sqltable[
| id | name | salary | taxes | dep_num | num | name |
|----|-------------|--------|-------|---------|-----|------------|
| 1 | John Doe | 1000 | 200 | 1 | 1 | Marketing |
| 2 | Jane Doe | 800 | 100 | 2 | 1 | Marketing |
| 3 | John Smith | 1200 | 350 | 2 | 1 | Marketing |
| 4 | Jane Roe | 1000 | 200 | 3 | 1 | Marketing |
| 5 | Richard Roe | 900 | 0 | NULL | 1 | Marketing |
| 1 | John Doe | 1000 | 200 | 1 | 2 | Sales |
| 2 | Jane Doe | 800 | 100 | 2 | 2 | Sales |
| 3 | John Smith | 1200 | 350 | 2 | 2 | Sales |
| 4 | Jane Roe | 1000 | 200 | 3 | 2 | Sales |
| 5 | Richard Roe | 900 | 0 | NULL | 2 | Sales |
| 1 | John Doe | 1000 | 200 | 1 | 3 | Production |
| 2 | Jane Doe | 800 | 100 | 2 | 3 | Production |
| 3 | John Smith | 1200 | 350 | 2 | 3 | Production |
| 4 | Jane Roe | 1000 | 200 | 3 | 3 | Production |
| 5 | Richard Roe | 900 | 0 | NULL | 3 | Production |
]
---
template: top
# Joining using WHERE
```sql
SELECT * FROM Employee, Department WHERE dep_num = num;
```
.smaller.sqltable[
| id | name | salary | taxes | dep_num | num | name
|-------|-----------------|----------|---------|----------|------|---------------
| 1 | John Doe | 1000 | 200 | 1 | 1 | Marketing
| ~~2~~ | ~~Jane Doe~~ | ~~800~~ | ~~100~~ | ~~2~~ |~~1~~ | ~~Marketing~~
| ~~3~~ | ~~John Smith~~ | ~~1200~~ | ~~350~~ | ~~2~~ |~~1~~ | ~~Marketing~~
| ~~4~~ | ~~Jane Roe~~ | ~~1000~~ | ~~200~~ | ~~3~~ |~~1~~ | ~~Marketing~~
| ~~5~~ | ~~Richard Roe~~ | ~~900~~ | ~~0~~ | ~~NULL~~ |~~1~~ | ~~Marketing~~
| ~~1~~ | ~~John Doe~~ | ~~1000~~ | ~~200~~ | ~~1~~ |~~2~~ | ~~Sales~~
| 2 | Jane Doe | 800 | 100 | 2 | 2 | Sales
| 3 | John Smith | 1200 | 350 | 2 | 2 | Sales
| ~~4~~ | ~~Jane Roe~~ | ~~1000~~ | ~~200~~ | ~~3~~ |~~2~~ | ~~Sales~~
| ~~5~~ | ~~Richard Roe~~ | ~~900~~ | ~~0~~ | ~~NULL~~ |~~2~~ | ~~Sales~~
| ~~1~~ | ~~John Doe~~ | ~~1000~~ | ~~200~~ | ~~1~~ |~~3~~ | ~~Production~~
| ~~2~~ | ~~Jane Doe~~ | ~~800~~ | ~~100~~ | ~~2~~ |~~3~~ | ~~Production~~
| ~~3~~ | ~~John Smith~~ | ~~1200~~ | ~~350~~ | ~~2~~ |~~3~~ | ~~Production~~
| 4 | Jane Roe | 1000 | 200 | 3 | 3 | Production
| ~~5~~ | ~~Richard Roe~~ | ~~900~~ | ~~0~~ | ~~NULL~~ |~~3~~ | ~~Production~~
]
---
template: top
# Joining using WHERE
```sql
SELECT * FROM Employee, Department WHERE dep_num = num;
```
.smaller.sqltable[
|id|name|salary|taxes|dep_num|num|name
|-|-|
|1 | John Doe | 1000 | 200 | 1 |1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 |2 | Sales
|3 | John Smith | 1200 | 350 | 2 |2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 |3 | Production
]
.box_info[Employees and their departments]
---
template: top
name: ambiguities
.no-top-margin[
# Solving ambiguities
]
```sql
SELECT * FROM Employee, Department WHERE dep_num = num;
```
.smaller.sqltable[
|id|name|salary|taxes|dep_num|num|name
|-|-|
|1 | John Doe | 1000 | 200 | 1 |1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 |2 | Sales
|3 | John Smith | 1200 | 350 | 2 |2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 |3 | Production
]
When selecting from more than one table, columns with the same name might lead to ambiguities.
.flex[
```sql
SELECT id, name, name FROM Employee, Department WHERE dep_num = num;
```
❌
]
---
template: ambiguities
To solve them we must use the table name before the column name:
.flex[
```sql
SELECT id, Employee.name, Department.name
FROM Employee, Department
WHERE dep_num = num;
```
.smaller.sqltable[
|id|name|name
|-|-|
|1 | John Doe | Marketing
|2 | Jane Doe | Sales
|3 | John Smith | Sales
|4 | Jane Roe | Production
]
]
---
# Join using JOIN ... ON
* Instead of using a cartesian product followed by the WHERE keyword, we can use the more specific keywords: **JOIN ON**.
* These keywords allow us to specify simultaneously **which tables** to join and with which joining **condition**.
* Separates regular row filtering from joining conditions.
* Makes joining lots of tables **easier** to understand.
* These are also called *inner joins*.
```sql
SELECT * FROM Employee, Department WHERE dep_num = num;
```
Same as:
```sql
SELECT * FROM Employee JOIN Department ON dep_num = num;
```
---
# Join using JOIN ... ON
```sql
SELECT * FROM Employee JOIN Department ON dep_num = num;
```
.smaller.sqltable[
|id|name|salary|taxes|dep_num|num|name
|-|-|
|1 | John Doe | 1000 | 200 | 1 |1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 |2 | Sales
|3 | John Smith | 1200 | 350 | 2 |2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 |3 | Production
]
---
# Join using JOIN ... USING
If the columns used in the join operation have the same name, we can join them using in a simpler way with JOIN USING.
```sql
SELECT * FROM Employee;
```
.smaller.sqltable[
|id|name|salary|taxes|<span class="red">num</span>
|-|-|
|1 | John Doe | 1000 | 200 | 1
|2 | Jane Doe | 800 | 100 | 2
|3 | John Smith | 1200 | 350 | 2
|4 | Jane Roe | 1000 | 200 | 3
|5 | Richard Roe | 900 | 0 | NULL
]
```sql
SELECT * FROM Department;
```
.smaller.sqltable[
|<span class="red">num</span>|name
|-|-|
|1 | Marketing
|2 | Sales
|3 | Production
]
---
# Join using JOIN ... USING
```sql
SELECT * FROM Employee JOIN Department USING(num);
```
.smaller.sqltable[
|id|name|salary|taxes|<span class="red">num</span>|name
|-|-|
|1 | John Doe | 1000 | 200 | 1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 | Sales
|3 | John Smith | 1200 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
]
.box_info[Note that the columns used for the join operation are not repeated in the result.]
---
# Join LEFT, RIGHT and FULL
* Sometimes, when joining tables, some of the rows are left out as they do not match any rows on the other table.
* If we want these rows to be present in the result, we must use what is called and *outer join*.
* There are three types of *outer joins*:
* LEFT - Rows on the **left** table that do not match any row in the right table are kept.
* RIGHT - Rows on the **right** table that do not match any row in the left table are kept.
* FULL - Rows on any of the tables that do not match any row in the other table are kept.
---
# JOIN vs LEFT JOIN
```sql
SELECT * FROM Employee JOIN Department USING(num);
```
.smaller.sqltable[
|id|name|salary|taxes|num|name
|-|-|
|1 | John Doe | 1000 | 200 | 1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 | Sales
|3 | John Smith | 1200 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
]
```sql
SELECT * FROM Employee LEFT JOIN Department USING(num);
```
.smaller.sqltable[
|id|name|salary|taxes|num|name
|-|-|
|1 | John Doe | 1000 | 200 | 1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 | Sales
|3 | John Smith | 1200 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
|**5** | **Richard Roe** | **900** | **0** | **NULL** | **NULL**
]
---
# Renaming tables
* We can also rename tables.
* This might be useful if we need to use the **same table twice** in the same query.
* Or if we want to make the table names **simpler** in a complicated query.
For example, say we have the following table of employees:
.smaller.sqltable[
|id|name|sup_id
|-|-|
|1 | John Doe | NULL
|2 | Jane Doe | 1
|3 | John Smith | 1
|4 | Jane Roe | NULL
|5 | Richard Roe | 4
]
And we want to know the name of the supervisor of each employee.
---
# Renaming tables
* We can join the table with itself in order to obtain the names of the supervisors.
* But, when using a table twice in the same query, we must **use a different name for each instance of the table**:
```sql
SELECT *
FROM Employee JOIN Employee AS Supervisor
ON Employee.sup_id = Supervisor.id;
```
.smaller.sqltable[
|id|name|sup_id|id|name|sup_id
|-|-|
|2 | Jane Doe | 1 |1 | John Doe | NULL
|3 | John Smith | 1 |1 | John Doe | NULL
|5 | Richard Roe | 4 |4 | Jane Roe | NULL
]
---
template: inverse
name: aggregating
# Aggregating Data
---
# Aggregate Functions
There are **five** special aggregate functions defined in the SQL language:
```sql
MIN, MAX, SUM, AVG and COUNT
```
.sqltable[
|val
|-|-|
|1
|2
|NULL
|2
|3
]
.small-code[
```sql
SELECT MIN(val) FROM MyTable; -- 1
SELECT MAX(val) FROM MyTable; -- 3
SELECT SUM(val) FROM MyTable; -- 8
SELECT AVG(val) FROM MyTable; -- 2
SELECT COUNT(val) FROM MyTable; -- 4 (counts non null values)
SELECT COUNT(DISTINCT val) FROM MyTable; -- 3 (counts distinct non null values)
SELECT COUNT(*) FROM MyTable; -- 5 (counts lines)
```
]
---
# Aggregate Functions
When using aggregate functions, all rows are grouped into a single row.
.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|1 | John Doe | 700 | 200 | 1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 | Sales
|3 | John Smith | 1500 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
]
```sql
SELECT AVG(salary) FROM Employee; -- 1000
SELECT name, AVG(salary) FROM Employee; -- Does not work
```
.box_warning[You can **no longer refer to columns** without an aggregate function.]
---
# GROUP BY
Groups the rows into sets based on the value of a specific column or columns.
.smaller.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|1 | John Doe | 700 | 200 | 1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 | Sales
|3 | John Smith | 1500 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
]
```sql
SELECT AVG(salary) FROM Employee GROUP BY num;
```
* All rows with same value in the *num* column get grouped together.
* The aggregate functions are used inside each group.
.sqltable[
|AVG(salary)
|-|-|
|700
|1150
|1000
]
---
# GROUP BY
Only columns used in **GROUP BY** expressions can be selected without using an aggregate function.
.smaller.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|1 | John Doe | 700 | 200 | 1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 | Sales
|3 | John Smith | 1500 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
]
```sql
SELECT num, AVG(salary) FROM Employee GROUP BY num;
```
.sqltable[
|num|AVG(salary)
|-|-|
|1|700
|2|1150
|3|1000
]
---
# GROUP BY
Only columns used in **GROUP BY** expressions can be selected without using an aggregate function.
.smaller.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|1 | John Doe | 700 | 200 | 1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 | Sales
|3 | John Smith | 1500 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
]
```sql
SELECT num, d_name, AVG(salary) FROM Employee GROUP BY num, d_name;
```
.sqltable[
|num|d_name|AVG(salary)
|-|-|-|
|1|Marketing|700
|2|Sales|1150
|3|Production|1000
]
---
# HAVING
Grouped rows can be filtered using the **HAVING** clause.
.smaller.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|1 | John Doe | 700 | 200 | 1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 | Sales
|3 | John Smith | 1500 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
]
```sql
SELECT num, d_name, AVG(salary)
FROM Employee
GROUP BY num, d_name
HAVING AVG(salary) >= 1000;
```
.sqltable[
|num|d_name|AVG(salary)
|-|-|-|
|2|Sales|1150
|3|Production|1000
]
---
template: inverse
name: sorting
# Sorting Rows
---
# ORDER BY
* The order in which rows are sorted in a query result is unpredictable.
* You can sort the end result using the **ORDER BY** clause.
.smaller.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|1 | John Doe | 700 | 200 | 1 | Marketing
|2 | Jane Doe | 800 | 100 | 2 | Sales
|3 | John Smith | 1500 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
]
```sql
SELECT * FROM Employee ORDER BY salary;
```
.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|1 | John Doe | **700** | 200 | 1 | Marketing
|2 | Jane Doe | **800** | 100 | 2 | Sales
|4 | Jane Roe | **1000** | 200 | 3 | Production
|3 | John Smith | **1500** | 350 | 2 | Sales
]
---
# ORDER BY
* By default values are sorted in **ascending** order.
* We can change the default order using the **ASC** and **DESC** clauses.
```sql
SELECT * FROM Employee ORDER BY salary ASC; -- default
```
.smaller.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|1 | John Doe | **700** | 200 | 1 | Marketing
|2 | Jane Doe | **800** | 100 | 2 | Sales
|4 | Jane Roe | **1000** | 200 | 3 | Production
|3 | John Smith | **1500** | 350 | 2 | Sales
]
```sql
SELECT * FROM Employee ORDER BY salary DESC;
```
.smaller.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|3 | John Smith | **1500** | 350 | 2 | Sales
|4 | Jane Roe | **1000** | 200 | 3 | Production
|2 | Jane Doe | **800** | 100 | 2 | Sales
|1 | John Doe | **700** | 200 | 1 | Marketing
]
---
# ORDER BY
* We can also sort by more than one column.
* When this happens, we first sort using the first column and if there is a tie we use the next column.
```sql
SELECT * FROM Employee ORDER BY taxes DESC, salary ASC;
SELECT * FROM Employee ORDER BY taxes DESC, salary; -- equivalent
```
.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|3 | John Smith | **1500** | 350 | 2 | Sales
|1 | John Doe | **700** | 200 | 1 | Marketing
|4 | Jane Roe | **1000** | 200 | 3 | Production
|2 | Jane Doe | **800** | 100 | 2 | Sales
]
---
template: inverse
name: limiting
# Limiting Data
---
# LIMIT
The number of rows returned by a query can be limited using the **LIMIT** clause.
```sql
SELECT * FROM Employee ORDER BY salary DESC LIMIT 2;
```
.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|3 | John Smith | 1500 | 350 | 2 | Sales
|4 | Jane Roe | 1000 | 200 | 3 | Production
]
---
# OFFSET
When using the **LIMIT** clause, we can also indicate how many rows to skip using the **OFFSET** clause.
```sql
SELECT * FROM Employee ORDER BY salary DESC LIMIT 2 OFFSET 1;
```
.sqltable[
|id|name|salary|taxes|num|d_name
|-|-|
|4 | Jane Roe | 1000 | 200 | 3 | Production
|2 | Jane Doe | 800 | 100 | 2 | Sales
]
.box_info[Here the first line (with a salary of 1500) was skipped.]
---
# Pagination
**LIMIT** and **OFFSET** can be used to paginate our results.
.box_info[Example for page 3 with 10 results per page.]
```sql
SELECT * FROM Employee ORDER BY id LIMIT 10 OFFSET 20;
```
.box_info[Generically for page N with R results per page.]
```sql
SELECT * FROM Employee ORDER BY id LIMIT R OFFSET R * (N - 1);
```
---
template: inverse
name: nested
# A Complete Query
---
# A Complete Query
* A query will always start with the **SELECT** clause.
* All the other clauses are optional.
* But they always follow the same order.
```sql
SELECT c1, c2, SUM(c3), AVG(c4) AS c5
FROM T1
JOIN T2 ON T1.foreign_key = T2.primary_key
JOIN T3 USING (join_column)
WHERE condition_1 AND (condition_2 OR condition_3)
GROUP BY c1, c2
HAVING c5 > 10
ORDER BY c5
LIMIT 5 OFFSET 10;
```
---
template: inverse
name: text
# Text Operators
---
# LIKE
The **LIKE** operator can be used to compare strings using simple patterns.
* The **%** particle means zero or more characters
* The **_** character means exactly one character.
.smaller.sqltable[
|id|name
|-|-|
|1 | John Doe
|2 | Jane Doe
|3 | Jean Doe
|4 | Jennifer Doe
|5 | William Doe
]
.small-code[
```sql
SELECT * FROM People WHERE name LIKE '% Doe' -- All five rows
SELECT * FROM People WHERE name LIKE 'J% Doe' -- John, Jane, Jean and Jennifer
SELECT * FROM People WHERE name LIKE 'J___ Doe' -- John, Jane and Jean
SELECT * FROM People WHERE name LIKE '_e%' -- Jean and Jennifer
```
]
.box_info[In SQLite, LIKE is case-insensitive. To make it case-sensitive, use: `PRAGMA case_sensitive_like = ON;`]
---
template: inverse
name: nested
# Nested Queries
---
# Queries as Tables
**The result of a query is a table**. This allows us to use queries in the same
places we use tables.
```sql
SELECT *
FROM (
SELECT *
FROM Employee JOIN Department USING(d_num)
WHERE Department.name = 'Logistics'
) AS logistic_employees JOIN Works ON logistic_employees.id = Works.id
WHERE Works.hours > 10;
```
.box_info[In the SQL standard, *subqueries* must be named using the **AS** particle. In SQLite this rule is not enforced.]
---
# Subquery Expressions
Subquery expressions can be used to combine two queries in the **WHERE** or
**HAVING** clauses.
There are several subquery expressions:
```sql
expression IN subquery
expression NOT IN subquery
```
---
# IN and NOT IN
```sql
expression IN subquery
expression NOT IN subquery
```
These expressions test if a value (or values) exist (or not) in the subquery.
```sql
SELECT * -- Employees whose id exists in the subquery
FROM Employee
WHERE Employee.id IN (
SELECT emp_id -- The ids of employees that have worked
FROM Works -- more than 10 hours in projects
GROUP BY emp_id
HAVING SUM(hours) > 10
);
```