Sie sind auf Seite 1von 11

Advanced SQL - exercises

To perform excercises use PostgreSQL DBMS version at least 9.0.


Download installation files for PostgreSQL at following url:
http://www.enterprisedb.com/products-services-training/pgdownload#windows
Warning: do not install PostgreSQL in C:\Program Files. That will result in error. Choose any other
folder like e.g. C:\PostgreSQL.
If you downloaded setup file for PostGreSQL version 9.3, upon successful installation of PostgreSQL, you
can proceed with the offered installation of Stack Builder. It will help you to install add-ons such as
"drivers" to connect to the PostgreSQL system:

If you installed PostgreSQL on Windows operating system, along with the DBMS the tool to access the
DBMS and perform queries -. pgAdmin will be installed. If you have not installed pgAdmin along with
PostgreSQL DBMS, it can be downloaded from http://www.pgadmin.org/download/.
PgAdmin will recognize local instance of postgreSQL server and will create the appropriate object Server.
Using gAdmin program connect to the PostgreSQL DBMS as a user postgres. Create a database e.g.
advancedSQL (right-click on Databases - New Database, Name: advancedSQL, Owner: postgres). Refresh
the content of the Databases sub tree (right-click on Databases - Refresh), select the advancedSQL and
open a query window (Tools - Query Tool).
Set the format of the Date data type in dd/mm/yyyy performing the following command:
set DateStyle ='SQL, DMY'

Windows, partitions, frames and window functions


Excercise 1.
For each course and exam date calculate cumulative number of exams and cumulative average grade.
Cumulative number of exams and cumulative average for particular course and exam date include
exams from that exam date and from previous exam dates for the same course.
Look at the slide Motivation example 1 from the first lectures.
exam
courseID
1
1
1

studID
1
3
1

examDate
01/06/2013
01/06/2013
01/07/2013

grade
1
5
4

Excercise 2.
Calculate total number of points and rank for each student and course.
Look at the slide Motivation example 1 from the first lectures.
studTest
courseID
1
1
1

studID
1
2
3

testType
1DZ
1DZ
1DZ

points
5.00
10.00
5.00

Excercise 3.
Determine the total number of points and ranking for each student in each course. In addition,
determine the average value of total points for the students at the course.
Excercises 4-6.
For excercises 4-6 the relations orgUnit, employee, empPayment and project are used. Their schema and
content is defined with SQL statements in file excercises1.sql. Scheme and sample of the contents of
relations is shown in the following figure.
orgUnit
orgUnitID
1
2
3

empPayment
empID
1
1
1

orgUnitName
Grocery in Radnika
Grocery in Vukovarska
Grocery in Ilica

employee
empDl FName
1
Ana
2
ime
3
Petra
...

LName
Par
Knez
Pili

orgUnitId
1
1
1

supEmpId
1
1

project
projectID
234
764

paymentDate
01/01/2013
01/02/2013
01/03/2013

respEmpID
1
2

amount
4700.00
4700.00
4700.00

beginDate
01/3/2013
05/4/2013

endDate
20/7/2013
15/8/2013

Execute SQL statements from the excercises1.sql.


Excercise 4.
For each payment to the employee calculate the total amount paid along with 2 payments that have
preceded it. Present data in the following format:
LName
Par
Par
Par
Par
Par

FName
Ana
Ana
Ana
Ana
Ana

paymentDate
2013-01-10
2013-02-10
2013-03-10
2013-04-10
2013-05-10

amount
4700.00
4700.00
4700.00
4700.00
4700.00

totAmount
4700.00
9400.00
14100.00
14100.00
14100.00

Excercise 5.
For each organizational unit print information about the best and worst-paid employee in the following
format:
orgUnitName
Grocery in Ilica
Grocery in Ilica

LName
Mili
Buri

FName
Petar
Marija

totAmount
26100.00
96900.00

comment
worst paid
best paid

Excercise 6.
For each employee determine number of salary changes. Presend data in the following format:
LName
Mili
Knez
Jeli
Par

FName
Petar
ime
Marko
Ana

noOfChanges
0
3
0
2

