MySQL Group BY, Having, Order By Query - mysql

Ok, so assume I have the following tables:
recipes
id (pk)
name
added
modified
recipe_versions
id (pk)
recipe_id (fk to recipes.id)
version
content
added
What I want is a query that grabs the latest recipe_versions.added data and then joins with the base recipe data. Then sorts all results by recipes.added ASC I have the following, but the group by, is not selecting the latest recipe_versions row, seems to be selecting the first.
SELECT r.`id`,
r.name,
rv.version,
rv.content,
r.added,
r.modified,
FROM recipes r,
recipe_versions rv
WHERE r.`id` = rv.recipe
GROUP BY rv.recipe
HAVING max(rv.added)
ORDER BY r.added ASC

Use this solution:
SELECT
c.*, b.*
FROM
(
SELECT recipe_id, MAX(added) AS mostrecent
FROM recipe_versions
GROUP BY recipe_id
) a
INNER JOIN
recipe_versions b ON
a.recipe_id = b.recipe_id AND
a.mostrecent = b.added
INNER JOIN
recipes c ON a.recipe_id = c.id
ORDER BY
c.added

Related

Print every max value using three tables (mysql)

I need to find the author who has written the most volumes in my database, I have three tables "VOLUMES", "AUTHOR", "WRITTEN BY". Table VOLUMES has columns like title, volume_id(primary key), year, edition_year, etc. Table AUTHOR has columns for generic infos like name, surname, id,etc and table WRITTEN BY is used to connect AUTHOR and VOLUMES, it contains the columns volume_id and author_id.
My data can contain many copies of the same volumes so I guess I need to group every copy of the same volume.
The query I have written is:
SELECT A.NAME, A.SURNAME, COUNT(V.ID) no_of_volumes
FROM VOLUMES AS V, AUTHOR AS A JOIN WRITTEN_BY W
WHERE (V.ID = W.VOLUME_ID AND A.ID = W.A_ID)
GROUP BY A.NAME, A.SURNAME
ORDER BY no_of_volumes DESC
LIMIT 1;
Now, this should print just one author, but I want it to print EVERY author that has written the same number of volumes... How do I do that?
If you are allowed to change the scheme slightly (use unique ID-names) than this could do the trick
select AUTHOR.NAME
, count(*) VolumeCount
from AUTHOR
inner join WRITTEN_BY using(AUTHOR_ID)
inner join VOLUME using(VOLUME_ID)
group by a.AUTHOR_ID
order by VolumeCount desc
limit 1;
x
If not than this should do the trick:
select AUTHOR.NAME
, count(*) VolumeCount
from AUTHOR
inner join WRITTEN_BY on AUTHOR_ID = AUTHOR.ID
inner join VOLUME on VOLUME_ID = VOLUME.ID
group by a.AUTHOR_ID
order by VolumeCount desc
limit 1;
Please reserve where for filtering only. Using it for foreign-key makes messy code.
(And if your table and field names are uppercase it makes sense to use lowercase keywords)
You must aggregate twice inside written_by: once to get the max number of volumes written and then to get the author ids who wrote the max number of volumes.
Then join to authors to get the authors details:
SELECT a.*, t.no_of_volumes
FROM author a INNER JOIN (
SELECT author_id, COUNT(*) no_of_volumes
FROM written_by
GROUP BY author_id
HAVING COUNT(*) = (
SELECT COUNT(*) no_of_volumes
FROM written_by
GROUP BY author_id
ORDER BY no_of_volumes DESC LIMIT 1
)
) t ON t.author_id = a.id
The table volumes is not needed.
For MySql 8.0+ the code can be simplified with the use of the window function RANK():
SELECT a.*, t.no_of_volumes
FROM author a INNER JOIN (
SELECT r.*
FROM (
SELECT author_id, COUNT(*) no_of_volumes,
RANK() OVER (ORDER BY COUNT(*) DESC) rnk
FROM written_by
GROUP BY author_id
) r
WHERE r.rnk = 1
) t ON t.author_id = a.id

Highest total salary

