Oracle sql query with "non-attribute" function. Ora-00935 - sql

I have simple database:
Paintings {
PAINTING_ID
PAINTING_NAME
AUTHOR
MUSEUM
}
Museums {
MUSEUM_ID
MUSEUM_NAME
}
Authors {
AUTHOR_ID
AUTHOR_NAME
}
AUTHOR and MUSEUM in paintings are foreign keys.
I have the task:
Display name for museum, that has the largest number of paintings of author id 6.
I tried some things:
SELECT MUSEUM.MUSEUM_NAME
FROM PAINTINGS
INNER JOIN AUTHORS
ON AUTHORS.AUTHOR_ID = PAINTINGS.AUTHOR
INNER JOIN MUSEUMS
ON MUSEUMS.MUSEUM_ID = PAINTINGS.MUSEUM
--WHERE AUTHORS.AUTHOR_ID = 6
GROUP BY MUSEUM_ID
HAVING MAX(COUNT(AUTHORS.AUTHOR_ID = 6)) // Ora-00935
or
SELECT COUNT()
FROM PAINTINGS
WHERE PAINTINGS.AUTHOR = 6
It looks like I must use aggregate function or sub-query function instead attribute, that actually impossible.

From Oracle 12, you can find the ID of the museum(s) with largest number of paintings using:
SELECT museum_id
FROM paintings
GROUP BY museum_id
ORDER BY COUNT(*) DESC
FETCH FIRST ROW WITH TIES
to restrict it to a single author you can add a WHERE filter:
SELECT museum_id
FROM paintings
WHERE author = 6
GROUP BY museum_id
ORDER BY COUNT(*) DESC
FETCH FIRST ROW WITH TIES
If you want the name of the museum then you can JOIN to the museum table (and either include museum_name in the GROUP BY clause or, because there is a correspondence from museum_id to museum_name, you can use an aggregation function to find the name):
SELECT MAX(museum_name) AS museum_name
FROM paintings p
INNER JOIN museums m
ON (p.museum_id = m.museum_id)
WHERE p.author = 6
GROUP BY p.museum_id
ORDER BY COUNT(*) DESC
FETCH FIRST ROW WITH TIES

Related

SQL aggregate functions, inner join

I am working on writing a sql to get the SID and SNAME. In this task, I need to count which team win the max number of League and find out the SID.
Leagues(LID, CHAMPION_TID)
LID: League ID ; CHAMPION_TID: champion team ID
SUPPORT(SID, LID)
SPONSORS(SID, SNAME)
PRIMARY KEY: LID,SID
Now, I can find out which team win the max number of League through the following SQL:
SELECT
MAX(y.cham)
FROM
(SELECT
CHAMPION_TID, COUNT(L.CHAMPION_TID) AS cham
FROM
LEAGUES L
GROUP BY
L.CHAMPION_TID) y, LEAGUES L
WHERE
y.CHAMPION_TID = L.CHAMPION_TID;
I am confusing in the following step. My idea get the LID, then use the join table to display SID and SNAME. But I suck in this step.
SELECT L.LID, MAX(y.cham)
FROM
(SELECT CHAMPION_TID, COUNT(L.CHAMPION_TID) AS cham
FROM LEAGUES L
GROUP BY L.CHAMPION_TID) y, LEAGUES L
WHERE
y.CHAMPION_TID = L.CHAMPION_TID
You can use the following to find the Sponsor ID and Sponsor Name:
SELECT DISTINCT
sp.SID,
sp.SNAME
FROM
LEAGUES l3
INNER JOIN support s ON
l3.LID = s.LID
INNER JOIN SPONSORS sp ON
s.SID = sp.SID
WHERE
l3.CHAMPION_TID IN (
SELECT
l2.CHAMPION_TID
FROM
LEAGUES l2
GROUP BY
l2.CHAMPION_TID
HAVING
count(l2.CHAMPION_TID) = (
SELECT
count(l1.CHAMPION_TID)
FROM
LEAGUES l1
GROUP BY
l1.CHAMPION_TID
ORDER BY
count(l1.CHAMPION_TID) DESC
FETCH FIRST 1 ROW ONLY
)
);
It finds the count of CHAMPION_TID in LEAGUES, orders it by desc (such that the highest count is always on top), then uses it to find the associated CHAMPION_TID. It handles ties for max(count(CHAMPION_TID)) as well :)
If fetch first 1 row only does not work, you can use select top 1 l1.CHAMPION_TID...
Here is a working demo using Postgres.