Excercise 7.
For each employee determine the total amount earned and the percentage of the amount of the best
paid employees in an organizational unit, and in the whole company. Present data in the following
format:
orgUnitName
Grocery in Radnika
Grocery in Radnika
Grocery in Radnika
Grocery in Vukovarska
Grocery in Vukovarska

LName
Par
Knez
Pili
Kralj
Jeli

FName
Ana
ime
Petra
Anita
Marko

totAmount
43000.00
40850.00
32400.00
78900.00
67500.00

percOfMaxOU
100.00
95.00
75.34
100.00
85.55

percOfMaxC
44.37
42.15
33.43
81.42
69.65

Recursive queries
Excercise 8.
Find people with whom the person ana123@hotmail.com is connected directly or through a friend.
Consider only friendships in which ana123@hotmail.com (or her friends, friends of friends, etc.). is
initiator (foaf.person1 = 'ana123@hotmail.com'), but also those in which someone else has initiated a
friendship (foaf. Person2 = 'ana123@hotmail.com' and so on.).
foaf
person1

person2

ana123@hotmail.com
zecG@gmail.com
klaraB@gmail.com
zecG@gmail.com
Iva.malic@fer.hr
klaraB@gmail.com
jon.doe@info.hr
cat56@hotmail.com
...

zecG@gmail.com
iva.malic@fer.hr
ana123@hotmail.com
jezV@hotmail.com
jezV@hotmail.com
mia95@yahoo.com
klaraB@gmail.com
jon.doe@info.hr

Excercise 9.
For each month in which the project with code 746 was active, calculate the total amounts paid to
employees on the project for the duration of the project.

Pivoting
Excercise 10.
For each year and month calculate total amount paid in the following format:
year

january

february

march

april

may

june

July

august

48890.00

2013

47840.00

47840.00

48070.00

48840.00

48890.00

Excercise 11.
For employees in organizational units Grocery in Radnika, Grocery in Vukovarska and Grocery in Ilica
print total amount paid in the following format:
year

Grocery in Radnika

Grocery in Vukovarska

Grocery in Ilica

2013

177420.00

116250.00

146400.00

SOLUTIONS:

Windows, partitions, frames and window functions


Excercise 1.
SET DateStyle ='SQL, MDY';
CREATE TABLE exam (
courseID INT
NOT NULL,
studID INT
NOT NULL,
examDate DATE
NOT NULL,
grade
SMALLINT NOT NULL);
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT

INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO

exam
exam
exam
exam
exam
exam
exam
exam

VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES

(1,
(1,
(1,
(1,
(1,
(1,
(2,
(2,

1,
3,
1,
2,
4,
5,
1,
2,

'01/06/2013',
'01/06/2013',
'01/07/2013',
'01/07/2013',
'01/07/2013',
'01/07/2013',
'01/07/2013',
'01/07/2013',

1);
5);
4);
5);
3);
2);
3);
2);

SELECT DISTINCT courseID, examDate


, COUNT(*)
OVER (PARTITION BY courseID ORDER BY courseID, examDate)
cumNoOFExams
, AVG(grade) OVER (PARTITION BY courseID ORDER BY courseID, examDate)
cumAvgGrade
FROM exam
ORDER BY courseID, examDate

Excercise 2.
CREATE TABLE studTest (
courseID int NOT NULL,
studID int NOT NULL,
testType CHAR(5) NOT NULL,
points numeric (5,2) NOT NULL);-INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT

INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO

studTest
studTest
studTest
studTest
studTest
studTest
studTest
studTest
studTest

VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES

(1,
(1,
(1,
(1,
(1,
(1,
(2,
(2,
(2,

1,
2,
3,
1,
2,
3,
1,
2,
3,

'1DZ',
5.00);
'1DZ',
10.00);
'1DZ',
5.00);
'MI', 10.00);
'MI', 5.00);
'MI', 20.00);
'MI', 20.00);
'MI', 25.00);
'MI', 30.00);

SELECT courseID
, studID
, SUM(points) totPoints
, rank () OVER (PARTITION BY courseID ORDER BY SUM(points)DESC) AS rank
FROM studTest
GROUP BY courseID, studID
ORDER BY courseID, rank