I have 3 tables with the following values and the question is : Find the name of the instructor with the highest total salary along with the names of the dancers he trains.
I was thinking something like :
Select Instructor.name, Dancer.name
FROM Instructor, Dancer, Info
WHERE Instructor.i_ID=Info.ints_id
AND Dancer.d_ID=Info.danc_id
GROUP BY Info.inst_id, Info.danc_id
ORDER BY SUM(Info.salary)
DESC LIMIT 2;
But the output is not right and also I am not suppose to know how many dancers every instructor has, so DESC LIMIT 2 is definitely wrong.
Join properly the tables and group by instructor to get the total salary for each.
Then sort descending by the total salary and keep only the 1st row.
With group_concat() you get the names of the dancers as a comma separated list:
select i.name,
sum(n.salary) total_salary,
group_concat(d.name) dancers
from instructor i
inner join info n on n.inst_id = i.i_id
inner join dancer d on d.danc_id = d.d_id
group by i.i_id, i.name
order by total_salary desc limit 1
Note that this query will return only 1 row, so if there are 2 instructors with the same top total salary you will get only 1.
If you want in the results ties in the top salary then use a HAVING clause:
select i.name,
sum(n.salary) total_salary,
group_concat(d.name) dancers
from instructor i
inner join info n on n.inst_id = i.i_id
inner join dancer d on d.danc_id = d.d_id
group by i.i_id, i.name
having total_salary = (select sum(salary) from info group by inst_id order by sum(salary) desc limit 1)
Try This:
select tab3.name "Instructor",tab2.name "Dancer"
from info tab1
inner join
dancer tab2 on tab1.dance_id=tab2.d_id
inner join
(select t1.inst_id,t2.name, sum(t1.salary) as "total_salary" from info t1
inner join instructor t2
on t1.inst_id=t2.i_id
group by t1.inst_id,t2.name
order by total_salary desc limit 1) tab3
on tab3.inst_id=tab1.inst_id
DB-FIDDLE
well it's a bit odd relation table but you could try something like this
SELECT
ins.name,
SUBSTRING_INDEX(GROUP_CONCAT(inf.salary ORDER BY inf.salary DESC),',',1) salary,
SUBSTRING_INDEX(GROUP_CONCAT(d.name ORDER BY inf.salary DESC),',',1) dancer_name
FROM Instructor ins
JOIN Info inf ON (ins.i_ID=inf.inst_id)
JOIN Dancer d on (d.d_ID=danc_id)
GROUP BY ins.i_ID
LIMIT 1;

Return the company with most film in a genre

I am working on this project at my university, where I need to create a query to the database. I want the query to return the company with most movies in the given genre. At the moment I have this query, but this only return one company, but there can probably be more than one.
SELECT CompanyID, CategoryID, COUNT(*) as NumberOfMovies
FROM Movie
NATURAL JOIN CategoryFilm
NATURAL JOIN Category
NATUAL JOIN Comapny
GROUP BY CategoryID, CompanyID
Order by NumberOfMovies DESC LIMIT 1
I beleave I will need a "having" in here.
pls try this, it may because you added limit 1, which only show 1st retrieved record
SELECT CompanyID, CategoryID, COUNT(*) as NumberOfMovies
FROM Movie
NATURAL JOIN CategoryFilm
NATURAL JOIN Category
NATURAL JOIN Comapny
GROUP BY CategoryID, CompanyID
Order by NumberOfMovies DESC
I assume by "category" you mean "genre" -- or that they are the same thing.
Do not use NATURAL JOIN. It does not even use properly declared foreign key relationships, instead relying merely on name similarity between tables. It is dangerous because the columns used are not specified and can introduce hard-to-debug errors. I often refer to it as an "abomination" because it does not take table declarations into account.
If you have a given category, then I would expect a WHERE clause:
SELECT CompanyID, COUNT(*) as NumberOfMovies
FROM Movie m JOIN
CategoryFilm cf
ON cf.movie_id = m.movie_id JOIN
Company c
ON c.company_id = m.company_id
WHERE cf.category_id = ?
GROUP BY CategoryID
ORDER BY NumberOfMovies DESC
LIMIT 1;
If you want to allow ties, you can use window function rank():
select *
from (
select
co.companyID,
ca.categoryID,
count(*) NumberOfMovies,
rank() over(partition by c.categoryID order by count(*) desc) rn
from movie m
inner join categoryFilm cf on cf.movieID = m.movieID
inner join category ca on ca.categoryID = cf.categoryID
inner join company co on co.companyID = m.companyID
group by co.companyID, ca.categoryID
) t
where rn = 1
order by ca.categoryID
This gives you the top company for each and every category, ties included. If you want to filter on a given category, you can just add a where clause to the inner query.
Side note: do not use natural joins: they are error-prone. I rewrote the query to use inner joins instead (I made a few assumptions on the relations).

