Chapter 13: Filter Results Using WHERE and Having

Download as pdf or txt
Download as pdf or txt
You are on page 1of 49

Chapter 13: Filter results using WHERE and

HAVING
Section 13.1: Use BETWEEN to Filter Results
The following examples use the Item Sales and Customers sample databases.

Note: The BETWEEN operator is inclusive.

Using the BETWEEN operator with Numbers:

SELECT * From ItemSales


WHERE Quantity BETWEEN 10 AND 17

This query will return all ItemSales records that have a quantity that is greater or equal to 10 and less than or equal
to 17. The results will look like:

Id SaleDate ItemId Quantity Price


1 2013-07-01 100 10 34.5
4 2013-07-23 100 15 34.5
5 2013-07-24 145 10 34.5

Using the BETWEEN operator with Date Values:

SELECT * From ItemSales


WHERE SaleDate BETWEEN '2013-07-11' AND '2013-05-24'

This query will return all ItemSales records with a SaleDate that is greater than or equal to July 11, 2013 and less
than or equal to May 24, 2013.

Id SaleDate ItemId Quantity Price


3 2013-07-11 100 20 34.5
4 2013-07-23 100 15 34.5
5 2013-07-24 145 10 34.5

When comparing datetime values instead of dates, you may need to convert the datetime values into a
date values, or add or subtract 24 hours to get the correct results.

Using the BETWEEN operator with Text Values:

SELECT Id, FName, LName FROM Customers


WHERE LName BETWEEN 'D' AND 'L';

Live example: SQL fiddle

This query will return all customers whose name alphabetically falls between the letters 'D' and 'L'. In this case,
Customer #1 and #3 will be returned. Customer #2, whose name begins with a 'M' will not be included.

Id FName LName

GoalKicker.com – SQL Notes for Professionals 44


1 William Jones
3 Richard Davis

Section 13.2: Use HAVING with Aggregate Functions


Unlike the WHERE clause, HAVING can be used with aggregate functions.

An aggregate function is a function where the values of multiple rows are grouped together as input on
certain criteria to form a single value of more significant meaning or measurement (Wikipedia).

Common aggregate functions include COUNT(), SUM(), MIN(), and MAX().

This example uses the Car Table from the Example Databases.

SELECT CustomerId, COUNT(Id) AS [Number of Cars]


FROM Cars
GROUP BY CustomerId
HAVING COUNT(Id) > 1

This query will return the CustomerId and Number of Cars count of any customer who has more than one car. In
this case, the only customer who has more than one car is Customer #1.

The results will look like:

CustomerId Number of Cars


1 2

Section 13.3: WHERE clause with NULL/NOT NULL values


SELECT *
FROM Employees
WHERE ManagerId IS NULL

This statement will return all Employee records where the value of the ManagerId column is NULL.

The result will be:

Id FName LName PhoneNumber ManagerId DepartmentId


1 James Smith 1234567890 NULL 1

SELECT *
FROM Employees
WHERE ManagerId IS NOT NULL

This statement will return all Employee records where the value of the ManagerId is not NULL.

The result will be:

Id FName LName PhoneNumber ManagerId DepartmentId


2 John Johnson 2468101214 1 1
3 Michael Williams 1357911131 1 2
4 Johnathon Smith 1212121212 2 1

GoalKicker.com – SQL Notes for Professionals 45


Note: The same query will not return results if you change the WHERE clause to WHERE ManagerId = NULL or WHERE
ManagerId <> NULL.

Section 13.4: Equality


SELECT * FROM Employees

This statement will return all the rows from the table Employees.

Id FName LName PhoneNumber ManagerId DepartmentId Salary Hire_date


CreatedDate ModifiedDate
1 James Smith 1234567890 NULL 1 1000 01-01-2002 01-01-2002
01-01-2002
2 John Johnson 2468101214 1 1 400 23-03-2005 23-03-2005
01-01-2002
3 Michael Williams 1357911131 1 2 600 12-05-2009 12-05-2009
NULL
4 Johnathon Smith 1212121212 2 1 500 24-07-2016 24-07-2016
01-01-2002

Using a WHERE at the end of your SELECT statement allows you to limit the returned rows to a condition. In this case,
where there is an exact match using the = sign:

SELECT * FROM Employees WHERE DepartmentId = 1

Will only return the rows where the DepartmentId is equal to 1:

Id FName LName PhoneNumber ManagerId DepartmentId Salary Hire_date


CreatedDate ModifiedDate
1 James Smith 1234567890 NULL 1 1000 01-01-2002 01-01-2002
01-01-2002
2 John Johnson 2468101214 1 1 400 23-03-2005 23-03-2005
01-01-2002
4 Johnathon Smith 1212121212 2 1 500 24-07-2016 24-07-2016
01-01-2002

Section 13.5: The WHERE clause only returns rows that match
its criteria
Steam has a games under $10 section of their store page. Somewhere deep in the heart of their systems, there's
probably a query that looks something like:

SELECT *
FROM Items
WHERE Price < 10

Section 13.6: AND and OR


You can also combine several operators together to create more complex WHERE conditions. The following examples
use the Employees table:

Id FName LName PhoneNumber ManagerId DepartmentId Salary Hire_date

GoalKicker.com – SQL Notes for Professionals 46


CreatedDate ModifiedDate
1 James Smith 1234567890 NULL 1 1000 01-01-2002 01-01-2002
01-01-2002
2 John Johnson 2468101214 1 1 400 23-03-2005 23-03-2005
01-01-2002
3 Michael Williams 1357911131 1 2 600 12-05-2009 12-05-2009
NULL
4 Johnathon Smith 1212121212 2 1 500 24-07-2016 24-07-2016
01-01-2002

AND

SELECT * FROM Employees WHERE DepartmentId = 1 AND ManagerId = 1

Will return:

Id FName LName PhoneNumber ManagerId DepartmentId Salary Hire_date


CreatedDate ModifiedDate
2 John Johnson 2468101214 1 1 400 23-03-2005 23-03-2005
01-01-2002

OR

SELECT * FROM Employees WHERE DepartmentId = 2 OR ManagerId = 2

Will return:

Id FName LName PhoneNumber ManagerId DepartmentId Salary Hire_date


CreatedDate ModifiedDate
3 Michael Williams 1357911131 1 2 600 12-05-2009 12-05-2009
NULL
4 Johnathon Smith 1212121212 2 1 500 24-07-2016 24-07-2016
01-01-2002

Section 13.7: Use IN to return rows with a value contained in a


list
This example uses the Car Table from the Example Databases.

SELECT *
FROM Cars
WHERE TotalCost IN (100, 200, 300)

This query will return Car #2 which costs 200 and Car #3 which costs 100. Note that this is equivalent to using
multiple clauses with OR, e.g.:

SELECT *
FROM Cars
WHERE TotalCost = 100 OR TotalCost = 200 OR TotalCost = 300

Section 13.8: Use LIKE to find matching strings and substrings