Excercise 3.
CREATE TABLE and INSERT statements are eyual to statements in previous excercise.
SELECT courseID
, studID
, SUM (points) totPoints
, rank () OVER (partition by courseID ORDER BY SUM(points) DESC) AS rank
, AVG(SUM(points)) OVER (partition by courseID) AS avgTotPoints
FROM studTest
GROUP BY courseID, studID
ORDER BY courseID, rank

Excercise 4.
select LName, FName, empPayment.paymentDate, amount
, SUM(amount) OVER (PARTITION BY empPayment.empID
ORDER BY paymentDate
ROWS 3 PRECEDING) totAmount
from empPayment, employee
where empPayment.empID = employee.empID

Excercise 5.
To print the comments 'best paid' or 'worst paid' we need conditional statement CASE. This is the syntax
of the CASE statement in PostgreSQL:
CASE WHEN condition THEN result
[WHEN ...]
[ELSE result]
END

Ranking of the employees in the organizational unit where the highest ranked employee is the one with
the highest income:
rank () OVER (PARTITION BY orgUnitName
ORDER BY SUM(amount) DESC)

maxA

Similar expression is used to rank employees according to the lowest income:


rank () OVER (PARTITION BY orgUnitName
ORDER BY SUM(amount)) minA

Notice that by using PARTITION BY orgUnitName we defined partitions so that the same partition
contains tuples belonging to the same organizational unit.
The following query we use to get data about the total amount of incomes and ranks of employees
within their organizational units:
SELECT orgUnitName, employee.empID
, LName, FName, SUM(amount) totAmount
, rank () OVER (PARTITION BY orgUnitName
ORDER BY SUM(amount) DESC) maxA
, rank () OVER (PARTITION BY orgUnitName
ORDER BY SUM(amount))
minA
FROM empPayment, employee, orgUnit
WHERE empPayment.empID = employee.empID
AND employee.orgUnitID = orgUnit.orgUnitID
GROUP BY employee.orgUnitID, orgUnit.orgUnitName
, employee.empID, LName, FName

This is the result of the previous query:


orgUnitName
Grocery in Ilica
Grocery in Ilica
Grocery in Ilica
Grocery in Radnika
Grocery in Radnika
Grocery in Radnika
Grocery in Vukovarska
Grocery in Vukovarska

empID
8
7
6
3
1
2
5
4

LName
Mili
Rendi
Buri
Pili
Par
Knez
Jeli
Kralj

FName
Petar
Slavica
Marija
Petra
Ana
ime
Marko
Anita

totAmount
26100.00
54420.00
96900.00
32400.00
42900.00
43620.00
67500.00
78900.00

maxA
3
2
1
3
2
1
2
1

minA
1
2
3
1
2
3
1
2

The above query is named rankPayment and nested in the outer query:
SELECT rankPayment.*
, CASE
WHEN rankPayment.bestPayed = 1 AND
rankPayment.worstPayed > 1 THEN 'best paid'
WHEN rankPayment.bestPayed > 1 AND
rankPayment.worstPayed = 1 THEN 'worst paid'
END AS comment
FROM (
SELECT orgUnitName, employee.empID
, LName, FName, SUM(amount) totAmount
, rank () OVER (PARTITION BY orgUnitName
ORDER BY SUM(amount) DESC)
bestPayed
, rank () OVER (PARTITION BY orgUnitName
ORDER BY SUM(amount))
worstPayed
FROM empPayment, employee, orgUnit
WHERE empPayment.empID = employee.empID
AND employee.orgUnitID = orgUnit.orgUnitID
GROUP BY employee.orgUnitID, orgUnit.orgUnitName
, employee.empID, LName, FName
) rankPayment
WHERE rankPayment.bestPayed = 1 OR rankPayment.worstPayed = 1

Result:
orgUnitName
Grocery in Ilica
Grocery in Ilica
Grocery in Radnika
Grocery in Radnika
Grocery in Vukovarska
Grocery in Vukovarska

LName
Mili
Buri
Pili
Knez
Jeli
Kralj

