More queries Outer joins and summary queries

advertisement
More queries
Outer joins and summary queries
Inner and outer joins
• An Inner join only returns matching rows from
two tables
– E.g. if I join the customer and order table, I will only
get customers who have orders.
– If I join staff to orders, I will only get back the staff
who have been in charge of an order.
• An Outer join can either be a left outer join or a
right outer join.
– A left outer join returns all rows from the left table and
only the matching rows from the right table.
– A right outer join returns all rows from the right table
and only the matching rows from the left table.
Example
• ‘Find me all customers that are recorded
along with any orders they have placed’.
• Select all Customers and any orders that
are joined to a customer.
– To make it easier to read, only select the
company name from the customer, and the
order id from the order
• Browse through the results to see if you
can find any nulls in the order fields.
Example of a left join
Select
orders.orderid,
cast(CompanyName as char(30))as
Company
from Customers left join Orders
on Customers.CustomerId =
Orders.CustomerId
A section of the output
10808
10347
10386
10414
10512
10581
10650
10725
NULL
10408
10480
10634
10763
10789
Old World Delicatessen
Familia Arquibaldo
Familia Arquibaldo
Familia Arquibaldo
Familia Arquibaldo
Familia Arquibaldo
Familia Arquibaldo
Familia Arquibaldo
FISSA Fabrica Inter. Salchicha
Folies gourmandes
Folies gourmandes
Folies gourmandes
Folies gourmandes
Folies gourmandes
• Note
– the customer
whose
company
name is FISSA
Fabrica Inter.
Salchicha is
registered,
– but there are
no orders
registered
against it
Uses of Outer Joins
• These can be used to show the difference
between two tables.
– Difference using an outer join
• Get all the rows from one side of the join and only
the matching rows from the other side
• This means that some of the resultant rows will
have NULLs in columns originating from one side
of the join.
• Display only those with nulls on the fully populated
side.
Using our example for
Intersection
• ‘Find me all customers that are recorded
that have never placed an order’.
• 1. Select all Customers and any orders
that are joined to a customer.
• 2. List only those rows that DO NOT have
a NULL value on the order side of the join.
Query
Select distinct
Customers.CustomerId,
cast(CompanyName as char(30))as
Company
from Customers left join Orders on
Customers.CustomerId =
Orders.CustomerId
where orders.customerId is not null
Results for Intersection
CustomerId
---------ALFKI
ANATR
ANTON
AROUT
BERGS
BLAUS
BLONP
BOLID
BONAP
Company
-----------------------------Alfreds Futterkiste
Ana Trujillo Emparedados y hel
Antonio Moreno Taquería
Around the Horn
Berglunds snabbköp
Blauer See Delikatessen
Blondesddsl père et fils
Bólido Comidas preparadas
Bon app'
………..89 rows.
Note about intersection
• This should be the same as an inner join:
– Select distinct
Customers.CustomerId, cast
(CompanyName as Char(30)) as
Company from Customers join Orders
on Customers.CustomerId =
Orders.CustomerId
– Run the two and see that the result is the
same.
Using our example for
Difference
• ‘Find me all customers that are recorded
that have never placed an order’.
• 1. Select all Customers and any orders
that are joined to a customer.
• 2. List only those rows that have a NULL
value on the order side of the join.
Query
Select
Customers.CustomerId,
cast(CompanyName as char(30))as
Company,
Orders.CustomerId
from Customers left join Orders on
Customers.CustomerId =
Orders.CustomerId
where orders.customerId is null
Results
CustomerId
---------FISSA
PARIS
Company
-----------------------------FISSA Fabrica Inter. Salchicha
Paris spécialités
CustomerId
---------NULL
NULL
(2 row(s) affected)
• Note: In reality, the output would make more
sense if we left out the Orders.CustomerId,
because we know that it is null anyway.
More on outer joins
• Which side do we use?
– If we have a 1:0 or many relationship, then the 1 side
is essential
– E.g. we can ask ‘what products do we have that have
not been ordered?’, but it makes no sense to ask
‘what order details do we have that don’t match a
product?’
– Why? Because we have used the product code as a
foreign key in the [order details], thereby ensuring that
an [order details] row cannot be added unless there is
a corresponding product to order!
– In the Northwind, all the products have been ordered
at least once, so we’ll use a different pair of tables.
Look at two tables
• Each employee (employeeterritories table) covers one or
more territory (territories table)
• An employee cannot cover a territory that does not exist:
select EmployeeId, TerritoryDescription
from employeeterritories left join territories
on territories.territoryId = employeeterritories.territoryId
– This shows no nulls
• but there may be no employee covering a territory:
select EmployeeId, TerritoryDescription
from employeeterritories right join territories
on territories.territoryId = employeeterritories.territoryId
• Shows that Columbia, Austin, Bentonville and Dallas
have no employees covering them.
And another thing
• The order of the join is important:
– Employees left join Employeeterritories
– Is the same as
– Employeeterritories right join Employees
• And similarly:
– Employeeterritories left join Employees
– Is the same as
– Employees right join Employeeterritories
Tables joined to themselves.
• This generally takes place in a
hierarchy. Two commonplace
examples are
• A part is part of a part
– E.g. a bulb is part of a light.
– A light is part of a front grille.
• An employee reports to an employee
Example
insert into Part (PartNo, PartDesc)
create table part
values (1, 'Biggest part')
( PartNo numeric (4,0)
insert into Part (PartNo, PartDesc)
unique not null,
values (2, 'Quite big part')
PartDesc varchar(20), insert into Part values (3,
'Component part',1)
PartOf numeric (4,0),
primary key (PartNo), insert into Part values (4,'small
part',3)
foreign key (PartOf)
select * from part
references
select * from part as component
Part(PartNo)
join part as container on
)
component.partof =
container.partno
Tables joined to themselves
• In the Northwind database, the employees
table is joined to itself, by the ‘reportsto’
field.
• See tutorial 6 and try to list all employees
and their bosses.
Categories
Shippers
Orders
CategoryId
CategoryName
Description
Picture
ShipperId
CompanyName
Phone
OrderDate
RequiredDate
ShippedDate
Freight
ShipName
ShipAddress
ShipCity
ShipRegion
ShipPostalCode
ShipCountry
OrderId
*ShipVia
*CustomerId
*EmployeeId
Products
Suppliers
SupplierId
CompanyName
ContactName
ContactTitle
Address
City
Region
PostalCode
Country
Phone
Fax
HomePage
ProductId
ProductName
*CategoryId
QuantityPerUnit
UnitPrice
UnitsInStock
UnitsOnOrder
ReorderLevel
Discontinued
*SupplierId
Order details
*OrderId
*ProductId
UnitPrice
Quantity
Discount
Note:
ShipVia is the name of
the foreign key in Order
Details that refers to
the Shippers entity.
ReportsTo is the name of
the foreign key in the
recursive relationship
in the Employee entity
NorthWind ERD
Customers
CustomerId
CompanyName
ContactName
ContactTitle
Address
City
Region
PostalCode
Country
Phone
Fax
Employee
EmployeeId
LastName
FirstName
Title
TitleOfCourtesy
BirthDate
HireDate
Address
City
Region
PostalCode
Country
HomePhone
Extension
Photo
Notes
PhotoPath
*ReportsTo
Summary Queries
• Summary queries involve multiple rows in a table.
• The rows are GROUPED, using the GROUP BY clause.
• A function (MIN, MAX, AVG, SUM, COUNT) operates on
one of the columns of the table.
– E.g. select count(*) from products.
– This will return the number of product ROWS in the table.
– Select min(unitprice) from products
• Include or exclude grouped rows by using the HAVING
clause
Summary
• Summarise the income by product, still
ordered in the same way, giving totals for
each product and totals for each category.
• NOTE:
– There are two summaries here – one per
product and one per category.
– Use the ‘WITH CUBE’ clause.
Query
Select categoryName,
cast(ProductName as char(25)) as Product,
cast (sum(([order
details].unitPrice*Quantity)-Discount) as
decimal(10,2))as income
from
categories join products on
categories.categoryid = products.categoryid
join [order details] on
products.productid = [order
details].productid
group by categoryname, productname with cube
categoryName
--------------Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Condiments
Condiments
Product
------------------------Chai
Chang
Chartreuse verte
Côte de Blaye
Guaraná Fantástica
Ipoh Coffee
Lakkalikööri
Laughing Lumberjack Lager
Outback Lager
Rhönbräu Klosterbier
Sasquatch Ale
Steeleye Stout
NULL
Aniseed Syrup
Chef Anton's Cajun Season
income
-----------14274.65
18554.70
13148.80
149983.10
4779.70
25077.80
16791.95
2561.40
11469.55
8648.15
6677.05
14535.10
Summary
286501.95
row
3079.80
9423.30
With Rollup
• ROLLUP specifies that, in addition to the usual
rows provided by GROUP BY, summary rows
are introduced into the result set.
• Groups are summarized in a hierarchical order,
from the lowest level in the group to the highest.
• The group hierarchy is determined by the order
in which the grouping columns are specified.
• Changing the order of the grouping columns
can affect the number of rows produced in the
result set.
Example
Select categoryname,
count(*)as ‘Number of products’
from
categories join products on
products.categoryId =
categories.categoryId
group by categoryname with
rollup;
Result
categoryname
--------------Beverages
Condiments
Confections
Dairy Products
Grains/Cereals
Meat/Poultry
Produce
Seafood
NULL
Number of products
-----------------12
12
13
10
7
6
5
12
77
(9 row(s) affected)
Where and Having
• The ‘WHERE’ allows filtering on rows.
– e.g. Show all order details where the value of
the order detail is > €5,000
• The ‘HAVING’ allows filtering on summary
rows.
– e.g. Show all orders where the value of the
order is > €10,000
Where
• Return all orderlines that cost more than €8,000
Use Northwind
/* Switches the database*/
Select OrderId,
cast(((UnitPrice*Quantity)- Discount)
/* price per order detail */
as decimal(10,2))
/* format for cast */
as OrderLineValue
/* name the derived column */
from [Order details]
where(UnitPrice*Quantity)- Discount >8000
/* where the cost of the row is > 8,000
output
OrderId
----------10353
10372
10417
10424
10865
10889
10897
10981
OrderLineValue
-------------10539.80
8431.75
10540.00
10329.00
15809.95
10540.00
9903.20
15810.00
(8 row(s) affected)
Having
• Return all Orders that are worth more than
€10,000
Use Northwind
Select Orders.OrderId,
cast(sum((UnitPrice*Quantity)Discount)as decimal(10,2))as OrderValue
from Orders join [Order details] on
Orders.OrderId = [Order
details].OrderId
group by Orders.OrderId
having sum((UnitPrice*Quantity)Discount) >10000
Having – with comments
Select Orders.OrderId, /* the order number */
cast
/* format the output */
(sum
/* sum over the order */
((UnitPrice*Quantity)- Discount)
/* price per order detail */
as decimal(10,2)) /* format for cast */
as OrderValue
/* name the summary column */
from Orders join [Order details] on Orders.OrderId
= [Order details].OrderId
group by Orders.OrderId
/* group the order details by order */
having sum((UnitPrice*Quantity)- Discount) >10000
/* sale value of order > 10000 */
output
OrderId
----------10353
10372
10417
10424
10479
10515
10540
10691
10817
10865
10889
10897
10981
11030
OrderValue
-----------10741.20
12280.20
11282.70
11492.60
10495.60
10588.05
10191.70
10164.80
11490.25
17249.90
11380.00
10835.24
15810.00
16321.15
(14 row(s) affected)
Tables joined to themselves.
• This generally takes place in a
hierarchy. Two commonplace
examples are
• A part is part of a part
– E.g. a bulb is part of a light.
– A light is part of a front grille.
• An employee reports to an employee
Example
create table part
( PartNo numeric (4,0) insert into Part (PartNo, PartDesc)
values (1, 'Biggest part')
unique not null,
PartDesc varchar(20), insert into Part (PartNo, PartDesc)
values (2, 'Quite big part')
PartOf numeric (4,0), insert into Part values (3,
'Component part',1)
primary key (PartNo),
insert into Part values (4,'small
foreign key (PartOf)
part',3)
references
select * from part
Part(PartNo)
select * from part as component
)
join part as container on
component.partof =
container.partno
Categories
Shippers
Orders
CategoryId
CategoryName
Description
Picture
ShipperId
CompanyName
Phone
OrderDate
RequiredDate
ShippedDate
Freight
ShipName
ShipAddress
ShipCity
ShipRegion
ShipPostalCode
ShipCountry
OrderId
*ShipVia
*CustomerId
*EmployeeId
Products
Suppliers
SupplierId
CompanyName
ContactName
ContactTitle
Address
City
Region
PostalCode
Country
Phone
Fax
HomePage
ProductId
ProductName
*CategoryId
QuantityPerUnit
UnitPrice
UnitsInStock
UnitsOnOrder
ReorderLevel
Discontinued
*SupplierId
Order details
*OrderId
*ProductId
UnitPrice
Quantity
Discount
Note:
ShipVia is the name of
the foreign key in Order
Details that refers to
the Shippers entity.
ReportsTo is the name of
the foreign key in the
recursive relationship
in the Employee entity
NorthWind ERD
Customers
CustomerId
CompanyName
ContactName
ContactTitle
Address
City
Region
PostalCode
Country
Phone
Fax
Employee
EmployeeId
LastName
FirstName
Title
TitleOfCourtesy
BirthDate
HireDate
Address
City
Region
PostalCode
Country
HomePhone
Extension
Photo
Notes
PhotoPath
*ReportsTo
Summary Queries
• Summary queries involve multiple rows in a table.
• The rows are GROUPED, using the GROUP BY clause.
• A function (MIN, MAX, AVG, SUM, COUNT) operates on
one of the columns of the table.
– E.g. select count(*) from products.
– This will return the number of product ROWS in the table.
– Select min(unitprice) from products
• Include or exclude grouped rows by using the HAVING
clause
Summary
• Summarise the income by product, still
ordered in the same way, giving totals for
each product and totals for each category.
• NOTE:
– There are two summaries here – one per
product and one per category.
– Use the ‘WITH CUBE’ clause.
Query
Select categoryName,
cast(ProductName as char(25)) as Product,
cast (sum(([order
details].unitPrice*Quantity)-Discount) as
decimal(10,2))as income
from
categories join products on
categories.categoryid = products.categoryid
join [order details] on
products.productid = [order
details].productid
group by categoryname, productname with cube
categoryName
--------------Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Beverages
Condiments
Condiments
Product
------------------------Chai
Chang
Chartreuse verte
Côte de Blaye
Guaraná Fantástica
Ipoh Coffee
Lakkalikööri
Laughing Lumberjack Lager
Outback Lager
Rhönbräu Klosterbier
Sasquatch Ale
Steeleye Stout
NULL
Aniseed Syrup
Chef Anton's Cajun Season
income
-----------14274.65
18554.70
13148.80
149983.10
4779.70
25077.80
16791.95
2561.40
11469.55
8648.15
6677.05
14535.10
Summary
286501.95
row
3079.80
9423.30
With Rollup
• ROLLUP specifies that, in addition to the usual
rows provided by GROUP BY, summary rows
are introduced into the result set.
• Groups are summarized in a hierarchical order,
from the lowest level in the group to the highest.
• The group hierarchy is determined by the order
in which the grouping columns are specified.
• Changing the order of the grouping columns
can affect the number of rows produced in the
result set.
Example
Select categoryname,
count(*)as ‘Number of products’
from
categories join products on
products.categoryId =
categories.categoryId
group by categoryname with
rollup;
Result
categoryname
--------------Beverages
Condiments
Confections
Dairy Products
Grains/Cereals
Meat/Poultry
Produce
Seafood
NULL
Number of products
-----------------12
12
13
10
7
6
5
12
77
(9 row(s) affected)
Where and Having
• The ‘WHERE’ allows filtering on rows.
– e.g. Show all order details where the value of
the order detail is > €5,000
• The ‘HAVING’ allows filtering on summary
rows.
– e.g. Show all orders where the value of the
order is > €10,000
Where
• Return all orderlines that cost more than €8,000
Use Northwind
/* Switches the database*/
Select OrderId,
cast(((UnitPrice*Quantity)- Discount)
/* price per order detail */
as decimal(10,2))
/* format for cast */
as OrderLineValue
/* name the derived column */
from [Order details]
where(UnitPrice*Quantity)- Discount >8000
/* where the cost of the row is > 8,000
output
OrderId
----------10353
10372
10417
10424
10865
10889
10897
10981
OrderLineValue
-------------10539.80
8431.75
10540.00
10329.00
15809.95
10540.00
9903.20
15810.00
(8 row(s) affected)
Having
• Return all Orders that are worth more than
€10,000
Use Northwind
Select Orders.OrderId,
cast(sum((UnitPrice*Quantity)Discount)as decimal(10,2))as OrderValue
from Orders join [Order details] on
Orders.OrderId = [Order
details].OrderId
group by Orders.OrderId
having sum((UnitPrice*Quantity)Discount) >10000
Having – with comments
Select Orders.OrderId, /* the order number */
cast
/* format the output */
(sum
/* sum over the order */
((UnitPrice*Quantity)- Discount)
/* price per order detail */
as decimal(10,2)) /* format for cast */
as OrderValue
/* name the summary column */
from Orders join [Order details] on Orders.OrderId
= [Order details].OrderId
group by Orders.OrderId
/* group the order details by order */
having sum((UnitPrice*Quantity)- Discount) >10000
/* sale value of order > 10000 */
output
OrderId
----------10353
10372
10417
10424
10479
10515
10540
10691
10817
10865
10889
10897
10981
11030
OrderValue
-----------10741.20
12280.20
11282.70
11492.60
10495.60
10588.05
10191.70
10164.80
11490.25
17249.90
11380.00
10835.24
15810.00
16321.15
(14 row(s) affected)
Download