See full documentation on LIKE operator.

GoalKicker.com – SQL Notes for Professionals 47


This example uses the Employees Table from the Example Databases.

SELECT *
FROM Employees
WHERE FName LIKE 'John'

This query will only return Employee #1 whose first name matches 'John' exactly.

SELECT *
FROM Employees
WHERE FName like 'John%'

Adding % allows you to search for a substring:

John% - will return any Employee whose name begins with 'John', followed by any amount of characters
%John - will return any Employee whose name ends with 'John', proceeded by any amount of characters
%John% - will return any Employee whose name contains 'John' anywhere within the value

In this case, the query will return Employee #2 whose name is 'John' as well as Employee #4 whose name is
'Johnathon'.

Section 13.9: Where EXISTS


Will select records in TableName that have records matching in TableName1.

SELECT * FROM TableName t WHERE EXISTS (


SELECT 1 FROM TableName1 t1 where t.Id = t1.Id)

Section 13.10: Use HAVING to check for multiple conditions in a


group
Orders Table

CustomerId ProductId Quantity Price


1 2 5 100
1 3 2 200
1 4 1 500
2 1 4 50
3 5 6 700

To check for customers who have ordered both - ProductID 2 and 3, HAVING can be used

select customerId
from orders
where productID in (2,3)
group by customerId
having count(distinct productID) = 2

Return value:

customerId
1

The query selects only records with the productIDs in questions and with the HAVING clause checks for groups

GoalKicker.com – SQL Notes for Professionals 48


having 2 productIds and not just one.

Another possibility would be

select customerId
from orders
group by customerId
having sum(case when productID = 2 then 1 else 0 end) > 0
and sum(case when productID = 3 then 1 else 0 end) > 0

This query selects only groups having at least one record with productID 2 and at least one with productID 3.

GoalKicker.com – SQL Notes for Professionals 49


Chapter 14: SKIP TAKE (Pagination)
Section 14.1: Limiting amount of results
ISO/ANSI SQL:

SELECT * FROM TableName FETCH FIRST 20 ROWS ONLY;

MySQL; PostgreSQL; SQLite:

SELECT * FROM TableName LIMIT 20;

Oracle:

SELECT Id,
Col1
FROM (SELECT Id,
Col1,
row_number() over (order by Id) RowNumber
FROM TableName)
WHERE RowNumber <= 20

SQL Server:

SELECT TOP 20 *
FROM dbo.[Sale]

Section 14.2: Skipping then taking some results (Pagination)


ISO/ANSI SQL:

SELECT Id, Col1


FROM TableName
ORDER BY Id
OFFSET 20 ROWS FETCH NEXT 20 ROWS ONLY;

MySQL:

SELECT * FROM TableName LIMIT 20, 20; -- offset, limit

Oracle; SQL Server:

SELECT Id,
Col1
FROM (SELECT Id,
Col1,
row_number() over (order by Id) RowNumber
FROM TableName)
WHERE RowNumber BETWEEN 21 AND 40

PostgreSQL; SQLite:

SELECT * FROM TableName LIMIT 20 OFFSET 20;

GoalKicker.com – SQL Notes for Professionals 50


Section 14.3: Skipping some rows from result
ISO/ANSI SQL:

SELECT Id, Col1


FROM TableName
ORDER BY Id
OFFSET 20 ROWS

MySQL:

SELECT * FROM TableName LIMIT 20, 42424242424242;


-- skips 20 for take use very large number that is more than rows in table

Oracle:

SELECT Id,
Col1
FROM (SELECT Id,
Col1,
row_number() over (order by Id) RowNumber
FROM TableName)
WHERE RowNumber > 20

PostgreSQL:

SELECT * FROM TableName OFFSET 20;

SQLite:

SELECT * FROM TableName LIMIT -1 OFFSET 20;

GoalKicker.com – SQL Notes for Professionals 51


Chapter 15: EXCEPT
Section 15.1: Select dataset except where values are in this
other dataset
--dataset schemas must be identical
SELECT 'Data1' as 'Column' UNION ALL
SELECT 'Data2' as 'Column' UNION ALL
SELECT 'Data3' as 'Column' UNION ALL
SELECT 'Data4' as 'Column' UNION ALL
SELECT 'Data5' as 'Column'
EXCEPT
SELECT 'Data3' as 'Column'
--Returns Data1, Data2, Data4, and Data5

GoalKicker.com – SQL Notes for Professionals 52


Chapter 16: EXPLAIN and DESCRIBE
Section 16.1: EXPLAIN Select query
An Explain infront of a select query shows you how the query will be executed. This way you to see if the query
uses an index or if you could optimize your query by adding an index.

Example query:

explain select * from user join data on user.test = data.fk_user;

Example result:

id select_type table type possible_keys key key_len ref rows Extra


1 SIMPLE user index test test 5 (null) 1 Using where; Using
index
1 SIMPLE data ref fk_user fk_user 5 user.test 1 (null)

on type you see if an index was used. In the column possible_keys you see if the execution plan can choose from
different indexes of if none exists. key tells you the acutal used index. key_len shows you the size in bytes for one
index item. The lower this value is the more index items fit into the same memory size an they can be faster
processed. rows shows you the expected number of rows the query needs to scan, the lower the better.

Section 16.2: DESCRIBE tablename;


DESCRIBE and EXPLAIN are synonyms. DESCRIBE on a tablename returns the definition of the columns.

DESCRIBE tablename;

Exmple Result:

COLUMN_NAME COLUMN_TYPE IS_NULLABLE COLUMN_KEY COLUMN_DEFAULT EXTRA


id int(11) NO PRI 0 auto_increment
test varchar(255) YES (null)

Here you see the column names, followed by the columns type. It shows if null is allowed in the column and if the
column uses an Index. the default value is also displayed and if the table contains any special behavior like an
auto_increment.

GoalKicker.com – SQL Notes for Professionals 53


Chapter 17: EXISTS CLAUSE
Section 17.1: EXISTS CLAUSE
Customer Table

Id FirstName LastName
1 Ozgur Ozturk
2 Youssef Medi
3 Henry Tai

Order Table

Id CustomerId Amount
1 2 123.50
2 3 14.80
Get all customers with a least one order
SELECT * FROM Customer WHERE EXISTS (
SELECT * FROM Order WHERE Order.CustomerId=Customer.Id
)

Result

Id FirstName LastName
2 Youssef Medi
3 Henry Tai
Get all customers with no order
SELECT * FROM Customer WHERE NOT EXISTS (
SELECT * FROM Order WHERE Order.CustomerId = Customer.Id
)

Result

Id FirstName LastName
1 Ozgur Ozturk
Purpose

EXISTS, IN and JOIN could sometime be used for the same result, however, they are not equals :

EXISTS should be used to check if a value exist in another table


IN should be used for static list
JOIN should be used to retrieve data from other(s) table(s)

GoalKicker.com – SQL Notes for Professionals 54


Chapter 18: JOIN
JOIN is a method of combining (joining) information from two tables. The result is a stitched set of columns from
both tables, defined by the join type (INNER/OUTER/CROSS and LEFT/RIGHT/FULL, explained below) and join criteria
(how rows from both tables relate).

A table may be joined to itself or to any other table. If information from more than two tables needs to be accessed,
multiple joins can be specified in a FROM clause.

Section 18.1: Self Join


A table may be joined to itself, with different rows matching each other by some condition. In this use case, aliases
must be used in order to distinguish the two occurrences of the table.

In the below example, for each Employee in the example database Employees table, a record is returned containing
the employee's first name together with the corresponding first name of the employee's manager. Since managers
are also employees, the table is joined with itself:

SELECT
e.FName AS "Employee",
m.FName AS "Manager"
FROM
Employees e
JOIN
Employees m
ON e.ManagerId = m.Id

This query will return the following data:

Employee Manager
John James
Michael James
Johnathon John

So how does this work?

The original table contains these records:

Id FName LName PhoneNumber ManagerId DepartmentId Salary HireDate


1 James Smith 1234567890 NULL 1 1000 01-01-2002
2 John Johnson 2468101214 1 1 400 23-03-2005
3 Michael Williams 1357911131 1 2 600 12-05-2009
4 Johnathon Smith 1212121212 2 1 500 24-07-2016

The first action is to create a Cartesian product of all records in the tables used in the FROM clause. In this case it's
the Employees table twice, so the intermediate table will look like this (I've removed any fields not used in this
example):

e.Id e.FName e.ManagerId m.Id m.FName m.ManagerId


1 James NULL 1 James NULL
1 James NULL 2 John 1
1 James NULL 3 Michael 1

GoalKicker.com – SQL Notes for Professionals 55


1 James NULL 4 Johnathon 2
2 John 1 1 James NULL
2 John 1 2 John 1
2 John 1 3 Michael 1
2 John 1 4 Johnathon 2
3 Michael 1 1 James NULL
3 Michael 1 2 John 1
3 Michael 1 3 Michael 1
3 Michael 1 4 Johnathon 2
4 Johnathon 2 1 James NULL
4 Johnathon 2 2 John 1
4 Johnathon 2 3 Michael 1
4 Johnathon 2 4 Johnathon 2

The next action is to only keep the records that meet the JOIN criteria, so any records where the aliased e table
ManagerId equals the aliased m table Id:

e.Id e.FName e.ManagerId m.Id m.FName m.ManagerId


2 John 1 1 James NULL
3 Michael 1 1 James NULL
4 Johnathon 2 2 John 1

Then, each expression used within the SELECT clause is evaluated to return this table:

e.FName m.FName
John James
Michael James
Johnathon John

Finally, column names e.FName and m.FName are replaced by their alias column names, assigned with the AS
operator:

Employee Manager
John James
Michael James
Johnathon John

Section 18.2: Dierences between inner/outer joins


SQL has various join types to specify whether (non-)matching rows are included in the result: INNER JOIN, LEFT
OUTER JOIN, RIGHT OUTER JOIN, and FULL OUTER JOIN (the INNER and OUTER keywords are optional). The figure
below underlines the differences between these types of joins: the blue area represents the results returned by the
join, and the white area represents the results that the join will not return.

GoalKicker.com – SQL Notes for Professionals 56


Cross Join SQL Pictorial Presentation (reference) :

Below are examples from this answer.

For instance there are two tables as below :

GoalKicker.com – SQL Notes for Professionals 57


A B
- -
1 3
2 4
3 5
4 6

Note that (1,2) are unique to A, (3,4) are common, and (5,6) are unique to B.

Inner Join

An inner join using either of the equivalent queries gives the intersection of the two tables, i.e. the two rows they
have in common:

select * from a INNER JOIN b on a.a = b.b;


select a.*,b.* from a,b where a.a = b.b;

a | b
--+--
3 | 3
4 | 4

Left outer join

A left outer join will give all rows in A, plus any common rows in B:

select * from a LEFT OUTER JOIN b on a.a = b.b;

a | b
--+-----
1 | null
2 | null
3 | 3
4 | 4

Right outer join

Similarly, a right outer join will give all rows in B, plus any common rows in A:

select * from a RIGHT OUTER JOIN b on a.a = b.b;

a | b
-----+----
3 | 3
4 | 4
null | 5
null | 6

Full outer join

A full outer join will give you the union of A and B, i.e., all the rows in A and all the rows in B. If something in A
doesn't have a corresponding datum in B, then the B portion is null, and vice versa.

select * from a FULL OUTER JOIN b on a.a = b.b;

a | b
-----+-----

GoalKicker.com – SQL Notes for Professionals 58


1 | null
2 | null
3 | 3
4 | 4
null | 6
null | 5

Section 18.3: JOIN Terminology: Inner, Outer, Semi, Anti..


Let's say we have two tables (A and B) and some of their rows match (relative to the given JOIN condition, whatever
it may be in the particular case):

GoalKicker.com – SQL Notes for Professionals 59


We can use various join types to include or exclude matching or non-matching rows from either side, and correctly
name the join by picking the corresponding terms from the diagram above.

The examples below use the following test data:

CREATE TABLE A (
X varchar(255) PRIMARY KEY

GoalKicker.com – SQL Notes for Professionals 60


);

CREATE TABLE B (
Y varchar(255) PRIMARY KEY
);

INSERT INTO A VALUES


('Amy'),
('John'),
('Lisa'),
('Marco'),
('Phil');

INSERT INTO B VALUES


('Lisa'),
('Marco'),
('Phil'),
('Tim'),
('Vincent');

Inner Join

Combines left and right rows that match.

SELECT * FROM A JOIN B ON X = Y;

X Y
------ -----
Lisa Lisa
Marco Marco
Phil Phil

Left Outer Join

Sometimes abbreviated to "left join". Combines left and right rows that match, and includes non-matching left
rows.

GoalKicker.com – SQL Notes for Professionals 61


SELECT * FROM A LEFT JOIN B ON X = Y;

X Y
----- -----
Amy NULL
John NULL
Lisa Lisa
Marco Marco
Phil Phil

Right Outer Join

Sometimes abbreviated to "right join". Combines left and right rows that match, and includes non-matching right
rows.

GoalKicker.com – SQL Notes for Professionals 62


SELECT * FROM A RIGHT JOIN B ON X = Y;

X Y
----- -------
Lisa Lisa
Marco Marco
Phil Phil
NULL Tim
NULL Vincent

Full Outer Join

Sometimes abbreviated to "full join". Union of left and right outer join.

GoalKicker.com – SQL Notes for Professionals 63


SELECT * FROM A FULL JOIN B ON X = Y;

X Y
----- -------
Amy NULL
John NULL
Lisa Lisa
Marco Marco
Phil Phil
NULL Tim
NULL Vincent

Left Semi Join