FName
Petar
Marija
Petra
ime
Marko
Anita

totAmount
26100.00
96900.00
32400.00
43620.00
67500.00
78900.00

comment
worst paid
best paid
worst paid
best paid
worst paid
best paid

Whatshould be changed in the above query to obtain information on the 2 best and 2 worst paid
employees in each organizational unit?

Excercise 6.
It is necessary to create a partition for each employee (PARTITION BY empPayment.empID), and sort
rows within the partition by payment date (ORDER BY paymentDate). Amount paid for each row is
compared with previously paid amount lag(amount) - by default offset is 1 and lag retrieves row (i.e.
attribute amount) that immediately preceded the current row. Any change in the amount will be
marked with 1 and unchanged amount with value 0.

SELECT employee.empID, LName, FName


, amount, paymentDate
, CASE
WHEN lag(amount) OVER (PARTITION BY empPayment.empID
ORDER BY paymentDate) != amount THEN 1
ELSE 0
END jePromjena
FROM empPayment, employee
WHERE empPayment.empID = employee.empID

Part of the results of the above query:


empID

LName

FName

amount

paymentDate

changed

1
1
1
1
1
1
1
1
1

Par
Par
Par
Par
Par
Par
Par
Par
Par

Ana
Ana
Ana
Ana
Ana
Ana
Ana
Ana
Ana

4700.00
4700.00
4700.00
4700.00
4750.00
4750.00
4900.00
4900.00
4900.00

2013-01-10
2013-02-10
2013-03-10
2013-04-10
2013-05-10
2013-06-10
2013-07-10
2013-08-10
2013-09-10

0
0
0
0
1
0
1
0
0

The above query is named paymentChanges and nested in the outer query. Number of salary changes is
determined by summing column changed.
SELECT LName, FName, SUM(changed) noOfChanges
FROM
(SELECT employee.empID, LName, FName
, CASE
WHEN lag(amount) OVER (PARTITION BY empPayment.empID
ORDER BY paymentDate) != amount THEN 1
ELSE 0
END changed
FROM empPayment, employee
WHERE empPayment.empID = employee.empID) AS paymentChanges
GROUP BY paymentChanges.empID, paymentChanges.LName, paymentChanges.FName

Excercise 7.
SELECT orgUnitName
, LName, FName, SUM(amount) totAmount
, 100* SUM(amount)
/nth_value (SUM(amount),1) OVER (PARTITION BY orgUnitName ORDER BY
SUM(amount) DESC )
percOfMaxOU
, 100* SUM(amount)
/nth_value (SUM(amount),1) OVER (ORDER BY SUM(amount) DESC)
percOfMaxC
FROM empPayment, employee, orgUnit
WHERE empPayment.empID = employee.empID
AND employee.orgUnitID = orgUnit.orgUnitID
GROUP BY employee.orgUnitID, orgUnit.orgUnitName
, employee.empID, LName, FName
ORDER BY employee.orgUnitID, orgUnit.orgUnitName, SUM(amount) DESC

Recursive queries
Excercise 8.
create table foaf (
person1 char(50) not null,
person2 char(50) not null);
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT

INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO

foaf
foaf
foaf
foaf
foaf
foaf
foaf
foaf

VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES

('ana123@hotmail.com',
('zecG@gmail.com',
('klaraB@gmail.com',
('zecG@gmail.com',
('iva.malic@fer.hr',
('klaraB@gmail.com',
('jon.doe@info.hr',
('cat56@hotmail.com',

'zecG@gmail.com');
'iva.malic@fer.hr');
'ana123@hotmail.com');
'jezV@hotmail.com');
'jezV@hotmail.com');
'mia95@yahoo.com');
'klaraB@gmail.com');
'jon.doe@info.hr');

WITH RECURSIVE friends (friend1, friend2) AS


( (SELECT person1 as friend1, person2 as friend2 FROM foaf)
UNION
(SELECT friends.friend1, foaf.person2 as friend2
FROM friends, foaf
WHERE friends.friend2 = foaf.person1)
)
SELECT * FROM friends
WHERE friend1 = 'ana123@hotmail.com'
OR friend2 = 'ana123@hotmail.com'

