I need a query that retrieves this result - sql

I have a table of 3 columns:
AuthorID (id can be repeated)
JournalName (name can be repeated)
AuthorScore
I need a query that gets JournalName and the count of all authors having their maximum score in this journal.
Thank you in advance.

select
maxscorejournalinstances.journalname,
COUNT(*) as maxscorecount
from
(
select
journalname
from
foo inner join
(
select
authorid,
MAX(authorscore) as maxscore
from
foo
group by
authorid
) maxauthorscores
on foo.AuthorId = maxauthorscores.AuthorId
and foo.AuthorScore = maxauthorscores.maxscore
) maxscorejournalinstances
group by
maxscorejournalinstances.JournalName
Note that if an author has the same high score in two or more journals, each of those journals will be included in the resultset.

SELECT AuthorID, MAX(AuthorScore) as AuthorScore,
(
SELECT JournalName
FROM tab t2
WHERE t1.AuthorID = t2.AuthorID AND t2.AuthorScore = MAX(t1.AuthorScore)
) as JournalName
FROM tab t1
GROUP BY AuthorID

select
x.journalname, count(x.authorid)
from tableX x
inner join
(
select authorid, max(authorscore) max_authorscore
from tableX
group by authorid
) tmp on x.authorid=tmp.authorid and x.authorscore=tmp.max_authorscore
group by journalname

Something like this may work.
select authorid, journalname, authorscore, max(authorscore) over(parition by authorid)
from <table>
order by journalname
Doing some research on sql olap function should point you in the right direction if this doesn't work.

Sounds like you need 2 queries as the data returned can not be returned in one record set where the data layout is simple to see.
The count of authors per journal,
select JournalName, count(distinct AuthorID)
from table
group by JournalName
The author's max score per journal,
select JournalName, AuthorID, max(AuthorScore)
from table
group by JournalName, AuthorID

Related

Subquery and normal query comes out with different results

I'm a beginner of the oracle, currently, I'm doing a question using subquery(without JOIN) and normal (with JOIN) query, but at the end, the results are different from this two query,
I can't figure out this problem, does anyone know?
The question is asking about list the dog owner details which has booked at least twice in this platform
SELECT PET_OWNER.Owner_id,Oname,OAdd,COUNT(*) AS BOOKING
FROM PET_OWNER
WHERE Owner_id IN(
SELECT Owner_id
FROM PET
WHERE PType = 'DOG' AND Pet_id IN(SELECT Pet_id FROM BOOKING))
GROUP BY PET_OWNER.Owner_id,Oname,OAdd
HAVING COUNT(*) >=2
ORDER BY PET_OWNER.Owner_id;
This subquery shows no rows selected,
SELECT PET_OWNER.Owner_id,Oname,OAdd,COUNT(*) AS BOOKING
FROM PET_OWNER,PET,BOOKING
WHERE PET_OWNER.Owner_id = PET.Owner_id AND
PET.Pet_id = BOOKING.Pet_id AND
PType = 'DOG'
GROUP BY PET_OWNER.Owner_id,Oname,OAdd
HAVING COUNT(*) >=2
ORDER BY PET_OWNER.Owner_id;
this query shows 10 records which are the correct answer for this question
I expected these two queries come out with the same result but it is not
does anyone know what is wrong with it?
can anyone show me how to convert this code to subquery?
Because duplicated join key will cause duplicatation in result.
In your case, the Owner_id should be non-unique in the PET table.
It is still possible to get the correct answer by using join. And as the owner_id in the subquery t is unique, so the execution plan should be same with the subquery version.
select p.* from Pet_Owner p
join (
select PET.Owner_id
from PET
inner join Booking on Booking.Pet_id = PET.Pet_id
where pType = 'DOG'
group by PET.Owner_id
having count(1) >= 2) t
on t.Owner_id = p.Owner_id
order by p.Owner_id
By the way, your SQL code is so old-school as it is in ANSI-89, while the join syntax is already in ANSI-92. I know many school teachers still love the old style, I hope you can read both, but only write code in ANSI-92 way.
What happen is that it will give you distinct values on your PET_OWNER.Owner_id,Oname,OAdd. So what we need is to group by owner_id first.
Here's your query. get first those owner_id with count() >= 2 as subquery
select * from Pet_Owner where Owner_id in (
select t1.Owner_id from PET_OWNER t1
inner join PET t2 on t1.Owner_id = t2.Owner_id
inner join Booking t3 on t3.Pet_id = t2.Pet_id
where pType = 'DOG'
group by t1.Owner_id
having count(1) >= 2)
order by Owner_id
not using join, nested subqueries is our only option
select * from Pet_Owner where Owner_id in (
select owner_id from Pet_Owner where Owner_id in
(select Owner_id from Pet where Pet_id in
(select Pet_id in Booking) and PType='DOG')
group by owner_id
having count(1) >= 2)
order by Owner_id
if you are trying to the # of dogs per owner:
select * from Pet_Owner where Owner_id in (
select Owner_id from Pet where Pet_id in
(select Pet_id in Booking) and PType='DOG'
group by owner_id
having count(1) >= 2)
) order by Owner_id