How to select the highest value after a count() | Sql Oracle

This is my query:
SELECT f.name, COUNT(*) as num_books
from author f
JOIN book b on b.tittle = f.book
Group by f.name
Which gives me this table:
NAME NUM_BOOKS
-------------------------------------------------- ----------
Dyremann 2
Nam mann 1
Thomas 1
Asgeir 1
Tullemann 5
Plantemann 1
Beste forfatter 1
Fagmann 5
Lars 1
Hans 1
Svein Arne 1
How could I easly alter the query to only display the author with the highest amount of released books? (While keeping in mind I'm rather new to sql)
Oracle, and as far as I know - only Oracle, allows you to nest two aggregate functions.
SELECT max (f.name) keep (dense_rank last order by count (*)) as name
from author f
JOIN book b on b.tittle = f.book
Group by f.name
In order to get ALL top authors:
select name
from (SELECT f.name,rank () over (order by count(*) desc) as rnk
from author f
JOIN book b on b.tittle = f.book
Group by f.name
)
where rnk = 1
Since Oracle 12c:
SELECT f.name
from author f
JOIN book b on b.tittle = f.book
Group by f.name
order by count (*) desc
fetch first row /* with ties (optional, in order to get all top authors) */
The best way to do is to use:
SELECT f.name, COUNT(*) as num_books
from author f
JOIN book b on b.tittle = f.book
Group by f.name
Order by num_books DESC
FETCH FIRST ROW ONLY
This will order the results from biggest to smallest and return the first result.
1) Oracle Specific : ( Using ROWNUM, For Postgres/MySql use limit )
select * from
(SELECT f.name, COUNT(*) as num_books
from author f
JOIN book b on b.tittle = f.book
Group by f.name order by num_books desc )
where ROWNUM = 1
2) General Query for all databases :
select f.name,count(*) as max_num_books from author f
JOIN book b on b.tittle = f.book
Group by f.name
having count(*) =
(select max(num_books)
from
(SELECT f.name, COUNT(*) as num_books
from author f
JOIN book b on b.tittle = f.book
Group by f.name)
);
I am not sure why you need a join in the first place. It appears that the author table has a column book - why is it not enough to count(book) from that table, grouping by name? This arrangement is very strange - the author table should only have author properties, the author name should be in the title table, but you do join on author.book = book.title which seems to suggest that you do, in fact, have that strange arrangement (and therefore you don't need a join). Also, having a table and a column (in another table) share the same name, book, is a practice best to be avoided.
The most elementary solution (not the most efficient though), in this case, is
select name, count(book) as max_num_books
from author
group by name
having count(book) = (select max(count(book) from author group by name);
The subquery groups by name, and then it selects the max over all group counts. The outer query selects the names that have a book count equal to this maximum. The subquery returns a single row in a single column - a single value. Such a query is called a "scalar" subquery and can be used wherever a single value is needed, such as the HAVING clause of the outer query. (It's in the HAVING clause and not a WHERE clause, since it refers to group properties - count(book) - and not to individual row properties).
The more efficient solution is as Dudu showed:
select name, ct as max_num_books
from ( select name, count(*) as ct, rank() over (order by count(*) desc) rnk
from author
group by name
)
where rnk = 1;

Oracle sql - referencing tables

My school task was to get names from my movie database actors which play in movies with highest ratings
I made it this way and it works :
select name,surname
from actor
where ACTORID in(
select actorid
from actor_movie
where MOVIEID in (
select movieid
from movie
where RATINGID in (
select ratingid
from rating
where PERCENT_CSFD = (
select max(percent_csfd)
from rating
)
)
)
);
the output is :
Gary Oldman
Sigourney Weaver
...but I'd like to also add to this select mentioned movie and its rating. It accessible in inner selects but I don't know how to join it with outer select in which i can work just with rows found in Actor Table.
Thank you for your answers.
You just need to join the tables properly. Afterwards you can simply add the columns you´d like to select. The final select could be looking like this.
select ac.name, ac.surname, -- go on selecting from the different tables
from actor ac
inner join actor_movie amo
on amo.actorid = ac.actorid
inner join movie mo
on amo.movieid = mo.movieid
inner join rating ra
on ra.ratingid = mo.ratingid
where ra.PERCENT_CSFD =
(select max(percent_csfd)
from rating)
A way to get your result with a slightly different method could be something like:
select *
from
(
select name, surname, percent_csfd, row_number() over ( order by percent_csfd desc) as rank
from actor
inner join actor_movie
using (actorId)
inner join movie
using (movieId)
inner join rating
using(ratingId)
(
where rank = 1
This uses row_number to evaluate the "rank" of the movie(s) and then filter for the movie(s) with the highest rating.

sql query to select matching rows for all or nothing criteria

I have a table of cars where each car belongs to a company. In another table I have a list of company locations by city.
I want to select all cars from the cars table whose company has locations on all cities passed into the stored procedure, otherwise exclude those cars all together even if it falls short of one city.
So, I've tried something like:
select id, cartype from cars where companyid in
(
select id from locations where cityid in
(
select id from cities
)
)
This doesn't work as it obviously satisfies the condition if ANY of the cities are in the list, not all of them.
It sounds like a group by count, but can't make it work with what I tried.
I"m using MS SQL 2005
One example:
select id, cartype from cars c
where ( select count(1) from cities where id in (...))
= ( select count(distinct cityid)
from locations
where c.companyid = locations.id and cityid in (...) )
Maybe try counting all the cities, and then select the car if the company has the same number of distinct location cities are there are total cities.
SELECT id, cartype FROM cars
WHERE
--Subquery to find the number of locations belonging to car's company
(SELECT count(distinct cities.id) FROM cities
INNER JOIN locations on locations.cityid = cities.id
WHERE locations.companyId = cars.companyId)
=
--Subquery to find the total number of locations
(SELECT count(distinct cities.id) FROM cities)
I haven't tested this, and it may not be the most efficient query, but I think this might work.
Try this
SELECT e.*
FROM cars e
WHERE NOT EXISTS (
SELECT 1
FROM Cities p
WHERE p.location = e.Location
)

sort sql query by values in another table

I am querying a table named artists, but I would like to sort the response based on a table named paintings (an artist has_many paintings - the painting table has an artist_id column).
To be more specific, I want to sort the artists by their most recent painting (paintings have a column named date_created). Does anyone know how this could be done?
Ideally this should be done using ANSI joins:
SELECT DISTINCT a.artist
FROM artists a
INNER JOIN paintings p
ON a.artistID = p.artistID
ORDER BY p.date_created desc
Perhaps something like this, depending on the specifics of your schema?
SELECT DISTINCT artists.* FROM
artists, paintings
WHERE artists.id = paintings.artist_id
ORDER BY paintings.painting_date DESC;
This will join the two tables on the artist id, and then order by their painting dates. DISTINCT ensures you only get one row per artist.
This will only return each artist once, with the latest date_created value for that artist.
SELECT artists.name, paintings.date_created
FROM artists JOIN (
SELECT artist_id, MAX(date_created) as date_created FROM paintings GROUP BY artist_id
) paintings ON artists.id = paintings.artist_id
ORDER BY paintings.date_created DESC
If I understand your requirement correctly:
1) Write an aggregation query that returns each artist and his/her latest painting;
2) Use it as a sub-query, joining it to the artists table;
3) SELECT columns from the join, ordering by the date of latest painting.
You can create a query:
select artistid, max(paintingdate)
from paintings
group by artistid
and then join to that as an inline-view:
select artistname from artist
inner join
(
select artistid, max(paintingdate) as latestdate
from paintings
group by artistid
) as Foo
on artist.artistid = Foo.artistid
order by latestdate desc