Excercise 9.
Using CTE we will store store data about employees and superior (parent) employees recursively into a
temporary relation employees.
WITH RECURSIVE employees (empID, supEmpID) AS
( (SELECT empID, supEmpID FROM employee)
UNION
(SELECT employees.empID, employee.supEmpID
FROM employees, employee
WHERE employees.supEmpID = employee.empID)
)
SELECT * FROM employees
WHERE empID = 2 OR supEmpID = 2

Performing the above query we get the following result:


empID supEmpID
2
4
5
2
6
7
8

1
2
2
2
2
2

Employee in charge for the the project (project.respEmpID) with code 746 should be equal to the
superior employee (employees.supEmpID) for the rows shown in the table above.
Since the employee with code 2 has no tuple with supEmpID = 2, and we want his income included in
the total income of employees on the project, we will solve this detail with the following condition:
(project.respEmpID = employees.supEmpID OR
project.respEmpID = employees.empID AND

employees.supEmpID IS NOT NULL)

Complete solution:
WITH RECURSIVE employees (empID, supEmpID) AS
( (SELECT empID, supEmpID FROM employee)
UNION
(SELECT employees.empID, employee.supEmpID
FROM employees, employee
WHERE employees.supEmpID = employee.empID)
)
SELECT EXTRACT (MONTH FROM paymentDate) AS month, SUM(amount) AS totAmount
FROM project, employees, empPayment
WHERE (project.respEmpID = employees.supEmpID OR
project.respEmpID = employees.empID AND
employees.supEmpID IS NOT NULL)
AND empPayment.empID = employees.empID
AND empPayment.paymentDate BETWEEN project.beginDate AND project.endDate
AND project.projectID = 746
GROUP BY month --EXTRACT (MONTH FROM paymentDate)
ORDER BY month --EXTRACT (MONTH FROM paymentDate)

Pivoting
Excercise 10.
CREATE EXTENSION tablefunc;
CREATE TEMP TABLE month
(monthOrd int);
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT

INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO

month
month
month
month
month
month
month
month
month
month
month
month

VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES

( 2);
( 3);
( 4);
( 1);
( 5);
( 6);
( 7);
( 8);
( 9);
(10);
(11);
(12);

SELECT *
FROM crosstab(
'SELECT CAST(EXTRACT(YEAR FROM paymentDate) AS int) AS godina
, CAST(EXTRACT(MONTH FROM paymentDate) AS int) AS month
, CAST(SUM(amount) AS NUMERIC (10,2))
FROM empPayment
GROUP BY godina, month
ORDER BY godina, month'
, 'SELECT monthOrd FROM month ORDER BY monthOrd')
AS pivotTable (godina INT, sijeanj NUMERIC(10,2), veljaa NUMERIC(10,2)
, oujak NUMERIC(10,2), travanj NUMERIC(10,2), svibanj NUMERIC(10,2)
, lipanj NUMERIC(10,2), srpanj NUMERIC(10,2), kolovoz NUMERIC(10,2)
, rujan NUMERIC(10,2), listopad NUMERIC(10,2), studeni NUMERIC(10,2)
, prosinac NUMERIC(10,2))
ORDER BY godina

Excercise 11.
SELECT *
FROM crosstab(
'SELECT CAST(EXTRACT(YEAR FROM paymentDate) AS int) AS godina
, orgUnit.orgUnitName
, CAST(SUM(amount) AS NUMERIC (10,2))
FROM empPayment, employee, orgUnit
WHERE empPayment.empID = employee.empID
AND employee.orgUnitID = orgUnit.orgUnitID
AND orgUnit.orgUnitName IN (''Grocery in Ilica''
, ''Grocery in Radnika'', ''Grocery in Vukovarska'')
GROUP BY godina, orgUnit.orgUnitName
ORDER BY godina, orgUnit.orgUnitName')
AS pivotTable (godina INT, GroceryIlica NUMERIC(10,2)
, GroceryRadnika NUMERIC(10,2)
, GroceryVukovarska NUMERIC(10,2))
ORDER BY godina

Das könnte Ihnen auch gefallen