Includes left rows that match right rows.

GoalKicker.com – SQL Notes for Professionals 64


SELECT * FROM A WHERE X IN (SELECT Y FROM B);

X
-----
Lisa
Marco
Phil

Right Semi Join

Includes right rows that match left rows.

GoalKicker.com – SQL Notes for Professionals 65


SELECT * FROM B WHERE Y IN (SELECT X FROM A);

Y
-----
Lisa
Marco
Phil

As you can see, there is no dedicated IN syntax for left vs. right semi join - we achieve the effect simply by switching
the table positions within SQL text.

Left Anti Semi Join

Includes left rows that do not match right rows.

GoalKicker.com – SQL Notes for Professionals 66


SELECT * FROM A WHERE X NOT IN (SELECT Y FROM B);

X
----
Amy
John

WARNING: Be careful if you happen to be using NOT IN on a NULL-able column! More details here.

Right Anti Semi Join

Includes right rows that do not match left rows.

GoalKicker.com – SQL Notes for Professionals 67


SELECT * FROM B WHERE Y NOT IN (SELECT X FROM A);

Y
-------
Tim
Vincent

As you can see, there is no dedicated NOT IN syntax for left vs. right anti semi join - we achieve the effect simply by
switching the table positions within SQL text.

Cross Join

A Cartesian product of all left with all right rows.

SELECT * FROM A CROSS JOIN B;

X Y
----- -------
Amy Lisa
John Lisa
Lisa Lisa
Marco Lisa
Phil Lisa
Amy Marco
John Marco
Lisa Marco
Marco Marco
Phil Marco
Amy Phil
John Phil
Lisa Phil
Marco Phil
Phil Phil
Amy Tim

GoalKicker.com – SQL Notes for Professionals 68


John Tim
Lisa Tim
Marco Tim
Phil Tim
Amy Vincent
John Vincent
Lisa Vincent
Marco Vincent
Phil Vincent

Cross join is equivalent to an inner join with join condition which always matches, so the following query would
have returned the same result:

SELECT * FROM A JOIN B ON 1 = 1;

Self-Join

This simply denotes a table joining with itself. A self-join can be any of the join types discussed above. For example,
this is a an inner self-join:

SELECT * FROM A A1 JOIN A A2 ON LEN(A1.X) < LEN(A2.X);

X X
---- -----
Amy John
Amy Lisa
Amy Marco
John Marco
Lisa Marco
Phil Marco
Amy Phil

Section 18.4: Left Outer Join


A Left Outer Join (also known as a Left Join or Outer Join) is a Join that ensures all rows from the left table are
represented; if no matching row from the right table exists, its corresponding fields are NULL.

The following example will select all departments and the first name of employees that work in that department.
Departments with no employees are still returned in the results, but will have NULL for the employee name:

SELECT Departments.Name, Employees.FName


FROM Departments
LEFT OUTER JOIN Employees
ON Departments.Id = Employees.DepartmentId

This would return the following from the example database:

Departments.Name Employees.FName
HR James
HR John
HR Johnathon
Sales Michael
Tech NULL

So how does this work?

GoalKicker.com – SQL Notes for Professionals 69


There are two tables in the FROM clause:

Id FName LName PhoneNumber ManagerId DepartmentId Salary HireDate


1 James Smith 1234567890 NULL 1 1000 01-01-2002
2 John Johnson 2468101214 1 1 400 23-03-2005
3 Michael Williams 1357911131 1 2 600 12-05-2009
4 Johnathon Smith 1212121212 2 1 500 24-07-2016

and

Id Name
1 HR
2 Sales
3 Tech

First a Cartesian product is created from the two tables giving an intermediate table.
The records that meet the join criteria (Departments.Id = Employees.DepartmentId) are highlighted in bold; these are
passed to the next stage of the query.

As this is a LEFT OUTER JOIN all records are returned from the LEFT side of the join (Departments), while any
records on the RIGHT side are given a NULL marker if they do not match the join criteria. In the table below this will
return Tech with NULL

Id Name Id FName LName PhoneNumber ManagerId DepartmentId Salary HireDate


1 HR 1 James Smith 1234567890 NULL 1 1000 01-01-2002
1 HR 2 John Johnson 2468101214 1 1 400 23-03-2005
1 HR 3 Michael Williams 1357911131 1 2 600 12-05-2009
1 HR 4 Johnathon Smith 1212121212 2 1 500 24-07-2016
2 Sales 1 James Smith 1234567890 NULL 1 1000 01-01-2002
2 Sales 2 John Johnson 2468101214 1 1 400 23-03-2005
2 Sales 3 Michael Williams 1357911131 1 2 600 12-05-2009
2 Sales 4 Johnathon Smith 1212121212 2 1 500 24-07-2016
3 Tech 1 James Smith 1234567890 NULL 1 1000 01-01-2002
3 Tech 2 John Johnson 2468101214 1 1 400 23-03-2005
3 Tech 3 Michael Williams 1357911131 1 2 600 12-05-2009
3 Tech 4 Johnathon Smith 1212121212 2 1 500 24-07-2016

Finally each expression used within the SELECT clause is evaluated to return our final table:

Departments.Name Employees.FName
HR James
HR John
Sales Richard
Tech NULL

Section 18.5: Implicit Join


Joins can also be performed by having several tables in the from clause, separated with commas , and defining the
relationship between them in the where clause. This technique is called an Implicit Join (since it doesn't actually
contain a join clause).

GoalKicker.com – SQL Notes for Professionals 70


All RDBMSs support it, but the syntax is usually advised against. The reasons why it is a bad idea to use this syntax
are:

It is possible to get accidental cross joins which then return incorrect results, especially if you have a lot of
joins in the query.
If you intended a cross join, then it is not clear from the syntax (write out CROSS JOIN instead), and someone
is likely to change it during maintenance.

The following example will select employee's first names and the name of the departments they work for:

SELECT e.FName, d.Name


FROM Employee e, Departments d
WHERE e.DeptartmentId = d.Id

This would return the following from the example database:

e.FName d.Name
James HR
John HR
Richard Sales

Section 18.6: CROSS JOIN


Cross join does a Cartesian product of the two members, A Cartesian product means each row of one table is
combined with each row of the second table in the join. For example, if TABLEA has 20 rows and TABLEB has 20
rows, the result would be 20*20 = 400 output rows.

Using example database

SELECT d.Name, e.FName


FROM Departments d
CROSS JOIN Employees e;

Which returns:

d.Name e.FName
HR James
HR John
HR Michael
HR Johnathon
Sales James
Sales John
Sales Michael
Sales Johnathon
Tech James
Tech John
Tech Michael
Tech Johnathon

It is recommended to write an explicit CROSS JOIN if you want to do a cartesian join, to highlight that this is what
you want.

GoalKicker.com – SQL Notes for Professionals 71