SQL to calculate author with most books

I have a table of books, a table of authors, and a "linker" table (many to many links between authors/books).
How do I find the authors with the highest number of books?
This is my schema:
books : rowid, name
authors : rowid, name
book_authors : rowid, book_id, author_id
This is what I came up with: (but it doesn't work)
SELECT count(*) IN book_authors
WHERE (SELECT count(*) IN book_authors
WHERE author_id = author_id)
And ideally I would like a report of the top 100 authors, something like:
author_name book_count
-----------------------------------
Johnny 25
Kelly 12
Ramboz 10
Do I need some kind of join? What is the fastest approach?
I'd join the three tables (via the book_authors table), group by the author, count occurrences and limit it to the top 100 rows:
SELECT a.name, COUNT(*)
FROM authors a
JOIN books_authors ba ON a.rowid = ba.author_id
JOIN books b ON ba.book_id = b.rowid
GROUP BY a.name
ORDER BY 2 DESC
LIMIT 100
EDIT:
Actually, we aren't using any data from books, just the fact the book actually exists, which can be inferred from books_authors, so this query can be improved by dropping the second join:
SELECT a.name, COUNT(*)
FROM authors a
JOIN books_authors ba ON a.rowid = ba.author_id
GROUP BY a.name
ORDER BY 2 DESC
LIMIT 100
Couldn't you just
select count(1) , Author_ID from Book_Authors group by Author_ID order by count(1) desc limit 100
The authors with the most books would be at the top (or the author_ID would be at least)
As for limiting to top 100... then add limit clause Sqlite LIMIT / OFFSET query
SELECT TOP 3 authors.author_name, authors.book_name, books.sold_copies,
(SELECT SUM(books.sold_copies) FROM books WHERE authors.book_name = books.book_name ) AS Total
FROM authors
INNER JOIN books
ON authors.book_name = books.book_name
ORDER BY sold_copies desc

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.

Multiple unique ways of executing a simple query (ORACLE)?

I have to come up with 5 different ways (unique execution plans) to process the following query.
Find the items that are delivered by all suppliers.
My database holds the following tables:
QSPL – it holds a list of supplier names
SPLNO (number)
SPLNAME (varchar)
QDEL– it holds delivery items, suppliers, and departments
DELNO (number)
DELQTY (number)
ITEMNAME (varchar)
DEPTNAME (varchar)
SPLNO (number)
QITEM – it holds list of items
ITEMNAME (varchar)
ITEMTYPE (varchar)
ITEMCOLOR (varchar)
I was able to successfully come up with the following four unique queries.
1.
select itemname --, etc.
from qitem
where itemname not in
(select itemname
from qitem, qspl
where (char(splno)+itemname) not in
(select char(splno)+itemname
from qdel));
2.
select itemname --,etc.
from qitem
where not exists
(select *
from qspl
where not exists
(select *
from qdel
where qdel.itemname = qitem.itemname
and Qdel.splno = qspl.splno));
3.
select a.itemname --, etc
from qitem a join qdel b on a.itemname = b.itemname
group by a.itemname
having count (distinct splno) = (select count(*) from qspl);
4.
select itemname
from qdel
group by itemname
having count (distinct splno) = (select count(*) from qspl);
I have no idea what to do for a 5th unique query.
Does anyone have a clue?
I tried to put this question in the best possible context with significant detail, feedback is greatly appreciated.
Thanks
Maybe some SQL 86 syntax:
select a.itemname --, etc
from qitem a, qdel b
where a.itemname = b.itemname
group by a.itemname
having count (distinct splno) = (select count(*) from qspl);
Or an outer join
select a.itemname --, etc
from qspl s, qdel b
WHERE s.splno (+)= b.splno
group by s.splno
having count (distinct b.splno) = (select count(*) from qspl);
This is another unique way (which I'm sure it's horribly inefficient):
select distinct splname
from (
select qi.itemname,
qs.splname,
count(distinct qi.itemname) over () as total_items,
count(distinct qd.itemname) over (partition by qd.splno) as items_per_supp
from qitem qi
left join qdel qd on qi.itemname = qd.itemname
left join qspl qs on qs.splno = qd.splno
) t
where total_items = items_per_supp
Or a variant of your #3 which will probably use a different execution plan:
with supplier_items as (
select splno, count(*) item_count
from qdel
group by splno
)
select splname
from qspl qs
join supplier_items si on qs.splno = si.splno
where si.item_count = (select count(*) from qitem);
Since this is homework, I will be obtuse: Check out the Oracle MINUS operator.

SQL Multiple Duplicate Row Detection

I'm trying to determine a correct way to isolate rows within a table that have the same values in 2 columns.
There are two tables, one (Name) with the person's names and IDs, and the other one (Nation) with people's IDs and their nations. I join the two tables with inner join, and now the new table columns consist of an ID, first name, last name, and nation. If I want to find pairs of people who have the same last name and are from the same nation, why isn't
select ID, FName, LName, Nation
from (Name inner join Nation on Name.ID = Nation.ID)
group by Name, Nation
having count(Name) > 1 and count(Nation) > 1
working?
I'm aiming for the result to be a table with columns:
ID -------First--------------- Last ---------Nation
where the last names and nations will be identical pairs while first names will be different.
I feel like the group by part isnt appropriate, but is there even an alternate way? Thanks for any help.
If you are using MS SQL Server:
select
*
from
(
select
Name.*,
Nation.Nation,
cnt = count(*) over(partition by LName, Nation)
from Name
join Nation on Nation.ID = Name.ID
) t
where cnt > 1
Try this:
SELECT * FROM (
SELECT Name.ID, Name.FName, Name.LName, Nation.Nation
FROM Name
INNER JOIN Nation ON (Name.ID = Nation.ID)
) a
INNER JOIN (
SELECT Name.ID, Name.FName, Name.LName, Nation.Nation
FROM Name
INNER JOIN Nation ON (Name.ID = Nation.ID)
) b ON (a.LName = b.LName AND a.Nation = b.Nation)
WHERE a.ID < b.ID
As Simon Righarts hinted, something's not right with the design.
Scenario 1)
If a name can have multiple nations, you would have 3 tables implementing an n:m relationship.
CREATE TABLE name (name_id int, name text, ...);
CREATE TABLE nation (nation_id int, nation text, ...);
CREATE TABLE nationality (name_id int references name(name_id)
,nation_id int references nation(nation_id)
... );
Query for the scenario:
SELECT a.name_id, a.fname, a.lname, n.nation
FROM name a
JOIN nationality na USING (name_id)
JOIN nation n USING (nation_id)
JOIN (
SELECT a.lname, na.nation_id
FROM name a
JOIN nationality na USING (name_id)
GROUP BY 1,2
HAVING count(*) > 1) x USING (lname, nation_id)
Scenario 2)
If a name can only have one nation, there would be a column nation_id in the table name:
CREATE TABLE name (name_id int
,name text
,nation_id int references nation(nation_id), ...);
CREATE TABLE nation (nation_id int, nation text, ...);
Query for this scenario:
SELECT a.name_id, a.fname, a.lname, n.nation
FROM name a
JOIN nation n USING (nation_id)
JOIN (
SELECT a.lname, a.nation_id
FROM name a
GROUP BY 1,2
HAVING count(*) > 1) x USING (lname, nation_id);
All multiple occurrences are included here, not just "pairs" - assuming you meant that.
Your actual description doesn't fit either scenario.