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;
Related
I try to create a query who select the contacts information (table invoice_contacts), and the adresses (table invoice_adresses) associate to the contact which is the most used in the (table invoice_compta)
For exemple I have two contact :
Mike
John
Mike have 2 adresses :
Paris
London
Mike have 1 invoice with Paris, and 5 invoice with London, so I want the adresse of London associate to Mike.
I have try this query with an subquery which count all adresses associate to the contact for an adresses (with NB_ADRESSES), and select only the biggest (with order by NB_ADRESSES desc and limit 1), it's seem wells but I have an error when I do where ia2.ID_CONTACT = ic.ID_CONTACT ic.ID_CONTACT is not found.. (and I need to associate the contact to the subquery).
select ic.*,
ia.*
from invoice_contacts ic
left join invoice_adresses ia on ia.ID_CONTACT = ic.ID_CONTACT
and ia.ID_ADRESSE in (
select ia3.ID_ADRESSE
from (
select ia2.ID_ADRESSE,
count(*) as NB_ADRESSES
from invoice_adresses ia2
left join invoice_comptas ico on ico.ID_ADRESSE_CONTACT = ia2.ID_ADRESSE
where ia2.ID_CONTACT = ic.ID_CONTACT
group by ia2.ID_ADRESSE
order by NB_ADRESSES desc
limit 1
) as ia3
)
group by ic.ID_CONTACT
order by CONTACT_TITRE asc
I also have try with "exist" or "inner join" instead of "in" but I doesn't find good results, so the best way seems it to be with this query for me, but I don't found the solution.
I hope you will help me :)
Thanks
UPDATE :
So finally I have found an solution with this query :
select ic.*,
ia.*
from invoice_contacts ic
left join invoice_adresses ia on ia.ID_CONTACT = ic.ID_CONTACT
and ia.ID_ADRESSE = (
select ia3.ID_ADRESSE
from (
select ia2.*,
count(*) as NB_ADRESSES
from invoice_adresses ia2
left join invoice_comptas ico on ico.ID_ADRESSE_CONTACT = ia2.ID_ADRESSE
group by ia2.ID_ADRESSE
) as ia3
where ia3.ID_CONTACT = ic.ID_CONTACT
order by NB_ADRESSES desc
limit 1
)
group by ic.ID_CONTACT
order by CONTACT_TITRE asc
Thanks
Let me rephrase the problem as finding the most common contact/address combination for a given invoice.
I find it hard to follow your query and your table naming. But this is the idea:
select contact, address
from (select contact, address, count(*) as cnt,
row_number() over (partition by contact order by count(*) desc) as seqnum
from invoices
group by contact, address
) ca
where seqnum = 1;
The subquery is counting the number of times a given address (or city if you prefer) occur for each contact. The row_number() enumerates these, so the most common one has a value of "1". The outer query then chooses the most common value.
I have to say who is the scientist who have been the most in mission. I tried this code but it wasn't successful:
select name
from scientist, mission
where mission.nums = chercheur.nums
having count(*) = (select max(count(numis)) from mission, scientist where
mission.nums = chercheur.nums
group by name)
I have done several modifications for this request but I only obtain errors (ora-0095 and ora-0096 if I remember correctly).
Also, I create my tables with:
CREATE TABLE Scientist
(NUMS NUMBER(8),
NAME VARCHAR2 (15),
CONSTRAINT CP_CHER PRIMARY KEY (NUMS));
CREATE TABLE MISSION
(NUMIS NUMBER(8),
Country VARCHAR2 (15),
NUMS NUMBER(8),
CONSTRAINT CP_MIS PRIMARY KEY (NUMIS),
CONSTRAINT CE_MIS FOREIGN KEY (NUMS) REFERENCES SCIENTIST (NUMC));
You could count the missions each scientist participated in, and wrap that query in a query with a window function that will rank them according to their participation:
SELECT name
FROM (SELECT name, RANK() OVER (PARTITION BY name ORDER BY cnt DESC) AS rk
FROM (SELECT name, COUNT(*) AS cnt
FROM scientist s
JOIN mission m ON s.nums = m.nums
GROUP BY name) t
) q
WHERE rk = 1
Step 0 : Format your code :-) It would make it much easier to visualize
Step 1 : Get the count of Numis by Nums in the Mission table. This will tell you how many missions were done by each Nums
This is done in the cte block cnt_by_nums
Next to get the name of the scientist by joining cnt_by_nums with scientist table.
After that you want to get only those scientists who have the cnt_by_missions as the max available value from cnt_by_num
with cnt_by_nums
as (select Nums,count(Numis) as cnt_missions
from mission
group by Nums
)
select a.Nums,max(b.Name) as name
from cnt_by_nums a
join scientist b
on a.Nums=b.Nums
group by a.Nums
having count(a.cnt_missions)=(select max(a1.cnt_missions) from cnt_by_nums a1)
I'd write a query like this:
SELECT NAME, COUNTER
FROM
(SELECT NAME, COUNT(*) AS COUNTER
FROM SCIENTIST S
LEFT JOIN MISSION M
ON S.NUMS=M.NUMS
GROUP BY NAME) NUM
INNER JOIN
(SELECT MAX(COUNTER) AS MAX_COUNTER FROM
(SELECT NAME, COUNT(*) AS COUNTER
FROM SCIENTIST S
LEFT JOIN MISSION M
ON S.NUMS=M.NUMS
GROUP BY NAME) C) MAX
ON NUM.COUNTER=MAX.MAX_COUNTER;
(it works on MYSQL, I hope it's the same in Oracle)
As you don't select the name of your scientist (only count their missions) you don't need to join those tables within the subquery. Grouping over the foreign key would be sufficient:
select count(numis) from mission group by nums
You column names are a bit weird but that's your choice ;-)
Selecting only the scientist with the most mission references could be achieved in two ways. One way would be your approach where you may get multiple scientists if they have the same max missions.
The first problem you have in your query is that you are checking an aggregation (HAVING COUNT(*) = ) without grouping. You are only grouping your subselect.
Second, you could not aggregate an aggregation (MAX(COUNT)) but you may select only the first row of that subselect ordered by it's size or select the max of it by subquerying the subquery.
Approach with only one line:
select s.name from scientist s, mission m
where m.nums = s.nums
group by name
having count(*) =
(select count(numis) from mission
group by nums
order by 1 desc
fetch first 1 row only)
Approach with double subquery:
select s.name from scientist s, mission m
where m.nums = s.nums
group by name having count(*) =
(select max(numis) from
(select count(numis) numis from mission group by nums)
)
Second approach would be doing the FETCH FIRST on yur final result but this would give you exactly 1 scientist even if there are multiple with the same max missions:
select s.name from scientist s, mission m
where m.nums = s.nums
group by name
order by count(*) desc
fetch first 1 row only
Doing a cartisian product is not state of the art but the optimizer would make it a "good join" with the given reference in the where clause.
Made these with IBM Db2 but should also work on Oracle.
If you want one row, then in Oracle 12+, you can do:
SELECT name, COUNT(*) AS cnt
FROM scientist s JOIN
mission m
ON s.nums = m.nums
GROUP BY name
ORDER BY COUNT(*) DESC
FETCH FIRST 1 ROW ONLY;
In earlier versions, you would generally use a subquery:
SELECT s.*
FROM (SELECT name, COUNT(*) AS cnt
FROM scientist s JOIN
mission m
ON s.nums = m.nums
GROUP BY name
ORDER BY COUNT(*) DESC
) sm
WHERE rownum = 1;
If you want ties, then generally window functions would be a simple solution:
SELECT s.*
FROM (SELECT name, COUNT(*) AS cnt,
RANK() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM scientist s JOIN
mission m
ON s.nums = m.nums
GROUP BY name
ORDER BY COUNT(*) DESC
) sm
WHERE seqnum = 1;
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
I have 3 tables:
Person (with a column PersonKey)
Telephone (with columns Tel_NumberKey, Tel_Number, Tel_NumberType e.g. 1=home, 2=mobile)
xref_Person+Telephone (columns PersonKey, Tel_NumberKey, CreatedDate, ModifiedDate)
I'm looking to get the most recent (e.g. the highest Tel_NumberKey) from the xref_Person+Telephone for each Person and use that Tel_NumberKey to get the actual Tel_Number from the Telephone table.
The problem I am having is that I keep getting duplicates for the same Tel_NumberKey. I also need to be sure I get both the home and mobile from the Telephone table, which I've been looking to do via 2 individual joins for each Tel_NumberType - again getting duplicates.
Been trying the following but to no avail:
-- For HOME
SELECT
p.PersonKey, pn.Phone_Number, pn.Tel_NumberKey
FROM
Persons AS p
INNER JOIN
xref_Person+Telephone AS x ON p.PersonKey = x.PersonKey
INNER JOIN
Telephone AS pn ON x.Tel_NumberKey = pn.Tel_NumberKey
WHERE
pn.Tel_NumberType = 1 -- e.g. Home phone number
AND pn.Tel_NumberKey = (SELECT MAX(pn1.Tel_NumberKey) AS Tel_NumberKey
FROM Person AS p1
INNER JOIN xref_Person+Telephone AS x1 ON p1.PersonKey = x1.PersonKey
INNER JOIN Telephone AS pn1 ON x1.Tel_NumberKey = pn1.Tel_NumberKey
WHERE pn1.Tel_NumberType = 1
AND p1.PersonKey = p.PersonKey
AND pn1.Tel_Number = pn.Tel_Number)
ORDER BY
p.PersonKey
And have been looking over the following links but again keep getting duplicates.
SQL select max(date) and corresponding value
How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?
SQL Server: SELECT only the rows with MAX(DATE)
Am sure this must be possible but been at this a couple of days and can't believe its that difficult to get the most recent / highest value when referencing 3 tables. Any help greatly appreciated.
select *
from
( SELECT p.PersonKey, pn.Phone_Number, pn.Tel_NumberKey
, row_number() over (partition by p.PersonKey, pn.Phone_Number order by pn.Tel_NumberKey desc) rn
FROM
Persons AS p
INNER JOIN
xref_Person+Telephone AS x ON p.PersonKey = x.PersonKey
INNER JOIN
Telephone AS pn ON x.Tel_NumberKey = pn.Tel_NumberKey
WHERE
pn.Tel_NumberType = 1
) tt
where tt.rn = 1
ORDER BY
tt.PersonKey
you have to use max() function and then you have to order by rownum in descending order like.
select f.empno
from(select max(empno) empno from emp e
group by rownum)f
order by rownum desc
It will give you all employees having highest employee number to lowest employee number. Now implement it with your case then let me know.
I have these relations
Entry
-----
id
creationdate
grade
Subject
------
id
name
and join table
Entry_Subjects
------------
entry_id
subject_id
I need to create the sql to find average grade of entries belonging to a particular subject ( say 'java') on a particular creationdate
I tried the following
assume id for Subject 'java' is 2
SELECT creationdate,
avg(grade)
FROM (SELECT *
FROM Entry
WHERE id IN
(SELECT id
FROM Entry_Subjects
WHERE subject_id =2
)
)
GROUP BY creationdate;
I get the error
subquery in FROM must have an alias
I tried to correct this but couldn't
can somebody tell me why this error occurs.. my db knowledge is not that good
Probably want JOINs instead of nested SELECTs.
SELECT
creationdate,
AVG(grade)
FROM Entry e
INNER JOIN Entry_Subjects f
ON e.id = f.entry_id
INNER JOIN Subject s
ON f.subject_id = s.id
WHERE s.name = 'java' --this is where you replace 'java' with a variable to search by name
GROUP BY creationdate
Can also be done using analytical functions
select a.creationdate,avg(a.grade) over (partition by a.creationdate order by a.creationdate) as avg_grade
from entry a,subject b,entry_subjects c
where a.id=c.entry_id and b.id=c.subject_id
and upper(b.name)='JAVA';