Section 18.7: CROSS APPLY & LATERAL JOIN
A very interesting type of JOIN is the LATERAL JOIN (new in PostgreSQL 9.3+),
which is also known as CROSS APPLY/OUTER APPLY in SQL-Server & Oracle.

The basic idea is that a table-valued function (or inline subquery) gets applied for every row you join.

This makes it possible to, for example, only join the first matching entry in another table.
The difference between a normal and a lateral join lies in the fact that you can use a column that you previously
joined in the subquery that you "CROSS APPLY".

Syntax:

PostgreSQL 9.3+

left | right | inner JOIN LATERAL

SQL-Server:

CROSS | OUTER APPLY

INNER JOIN LATERAL is the same as CROSS APPLY


and LEFT JOIN LATERAL is the same as OUTER APPLY

Example usage (PostgreSQL 9.3+):

SELECT * FROM T_Contacts

--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND


MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989

LEFT JOIN LATERAL


(
SELECT
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID

/*
AND
(
(__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
LIMIT 1

GoalKicker.com – SQL Notes for Professionals 72


) AS FirstOE

And for SQL-Server

SELECT * FROM T_Contacts

--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND


MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989

-- CROSS APPLY -- = INNER JOIN


OUTER APPLY -- = LEFT JOIN
(
SELECT TOP 1
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID

/*
AND
(
(@in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(@in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
) AS FirstOE

Section 18.8: FULL JOIN


One type of JOIN that is less known, is the FULL JOIN.
(Note: FULL JOIN is not supported by MySQL as per 2016)

A FULL OUTER JOIN returns all rows from the left table, and all rows from the right table.

If there are rows in the left table that do not have matches in the right table, or if there are rows in right table that
do not have matches in the left table, then those rows will be listed, too.

Example 1 :

SELECT * FROM Table1

FULL JOIN Table2


ON 1 = 2

Example 2:

SELECT
COALESCE(T_Budget.Year, tYear.Year) AS RPT_BudgetInYear
,COALESCE(T_Budget.Value, 0.0) AS RPT_Value
FROM T_Budget

FULL JOIN tfu_RPT_All_CreateYearInterval(@budget_year_from, @budget_year_to) AS tYear

GoalKicker.com – SQL Notes for Professionals 73


ON tYear.Year = T_Budget.Year

Note that if you're using soft-deletes, you'll have to check the soft-delete status again in the WHERE-clause (because
FULL JOIN behaves kind-of like a UNION);
It's easy to overlook this little fact, since you put AP_SoftDeleteStatus = 1 in the join clause.

Also, if you are doing a FULL JOIN, you'll usually have to allow NULL in the WHERE-clause; forgetting to allow NULL
on a value will have the same effects as an INNER join, which is something you don't want if you're doing a FULL
JOIN.

Example:

SELECT
T_AccountPlan.AP_UID
,T_AccountPlan.AP_Code
,T_AccountPlan.AP_Lang_EN
,T_BudgetPositions.BUP_Budget
,T_BudgetPositions.BUP_UID
,T_BudgetPositions.BUP_Jahr
FROM T_BudgetPositions

FULL JOIN T_AccountPlan


ON T_AccountPlan.AP_UID = T_BudgetPositions.BUP_AP_UID
AND T_AccountPlan.AP_SoftDeleteStatus = 1

WHERE (1=1)
AND (T_BudgetPositions.BUP_SoftDeleteStatus = 1 OR T_BudgetPositions.BUP_SoftDeleteStatus IS NULL)
AND (T_AccountPlan.AP_SoftDeleteStatus = 1 OR T_AccountPlan.AP_SoftDeleteStatus IS NULL)

Section 18.9: Recursive JOINs


Recursive joins are often used to obtain parent-child data. In SQL, they are implemented with recursive common
table expressions, for example:

WITH RECURSIVE MyDescendants AS (


SELECT Name
FROM People
WHERE Name = 'John Doe'

UNION ALL

SELECT People.Name
FROM People
JOIN MyDescendants ON People.Name = MyDescendants.Parent
)
SELECT * FROM MyDescendants;

Section 18.10: Basic explicit inner join


A basic join (also called "inner join") queries data from two tables, with their relationship defined in a join clause.

The following example will select employees' first names (FName) from the Employees table and the name of the
department they work for (Name) from the Departments table:

SELECT Employees.FName, Departments.Name


FROM Employees
JOIN Departments

GoalKicker.com – SQL Notes for Professionals 74


ON Employees.DepartmentId = Departments.Id

This would return the following from the example database:

Employees.FName Departments.Name
James HR
John HR
Richard Sales

Section 18.11: Joining on a Subquery


Joining a subquery is often used when you want to get aggregate data from a child/details table and display that
along with records from the parent/header table. For example, you might want to get a count of child records, an
average of some numeric column in child records, or the top or bottom row based on a date or numeric field. This
example uses aliases, which arguable makes queries easier to read when you have multiple tables involved. Here's
what a fairly typical subquery join looks like. In this case we are retrieving all rows from the parent table Purchase
Orders and retrieving only the first row for each parent record of the child table PurchaseOrderLineItems.

SELECT po.Id, po.PODate, po.VendorName, po.Status, item.ItemNo,


item.Description, item.Cost, item.Price
FROM PurchaseOrders po
LEFT JOIN
(
SELECT l.PurchaseOrderId, l.ItemNo, l.Description, l.Cost, l.Price, Min(l.id) as Id
FROM PurchaseOrderLineItems l
GROUP BY l.PurchaseOrderId, l.ItemNo, l.Description, l.Cost, l.Price
) AS item ON item.PurchaseOrderId = po.Id

GoalKicker.com – SQL Notes for Professionals 75


Chapter 19: UPDATE
Section 19.1: UPDATE with data from another table
The examples below fill in a PhoneNumber for any Employee who is also a Customer and currently does not have a
phone number set in the Employees Table.

(These examples use the Employees and Customers tables from the Example Databases.)

Standard SQL

Update using a correlated subquery:

UPDATE
Employees
SET PhoneNumber =
(SELECT
c.PhoneNumber
FROM
Customers c
WHERE
c.FName = Employees.FName
AND c.LName = Employees.LName)
WHERE Employees.PhoneNumber IS NULL

SQL:2003

Update using MERGE:

MERGE INTO
Employees e
USING
Customers c
ON
e.FName = c.Fname
AND e.LName = c.LName
AND e.PhoneNumber IS NULL
WHEN MATCHED THEN
UPDATE
SET PhoneNumber = c.PhoneNumber

SQL Server

Update using INNER JOIN:

UPDATE
Employees
SET
PhoneNumber = c.PhoneNumber
FROM
Employees e
INNER JOIN Customers c
ON e.FName = c.FName
AND e.LName = c.LName
WHERE
PhoneNumber IS NULL

GoalKicker.com – SQL Notes for Professionals 76


Section 19.2: Modifying existing values
This example uses the Cars Table from the Example Databases.

UPDATE Cars
SET TotalCost = TotalCost + 100
WHERE Id = 3 or Id = 4

Update operations can include current values in the updated row. In this simple example the TotalCost is
incremented by 100 for two rows:

The TotalCost of Car #3 is increased from 100 to 200


The TotalCost of Car #4 is increased from 1254 to 1354

A column's new value may be derived from its previous value or from any other column's value in the same table or
a joined table.

Section 19.3: Updating Specified Rows


This example uses the Cars Table from the Example Databases.

UPDATE
Cars
SET
Status = 'READY'
WHERE
Id = 4

This statement will set the status of the row of 'Cars' with id 4 to "READY".

WHERE clause contains a logical expression which is evaluated for each row. If a row fulfills the criteria, its value is
updated. Otherwise, a row remains unchanged.

Section 19.4: Updating All Rows


This example uses the Cars Table from the Example Databases.

UPDATE Cars
SET Status = 'READY'

This statement will set the 'status' column of all rows of the 'Cars' table to "READY" because it does not have a WHERE
clause to filter the set of rows.

Section 19.5: Capturing Updated records


Sometimes one wants to capture the records that have just been updated.

CREATE TABLE #TempUpdated(ID INT)

Update TableName SET Col1 = 42


OUTPUT inserted.ID INTO #TempUpdated
WHERE Id > 50

GoalKicker.com – SQL Notes for Professionals 77


Chapter 20: CREATE Database
Section 20.1: CREATE Database
A database is created with the following SQL command:

CREATE DATABASE myDatabase;

This would create an empty database named myDatabase where you can create tables.

GoalKicker.com – SQL Notes for Professionals 78


Chapter 21: CREATE TABLE
Parameter Details
tableName The name of the table
Contains an 'enumeration' of all the columns that the table have. See Create a New Table for more
columns
details.
The CREATE TABLE statement is used create a new table in the database. A table definition consists of a list of
columns, their types, and any integrity constraints.

Section 21.1: Create Table From Select


You may want to create a duplicate of a table:

CREATE TABLE ClonedEmployees AS SELECT * FROM Employees;

You can use any of the other features of a SELECT statement to modify the data before passing it to the new table.
The columns of the new table are automatically created according to the selected rows.

CREATE TABLE ModifiedEmployees AS


SELECT Id, CONCAT(FName," ",LName) AS FullName FROM Employees
WHERE Id > 10;

Section 21.2: Create a New Table


A basic Employees table, containing an ID, and the employee's first and last name along with their phone number
can be created using

CREATE TABLE Employees(


Id int identity(1,1) primary key not null,
FName varchar(20) not null,
LName varchar(20) not null,
PhoneNumber varchar(10) not null
);

This example is specific to Transact-SQL

CREATE TABLE creates a new table in the database, followed by the table name, Employees

This is then followed by the list of column names and their properties, such as the ID

Id int identity(1,1) not null


Value Meaning
Id the column's name.
int is the data type.
states that column will have auto generated values starting at 1 and incrementing by 1 for each
identity(1,1)
new row.
primary key states that all values in this column will have unique values
not null states that this column cannot have null values

Section 21.3: CREATE TABLE With FOREIGN KEY


Below you could find the table Employees with a reference to the table Cities.

GoalKicker.com – SQL Notes for Professionals 79


CREATE TABLE Cities(
CityID INT IDENTITY(1,1) NOT NULL,
Name VARCHAR(20) NOT NULL,
Zip VARCHAR(10) NOT NULL
);

CREATE TABLE Employees(


EmployeeID INT IDENTITY (1,1) NOT NULL,
FirstName VARCHAR(20) NOT NULL,
LastName VARCHAR(20) NOT NULL,
PhoneNumber VARCHAR(10) NOT NULL,
CityID INT FOREIGN KEY REFERENCES Cities(CityID)
);

Here could you find a database diagram.

The column CityID of table Employees will reference to the column CityID of table Cities. Below you could find
the syntax to make this.

CityID INT FOREIGN KEY REFERENCES Cities(CityID)


Value Meaning
CityID Name of the column
int type of the column
FOREIGN KEY Makes the foreign key (optional)
REFERENCES Makes the reference
Cities(CityID) to the table Cities column CityID

Important: You couldn't make a reference to a table that not exists in the database. Be source to make first the
table Cities and second the table Employees. If you do it vise versa, it will throw an error.

Section 21.4: Duplicate a table


To duplicate a table, simply do the following:

CREATE TABLE newtable LIKE oldtable;


INSERT newtable SELECT * FROM oldtable;

Section 21.5: Create a Temporary or In-Memory Table


PostgreSQL and SQLite

GoalKicker.com – SQL Notes for Professionals 80


To create a temporary table local to the session:

CREATE TEMP TABLE MyTable(...);

SQL Server

To create a temporary table local to the session:

CREATE TABLE #TempPhysical(...);

To create a temporary table visible to everyone:

CREATE TABLE ##TempPhysicalVisibleToEveryone(...);

To create an in-memory table:

DECLARE @TempMemory TABLE(...);

GoalKicker.com – SQL Notes for Professionals 81


Chapter 22: CREATE FUNCTION
Argument Description
function_name the name of function
list_of_paramenters parameters that function accepts
return_data_type type that function returs. Some SQL data type
function_body the code of function
scalar_expression scalar value returned by function

Section 22.1: Create a new Function


CREATE FUNCTION FirstWord (@input varchar(1000))
RETURNS varchar(1000)
AS
BEGIN
DECLARE @output varchar(1000)
SET @output = SUBSTRING(@input, 0, CASE CHARINDEX(' ', @input)
WHEN 0 THEN LEN(@input) + 1
ELSE CHARINDEX(' ', @input)
END)

RETURN @output
END

This example creates a function named FirstWord, that accepts a varchar parameter and returns another varchar
value.

GoalKicker.com – SQL Notes for Professionals 82


Chapter 23: TRY/CATCH
Section 23.1: Transaction In a TRY/CATCH
This will rollback both inserts due to an invalid datetime:

BEGIN TRANSACTION
BEGIN TRY
INSERT INTO dbo.Sale(Price, SaleDate, Quantity)
VALUES (5.2, GETDATE(), 1)
INSERT INTO dbo.Sale(Price, SaleDate, Quantity)
VALUES (5.2, 'not a date', 1)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
THROW
ROLLBACK TRANSACTION
END CATCH

This will commit both inserts:

BEGIN TRANSACTION
BEGIN TRY
INSERT INTO dbo.Sale(Price, SaleDate, Quantity)
VALUES (5.2, GETDATE(), 1)
INSERT INTO dbo.Sale(Price, SaleDate, Quantity)
VALUES (5.2, GETDATE(), 1)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
THROW
ROLLBACK TRANSACTION
END CATCH

GoalKicker.com – SQL Notes for Professionals 83


Chapter 24: UNION / UNION ALL
UNION keyword in SQL is used to combine to SELECT statement results with out any duplicate. In order to use
UNION and combine results both SELECT statement should have same number of column with same data type in
same order, but the length of column can be different.

Section 24.1: Basic UNION ALL query


CREATE TABLE HR_EMPLOYEES
(
PersonID int,
LastName VARCHAR(30),
FirstName VARCHAR(30),
Position VARCHAR(30)
);

CREATE TABLE FINANCE_EMPLOYEES


(
PersonID INT,
LastName VARCHAR(30),
FirstName VARCHAR(30),
Position VARCHAR(30)
);

Let's say we want to extract the names of all the managers from our departments.

Using a UNION we can get all the employees from both HR and Finance departments, which hold the position of a
manager

SELECT
FirstName, LastName
FROM
HR_EMPLOYEES
WHERE
Position = 'manager'
UNION ALL
SELECT
FirstName, LastName
FROM
FINANCE_EMPLOYEES
WHERE
Position = 'manager'

The UNION statement removes duplicate rows from the query results. Since it is possible to have people having the
same Name and position in both departments we are using UNION ALL, in order not to remove duplicates.

If you want to use an alias for each output column, you can just put them in the first select statement, as follows:

SELECT
FirstName as 'First Name', LastName as 'Last Name'
FROM
HR_EMPLOYEES
WHERE
Position = 'manager'
UNION ALL
SELECT
FirstName, LastName
FROM

GoalKicker.com – SQL Notes for Professionals 84


FINANCE_EMPLOYEES
WHERE
Position = 'manager'

Section 24.2: Simple explanation and Example


In simple terms:

UNION joins 2 result sets while removing duplicates from the result set
UNION ALL joins 2 result sets without attempting to remove duplicates

One mistake many people make is to use a UNION when they do not need to have the duplicates removed.
The additional performance cost against large results sets can be very significant.

When you might need UNION

Suppose you need to filter a table against 2 different attributes, and you have created separate non-clustered
indexes for each column. A UNION enables you to leverage both indexes while still preventing duplicates.

SELECT C1, C2, C3 FROM Table1 WHERE C1 = @Param1


UNION
SELECT C1, C2, C3 FROM Table1 WHERE C2 = @Param2

This simplifies your performance tuning since only simple indexes are needed to perform these queries optimally.
You may even be able to get by with quite a bit fewer non-clustered indexes improving overall write performance
against the source table as well.

When you might need UNION ALL

Suppose you still need to filter a table against 2 attributes, but you do not need to filter duplicate records (either
because it doesn't matter or your data wouldn't produce any duplicates during the union due to your data model
design).

SELECT C1 FROM Table1


UNION ALL
SELECT C1 FROM Table2

This is especially useful when creating Views that join data that is designed to be physically partitioned across
multiple tables (maybe for performance reasons, but still wants to roll-up records). Since the data is already split,
having the database engine remove duplicates adds no value and just adds additional processing time to the
queries.

GoalKicker.com – SQL Notes for Professionals 85


Chapter 25: ALTER TABLE
ALTER command in SQL is used to modify column/constraint in a table

Section 25.1: Add Column(s)


ALTER TABLE Employees
ADD StartingDate date NOT NULL DEFAULT GetDate(),
DateOfBirth date NULL

The above statement would add columns named StartingDate which cannot be NULL with default value as current
date and DateOfBirth which can be NULL in Employees table.

Section 25.2: Drop Column


ALTER TABLE Employees
DROP COLUMN salary;

This will not only delete information from that column, but will drop the column salary from table employees(the
column will no more exist).

Section 25.3: Add Primary Key


ALTER TABLE EMPLOYEES ADD pk_EmployeeID PRIMARY KEY (ID)

This will add a Primary key to the table Employees on the field ID. Including more than one column name in the
parentheses along with ID will create a Composite Primary Key. When adding more than one column, the column
names must be separated by commas.

ALTER TABLE EMPLOYEES ADD pk_EmployeeID PRIMARY KEY (ID, FName)

Section 25.4: Alter Column


ALTER TABLE Employees
ALTER COLUMN StartingDate DATETIME NOT NULL DEFAULT (GETDATE())

This query will alter the column datatype of StartingDate and change it from simple date to datetime and set
default to current date.

Section 25.5: Drop Constraint


ALTER TABLE Employees
DROP CONSTRAINT DefaultSalary

This Drops a constraint called DefaultSalary from the employees table definition.

Note: Ensure that constraints of the column are dropped before dropping a column.

GoalKicker.com – SQL Notes for Professionals 86


Chapter 26: INSERT
Section 26.1: INSERT data from another table using SELECT
INSERT INTO Customers (FName, LName, PhoneNumber)
SELECT FName, LName, PhoneNumber FROM Employees

This example will insert all Employees into the Customers table. Since the two tables have different fields and you
don't want to move all the fields over, you need to set which fields to insert into and which fields to select. The
correlating field names don't need to be called the same thing, but then need to be the same data type. This
example is assuming that the Id field has an Identity Specification set and will auto increment.

If you have two tables that have exactly the same field names and just want to move all the records over you can
use:

INSERT INTO Table1


SELECT * FROM Table2

Section 26.2: Insert New Row


INSERT INTO Customers
VALUES ('Zack', 'Smith', '[email protected]', '7049989942', 'EMAIL');

This statement will insert a new row into the Customers table. Note that a value was not specified for the Id column,
as it will be added automatically. However, all other column values must be specified.

Section 26.3: Insert Only Specified Columns


INSERT INTO Customers (FName, LName, Email, PreferredContact)
VALUES ('Zack', 'Smith', '[email protected]', 'EMAIL');

This statement will insert a new row into the Customers table. Data will only be inserted into the columns specified -
note that no value was provided for the PhoneNumber column. Note, however, that all columns marked as not null
must be included.

Section 26.4: Insert multiple rows at once


Multiple rows can be inserted with a single insert command:

INSERT INTO tbl_name (field1, field2, field3)

VALUES (1,2,3), (4,5,6), (7,8,9);

For inserting large quantities of data (bulk insert) at the same time, DBMS-specific features and recommendations
exist.

MySQL - LOAD DATA INFILE

MSSQL - BULK INSERT

GoalKicker.com – SQL Notes for Professionals 87


Chapter 27: MERGE
MERGE (often also called UPSERT for "update or insert") allows to insert new rows or, if a row already exists, to
update the existing row. The point is to perform the whole set of operations atomically (to guarantee that the data
remain consistent), and to prevent communication overhead for multiple SQL statements in a client/server system.

Section 27.1: MERGE to make Target match Source


MERGE INTO targetTable t
USING sourceTable s
ON t.PKID = s.PKID
WHEN MATCHED AND NOT EXISTS (
SELECT s.ColumnA, s.ColumnB, s.ColumnC
INTERSECT
SELECT t.ColumnA, t.ColumnB, s.ColumnC
)
THEN UPDATE SET
t.ColumnA = s.ColumnA
,t.ColumnB = s.ColumnB
,t.ColumnC = s.ColumnC
WHEN NOT MATCHED BY TARGET
THEN INSERT (PKID, ColumnA, ColumnB, ColumnC)
VALUES (s.PKID, s.ColumnA, s.ColumnB, s.ColumnC)
WHEN NOT MATCHED BY SOURCE
THEN DELETE
;

Note: The AND NOT EXISTS portion prevents updating records that haven't changed. Using the INTERSECT construct
allows nullable columns to be compared without special handling.

Section 27.2: MySQL: counting users by name


Suppose we want to know how many users have the same name. Let us create table users as follows:

create table users(


id int primary key auto_increment,
name varchar(8),
count int,
unique key name(name)
);

Now, we just discovered a new user named Joe and would like to take him into account. To achieve that, we need to
determine whether there is an existing row with his name, and if so, update it to increment count; on the other
hand, if there is no existing row, we should create it.

MySQL uses the following syntax : insert … on duplicate key update …. In this case:

insert into users(name, count)


values ('Joe', 1)
on duplicate key update count=count+1;

Section 27.3: PostgreSQL: counting users by name


Suppose we want to know how many users have the same name. Let us create table users as follows:

GoalKicker.com – SQL Notes for Professionals 88


create table users(
id serial,
name varchar(8) unique,
count int
);

Now, we just discovered a new user named Joe and would like to take him into account. To achieve that, we need to
determine whether there is an existing row with his name, and if so, update it to increment count; on the other
hand, if there is no existing row, we should create it.

PostgreSQL uses the following syntax : insert … on conflict … do update …. In this case:

insert into users(name, count)


values('Joe', 1)
on conflict (name) do update set count = users.count + 1;

GoalKicker.com – SQL Notes for Professionals 89


Chapter 28: cross apply, outer apply
Section 28.1: CROSS APPLY and OUTER APPLY basics
Apply will be used when when table valued function in the right expression.

create a Department table to hold information about departments. Then create an Employee table which hold
information about the employees. Please note, each employee belongs to a department, hence the Employee table
has referential integrity with the Department table.

First query selects data from Department table and uses CROSS APPLY to evaluate the Employee table for each
record of the Department table. Second query simply joins the Department table with the Employee table and all
the matching records are produced.

SELECT *
FROM Department D
CROSS APPLY (
SELECT *
FROM Employee E
WHERE E.DepartmentID = D.DepartmentID
) A
GO
SELECT *
FROM Department D
INNER JOIN Employee E
ON D.DepartmentID = E.DepartmentID

If you look at the results they produced, it is the exact same result-set; How does it differ from a JOIN and how does
it help in writing more efficient queries.

The first query in Script #2 selects data from Department table and uses OUTER APPLY to evaluate the Employee
table for each record of the Department table. For those rows for which there is not a match in Employee table,
those rows contains NULL values as you can see in case of row 5 and 6. The second query simply uses a LEFT
OUTER JOIN between the Department table and the Employee table. As expected the query returns all rows from
Department table; even for those rows for which there is no match in the Employee table.

SELECT *
FROM Department D
OUTER APPLY (
SELECT *
FROM Employee E
WHERE E.DepartmentID = D.DepartmentID
) A
GO
SELECT *
FROM Department D
LEFT OUTER JOIN Employee E
ON D.DepartmentID = E.DepartmentID
GO

Even though the above two queries return the same information, the execution plan will be bit different. But cost
wise there will be not much difference.

Now comes the time to see where the APPLY operator is really required. In Script #3, I am creating a table-valued
function which accepts DepartmentID as its parameter and returns all the employees who belong to this
department. The next query selects data from Department table and uses CROSS APPLY to join with the function

GoalKicker.com – SQL Notes for Professionals 90


we created. It passes the DepartmentID for each row from the outer table expression (in our case Department
table) and evaluates the function for each row similar to a correlated subquery. The next query uses the OUTER
APPLY in place of CROSS APPLY and hence unlike CROSS APPLY which returned only correlated data, the OUTER
APPLY returns non-correlated data as well, placing NULLs into the missing columns.

CREATE FUNCTION dbo.fn_GetAllEmployeeOfADepartment (@DeptID AS int)


RETURNS TABLE
AS
RETURN
(
SELECT
*
FROM Employee E
WHERE E.DepartmentID = @DeptID
)
GO
SELECT
*
FROM Department D
CROSS APPLY dbo.fn_GetAllEmployeeOfADepartment(D.DepartmentID)
GO
SELECT
*
FROM Department D
OUTER APPLY dbo.fn_GetAllEmployeeOfADepartment(D.DepartmentID)
GO

So now if you are wondering, can we use a simple join in place of the above queries? Then the answer is NO, if you
replace CROSS/OUTER APPLY in the above queries with INNER JOIN/LEFT OUTER JOIN, specify ON clause (something
as 1=1) and run the query, you will get "The multi-part identifier "D.DepartmentID" could not be bound." error. This
is because with JOINs the execution context of outer query is different from the execution context of the function
(or a derived table), and you can not bind a value/variable from the outer query to the function as a parameter.
Hence the APPLY operator is required for such queries.

GoalKicker.com – SQL Notes for Professionals 91


Chapter 29: DELETE
The DELETE statement is used to delete records from a table.

Section 29.1: DELETE all rows


Omitting a WHERE clause will delete all rows from a table.

DELETE FROM Employees

See TRUNCATE documentation for details on how TRUNCATE performance can be better because it ignores triggers
and indexes and logs to just delete the data.

Section 29.2: DELETE certain rows with WHERE


This will delete all rows that match the WHERE criteria.

DELETE FROM Employees


WHERE FName = 'John'

Section 29.3: TRUNCATE clause


Use this to reset the table to the condition at which it was created. This deletes all rows and resets values such as
auto-increment. It also doesn't log each individual row deletion.

TRUNCATE TABLE Employees

Section 29.4: DELETE certain rows based upon comparisons


with other tables
It is possible to DELETE data from a table if it matches (or mismatches) certain data in other tables.

Let's assume we want to DELETEdata from Source once its loaded into Target.

DELETE FROM Source


WHERE EXISTS ( SELECT 1 -- specific value in SELECT doesn't matter
FROM Target
Where Source.ID = Target.ID )

Most common RDBMS implementations (e.g. MySQL, Oracle, PostgresSQL, Teradata) allow tables to be joined
during DELETE allowing more complex comparison in a compact syntax.

Adding complexity to original scenario, let's assume Aggregate is built from Target once a day and does not contain
the same ID but contains the same date. Let us also assume that we want to delete data from Source only after the
aggregate is populated for the day.

On MySQL, Oracle and Teradata this can be done using:

DELETE FROM Source


WHERE Source.ID = TargetSchema.Target.ID
AND TargetSchema.Target.Date = AggregateSchema.Aggregate.Date

In PostgreSQL use:

GoalKicker.com – SQL Notes for Professionals 92

You might also like