Table Joining Issues - sql

I have difficulties joining the tables below for the desired query as stated.
Theatre (Theatre#, Name, Address, MainTel);
Production (P#, Title, ProductionDirector, PlayAuthor);
Performance (Per#, P#, Theatre#, pDate, pHour, pMinute, Comments);
Client (Client#, Title, Name, Street, Town, County, telNo, e-mail);
Ticket Purchase (Purchase#, Client#, Per#, PaymentMethod, DeliveryMethod, TotalAmount)
Required Query
The theater name (for each theater) and the names of clients who have the highest spending in that theater
SELECT T.NAME, C.NAME, SUM(TOTALAMOUNT)
FROM TICKETPURCHASE TP,
THEATRE T,
CLIENT C,
PERFORMANCE PER
WHERE TOTALAMOUNT = (SELECT MAX (TOTALAMOUNT)
FROM TICKETPURCHASE TP2,
THEATRE T2,
PERFORMANCE PER2,
CLIENT C2,
PRODUCTION P2
WHERE T2.NAME = T.NAME
AND T2.THEATRE# = PER2.THEATRE#
AND TP2.CLIENT# = C2.CLIENT#
AND TP2.PER# =PER2.PER#
AND PER2.P# = P2.P# )
AND C.CLIENT# = TP.CLIENT#
AND T.THEATRE# = PER.THEATRE#
AND TP.PER# = PER.PER#
AND PER.P# = P.P#
GROUP BY T.NAME, C.NAME, TOTALAMOUNT

From Oracle 12, you can do it all in a single query if you aggregate by the primary keys for the theatre and the client to get the total spent and then rank the spending in the ORDER BY clause and use the row limiting clause to get the first ranked values:
SELECT MAX(T.NAME) AS theatre_name,
MAX(C.NAME) AS client_name,
SUM(TP.TOTALAMOUNT) AS amount_spent
FROM TICKETPURCHASE TP
INNER JOIN PERFORMANCE PER
ON (TP.PER# = PER.PER#)
INNER JOIN THEATRE T
ON (T.THEATRE# = PER.THEATRE#)
INNER JOIN CLIENT C
ON (C.CLIENT# = TP.CLIENT#)
GROUP BY
T.THEATRE#,
C.CLIENT#
ORDER BY
DENSE_RANK() OVER (
PARTITION BY T.THEATRE#
ORDER BY SUM(TP.TOTALAMOUNT) DESC
)
FETCH FIRST ROW WITH TIES;
In earlier versions, you can use the same technique and filter in an outer query:
SELECT theatre_name,
client_name,
amount_spent
FROM (
SELECT MAX(T.NAME) AS theatre_name,
MAX(C.NAME) AS client_name,
SUM(TP.TOTALAMOUNT) AS amount_spent,
DENSE_RANK() OVER (
PARTITION BY T.THEATRE#
ORDER BY SUM(TP.TOTALAMOUNT) DESC
) As rnk
FROM TICKETPURCHASE TP
INNER JOIN PERFORMANCE PER
ON (TP.PER# = PER.PER#)
INNER JOIN THEATRE T
ON (T.THEATRE# = PER.THEATRE#)
INNER JOIN CLIENT C
ON (C.CLIENT# = TP.CLIENT#)
GROUP BY
T.THEATRE#,
C.CLIENT#
)
WHERE rnk = 1;

Related

For each country, report the movie genre with the highest average rates

For each country, report the movie genre with the highest average ratings, and I am missing only one step that i cant figure it out.
Here's my current code:
SELECT c.code AS c_CODE, menres.genre AS GENRE, AVG(RATE) as AVERAGE_rate,MAX(RATE) AS MAXIMUM_rate, MIN(RATE) AS MINIMUM_rate from movirates
leftJOIN movgenres ON movgenres.movieid = movratings.movieid
left JOIN users ON users.userid = movrates.userid
left JOIN c ON c.code = users.city
LEFT JOIN menres ON movenres.genreid = menres.code
GROUP BY menres.genre , c.code
order by c.code asc, avg(rate) desc, menres.genre desc ;
You can use the ROW_NUMBER window function to assign a unique rank to each of your rows:
partitioned by country code
ordered by descendent average rating
Once you get this ranking, you may want to select all those rows which have the highest average rating (which are the same having the ranking equal to 1).
WITH cte AS (
SELECT c.code AS COUNTRY_CODE,
mg.genre AS GENRE,
AVG(rating) AS AVERAGE_RATING,
MAX(rating) AS MAXIMUM_RATING,
MIN(RATING) AS MINIMUM_RATING
FROM moviesratings r
INNER JOIN moviesgenres g ON g.movieid = r.movieid
INNER JOIN users u ON u.userid = r.userid
INNER JOIN countries c ON c.code = u.country
LEFT JOIN mGenres mg ON mg.code = g.genreid
GROUP BY mg.genre,
c.code
ORDER BY c.code,
AVG(rating) DESC,
mg.genre DESC;
)
SELECT *
FROM (SELECT *,
ROW_NUMBER() OVER(
PARTITION BY COUNTRY_CODE,
ORDER BY AVERAGE_RATING) AS rn
FROM cte) ranked_averages
WHERE rn = 1
Note: The code inside the common table expression is equivalent to yours. If you're willing to share your input tables, I may even suggest an improved query.
You should use window function in this case by using rank() then select the first rank only.
with mov_rates(c.code, genre, average, max, min)
as.
select c.code c_code,
e.genre genre,
avg (rate) avg
max (rate) max
min (rate) min
from movrates a
LEFT join movge.nres b on a.movieid = b.movieid
LEFT join users c on a.userid = c.user
LEFT join countr.ies d on c.code = d.code
left join mGenres e on b.genreid = e.code
group by d.country_code, e.x
),
rategenre (rank, c_code, genre, avgrate, max, min)
as
(
select rank() over (partition by c.c order by avgrates asc) rank,
country code,
genre,
average_r.ating,
maximum_rating,
minimum_.ating
from movrate \\just practicing on something
)
selec.t 2
from genre
where rank = 5
Reference:
OVER Clause

sql convert rows into columns in sql server 2008r2

I have a tables called customer, hobby's.
A customer has several hobby's. So if I join my table customer with table hobby I get a result like:
[CustomerName] [HobbyName]
Harry Tennis
Harry Football
What I would like is to see a result like this:
[CustomerName] [HobbyName1] [HobbyName2]
Harry Tennis Football
My current query looks like this:
Select tCustomer.name, tHobby.name
from dbo.customer tCustomer
inner join dbo.hobby tHobby on tHobby.customerid = tCustomer.id
You didn't provide many details on your table structure but you can use PIVOT to get the final result, but in order to use this you will first want to use row_number() to generate a unique sequence for each hobby per customer:
select customer, Hobby1, Hobby2
from
(
Select c.name customer,
h.name hobby,
'Hobby'+
cast(row_number() over(partition by c.id
order by h.name) as varchar(10)) seq
from dbo.customer c
inner join dbo.hobby h
on h.customerid = c.id
) d
pivot
(
max(hobby)
for seq in (hobby1, Hobby2)
) piv;
See SQL Fiddle with Demo. If you don't want to use the PIVOT function, then you could also use a CASE expression and an aggregate function:
select customer,
max(case when seq = 1 then hobby end) hobby1,
max(case when seq = 2 then hobby end) hobby2
from
(
Select c.name customer,
h.name hobby,
row_number() over(partition by c.id
order by h.name) seq
from dbo.customer c
inner join dbo.hobby h
on h.customerid = c.id
) d
group by customer;
See SQL Fiddle with Demo

oracle - maximum per group

University Table - UniversityName, UniversityId
Lease Table - LeaseId, BookId, UniversityId, LeaseDate
Book Table - BookId, UniversityId, Category, PageCount.
For each university, I have to find category that had the most number of books leased.
So, something like
UniversityName Category #OfTimesLeased
I have been playing around with it with some success using Dense_Rank etc - but if there is a tie, only one of them shows up, while I want both of them to show up.
Current Query:
select b.UniversityId, MAX(tempTable.type) KEEP (DENSE_RANK FIRST ORDER BY tempTable.counter DESC)
from book b
join
(select count(l.leaseid) AS counter, b.category, b.universityid
from lease l
join book b
on b.bookid =l.bookid AND b.universityid=r.universityid
group by b.category, b.universityid) tempTable
on counterTable.universityid= b.universityid
group by b.universityid
^Unable to solve the tie issue and get the number of leases for the most leased book type.
Try this
WITH CTE AS
(
SELECT UniversityName, Category, Count(*) NumOfTimesLeased
FROM University u
INNER JOIN Book b on u.UniversityId = b.UniversityId
INNER JOIN Lease l on b.bookid = l.bookid and b.UniversityId = l.UniversityId
GROUP BY UniversityName, Category
),
CTE2 AS (
SELECT UniversityName, Category, NumOfTimesLeased,
RANK() OVER (PARTITION BY UniversityName
ORDER BY NumOfTimesLeased DESC) Rnk
FROM CTE)
SELECT * FROM CTE2 WHERE Rnk = 1
You are on the right track with the analytic functions:
select Univerity, Category, NumLeased
from (select t.*,
row_number() over (partition by university order by Numleased desc) as seqnum
from (select l.university, b.category, count(*) as NumLeased
from lease l join
book b
on l.bookid = b.bookid
group by l.university, b.category
) t
) t
where seqnum = 1
I use the row_number() because you only want the one top value. Rank and dense_rank are more useful when you are looking for values other than "1".
If you want the top values to show up when there is a tie, then use dense_rank instead of row_number. The values will be on different rows.

Select one record from two tables in Oracle

There are three tables:
A table about students: s41071030(sno, sname, ssex, sage, sdept)
A table about course: c41071030(cno, cname, cpno, credit)
A table about selecting courses: sc41071030(sno, cno, grade)
Now, I want select the details about a student whose sdept='CS' and he or she has selected the most courses in department 'CS'.
As with any modestly complex SQL statement, it is best to do 'TDQD' — Test Driven Query Design. Start off with simple parts of the question and build them into a more complex answer.
To find out how many courses each student in the CS department is taking, we write:
SELECT S.Sno, COUNT(*) NumCourses
FROM s41071030 S
JOIN sc41071030 SC ON S.Sno = SC.Sno
WHERE S.Sdept = 'CS'
GROUP BY S.Sno;
We now need to find the largest value of NumCourses:
SELECT MAX(NumCourses) MaxCourses
FROM (SELECT S.Sno, COUNT(*) NumCourses
FROM s41071030 S
JOIN sc41071030 SC ON S.Sno = SC.Sno
WHERE S.Sdept = 'CS'
GROUP BY S.Sno
)
Now we need to join that result with the sub-query, so it is time for a CTE (Common Table Expression):
WITH N AS
(SELECT S.Sno, COUNT(*) NumCourses
FROM s41071030 S
JOIN sc41071030 SC ON S.Sno = SC.Sno
WHERE S.Sdept = 'CS'
GROUP BY S.Sno
)
SELECT N.Sno
FROM N
JOIN (SELECT MAX(NumCourses) MaxCourses FROM N) M
ON M.MaxCourses = N.NumCourses;
And we need to get the student details, so we join that with the student table:
WITH N AS
(SELECT S.Sno, COUNT(*) NumCourses
FROM s41071030 S
JOIN sc41071030 SC ON S.Sno = SC.Sno
WHERE S.Sdept = 'CS'
GROUP BY S.Sno
)
SELECT S.*
FROM s41071030 S
JOIN N ON N.Sno = S.Sno
JOIN (SELECT MAX(NumCourses) MaxCourses FROM N) M
ON M.MaxCourses = N.NumCourses;
Lightly tested SQL: you were warned. To test, run the component queries, making sure you get reasonable results each time. Don't move on to the next query until the previous one is working correctly.
Note that the courses table turns out to be immaterial to the query you are solving.
Also note that this may return several rows if it turns out there are several students all taking the same number of courses and that number is the largest number that any student is taking. (So, if there are 3 students taking 7 courses each, and no student taking more than 7 courses, then you will see 3 rows in the result set.)
Aggregate sc41071030 rows to get the counts.
Join the results to s41071030 to:
filter rows on sdept;
get student details;
RANK() the joined rows on the count values.
Select rows with the ranking of 1.
WITH
aggregated AS (
SELECT
sno,
COUNT(*) AS coursecount
FROM
sc41071030
GROUP BY
sno
),
ranked AS (
SELECT
s.*,
RANK() OVER (ORDER BY agg.coursecount DESC) AS rnk
FROM
s41071030 s
INNER JOIN aggregated agg ON s.sno = agg.sno
WHERE
s.sdept = 'CS'
)
SELECT
sno,
sname,
ssex,
sage,
sdept
FROM
ranked
WHERE
rnk = 1
;

How to count number of different items in SQL

Database structure:
Clubs: ID, ClubName
Teams: ID, TeamName, ClubID
Players: ID, Name
Registrations: PlayerID, TeamID, Start_date, End_date, SeasonID
Clubs own several teams. Players may get registered into several teams (inside same club or into different club) during one year.
I have to generate a query to list all players that have been registered into DIFFERENT CLUBS during one season. So if player swapped teams that were owned by the same club then it doesn't count.
My attempts so far:
SELECT
c.short_name,
p.surname,
r.start_date,
r.end_date,
(select count(r2.id) from ejl_registration as r2
where r2.player_id=r.player_id and r2.season=r.season) as counter
FROM
ejl_registration AS r
left Join ejl_players AS p ON p.id = r.player_id
left Join ejl_teams AS t ON r.team_id = t.id
left Join ejl_clubs AS c ON t.club_id = c.id
WHERE
r.season = '2008'
having counter >1
I can't figure out how to count and show only different clubs... (It's getting too late for clear thinking). I use MySQL.
Report should be like: Player name, Club name, Start_date, End_date
This is a second try at this answer, simplifying it to merely count the distinct clubs, not report a list of club names.
SELECT p.surname, r.start_date, r.end_date, COUNT(DISTINCT c.id) AS counter
FROM ejl_players p
JOIN ejl_registration r ON (r.player_id = p.id)
JOIN ejl_teams t ON (r.team_id = t.id)
JOIN ejl_clubs c ON (t.club_id = c.id)
WHERE r.season = '2008'
GROUP BY p.id
HAVING counter > 1;
Note that since you're using MySQL, you can be pretty flexible with respect to columns in the select-list not matching columns in the GROUP BY clause. Other brands of RDBMS are more strict about the Single-Value Rule.
There's no reason to use a LEFT JOIN as in your example.
Okay, here's the first version of the query:
You have a chain of relationships like the following:
club1 <-- team1 <-- reg1 --> player <-- reg2 --> team2 --> club2
Such that club1 must not be the same as club2.
SELECT p.surname,
CONCAT_WS(',', GROUP_CONCAT(DISTINCT t1.team_name),
GROUP_CONCAT(DISTINCT t2.team_name)) AS teams,
CONCAT_WS(',', GROUP_CONCAT(DISTINCT c1.short_name),
GROUP_CONCAT(DISTINCT c2.short_name)) AS clubs
FROM ejl_players p
-- Find a club where this player is registered
JOIN ejl_registration r1 ON (r1.player_id = p.id)
JOIN ejl_teams t1 ON (r1.team_id = t1.id)
JOIN ejl_clubs c1 ON (t1.club_id = c1.id)
-- Now find another club where this player is registered in the same season
JOIN ejl_registration r2 ON (r2.player_id = p.id AND r1.season = r2.season)
JOIN ejl_teams t2 ON (r2.team_id = t2.id)
JOIN ejl_clubs c2 ON (t2.club_id = c2.id)
-- But the two clubs must not be the same (use < to prevent duplicates)
WHERE c1.id < c2.id
GROUP BY p.id;
Here's a list of players for one season.
SELECT sub.PlayerId
FROM
(
SELECT
r.PlayerId,
(SELECT t.ClubID FROM Teams t WHERE r.TeamID = t.ID) as ClubID
FROM Registrations r
WHERE r.Season = '2008'
) as sub
GROUP BY PlayerId
HAVING COUNT(DISTINCT sub.ClubID) > 1
Here's a list of players and seasons, for all seasons.
SELECT PlayerId, Season
FROM
(
SELECT
r.PlayerId,
r.Season,
(SELECT t.ClubID FROM Teams t WHERE r.TeamID = t.ID) as ClubID
FROM Registrations r
) as sub
GROUP BY PlayerId, Season
HAVING COUNT(DISTINCT sub.ClubID) > 1
By the way, this works in MS SQL.
SELECT p.Name, x.PlayerID, x.SeasonID
FROM (SELECT DISTINCT r.PlayerID, r.SeasonID, t.ClubID
FROM Registrations r
JOIN Teams t ON t.ID = r.TeamID) x
JOIN Players p ON p.ID = x.PlayerID
GROUP BY p.rName, x.PlayerID, x.SeasonID
HAVING COUNT(*) > 1