Find Count of racers from Race Table

I am new to SQL,I have two tables RACER and SPONSOR,
RACER TABLE has these columns
RACER_NAME,
SPONSOR_ID
RACER_ID- Primary KEY
SPONSOR table has these columns
SPONSOR_ID,
SPONSOR_NAME
now I want to find the SPONSOR name and no.of racer associated with that SPONSOR.
Here is what I tried:
select s.sponsor_name , (select count(*) from racer r) where INNER JOIN s.sponsor_id = r.sponsor_id
You need to understand, how JOIN works and its syntax.
select s.sponsor_name, count(*) as total_racer
from
racer r inner join sponsor s
on r.sponsor_id=s.sponsor_id
group by r.sponsor_id
You need to specify both tables in FROM clause, which you were missing.
You could use a join ( left join if not al the sponsor have racer ) and get the result without subselect using group by and count
select s.sponsor_name , count(*)
from SPONSOR s r
left JOIN racer r s.sponsor_id = r.sponsor_id
GROUP BY s.sponsor_name
Your version, with a subquery is reasonable, particularly because in MySQL it can have better performance than the corresponding GROUP BY query. However, it needs to be a correlated subquery. That looks like:
select s.*,
(select count(*)
from racer r
where s.sponsor_id = r.sponsor_id
) as cnt
from sponsor s;
In other words, choose either the JOIN method or the subquery method, but not both for the same value.

mysql advanced group query

I need some help figuring out a query
I have 3 tables
sources
id, name, rank
origin
id, source_id (FK to sources id), name
One source can have many origins
product
id, origin_id (FK to origin id), name, time_added
One origin can have many products
Now, what I want is to select the most recent products per source, ordered by rank descending
Any suggestions?
This should do as you have requested, though without sample output it's hard to be 100% certain. Inner query selects products linked to the source id ordered by the date added from newest to oldest, and in turn that's joined to sources and grouped.
SELECT
*
FROM sources AS s
INNER JOIN (
SELECT
origins.source_id,
product.*
FROM origin
INNER JOIN product
ON product.origin_id = origin.origin_id
ORDER BY time_added DESC
) AS productsOrdered
ON productsOrdered.source_id = sources.source_id
ORDER BY s.rank DESC, productsOrdered.time_added DESC
This avoids having to do potentially expensive opreations as the inner select should be pretty fast and can be limited as required
A typical way of doing this is to
Find the MAX(time_added) for each origin
Get the product's id for each of these origins
Join with the sources and origin tables to retrieve all columns
Note that this fails if there are origins with multiple records with the exact same time_added.
SQL Statement
SELECT *
FROM sources s
INNER JOIN origin o ON o.source_id = s.id
INNER JOIN product p ON p.origin_id = o.id
INNER JOIN (
SELECT id
FROM product p
INNER JOIN (
SELECT origin_id
, MAX(time_added) AS time_addded
FROM product p
GROUP BY
origin_id
) pmax ON pmax.origin_id = p.origin_id
AND pmax.time_added = p.time_added
) pmax ON pmax.id = p.id
SELECT o.id,count(o.id) as numOfProdFromOrig p.id, p.name, p.time_added, s.rank
FROM product as p NATURAL JOIN sources as s NATURAL JOIN origin as o
GROUP BY (numOfProdFromOrig)
ORDER BY s.rank DESC
select b.id,(select p.name from origin o inner join product p
on p.origin_id = o.id where o.source_id = b.id order by time_added desc limit 1)a as product_name
from source b ;
Try this: