Retrieve the max values from different rows - mysql

Sorry, the title might be a bit hard to understand. I'm studying MySQL at school but right now I'm stuck in an exercise and I'm not able to find the solution anywhere around (although I'm 100% sure it's out there, I just can't find it).
Anyway, I have a table with some medical departments that go by the name of Psychiatry, Surgeon and Dermatologist. Both the surgeon and psychiatry areas have 2 medics in them, and dermatologist has just one. I'm supposed to get the name of the area with the most medics, simple as that. The problem I have is that I can't get BOTH areas to appear, just one of them or all of them. This is my code:
select speciality, count(*)
from medics
group by speciality
order by count(*);
I've also tried with:
select max(total), speciality
from
(select speciality, count(*) as 'total' from medics
group by speciality
order by count(*))
as test
group by speciality;
EDIT: This is the table where I must take the data from:
cod_hospital dni apellidos epecialidad
4 22233311 Martínez Molina, Gloria PSIQUIATRA
2 22233322 Tristán García, Ana CIRUJANO
2 22233333 Martínez Molina, AndrEs CIRUJANO
4 33222111 Mesa del Castillo, Juan DERMATOLOGO
1 66655544 Castillo Montes, Pedro PSIQUIATRA
And this is the expected result. "1 - DERMATOLOGO" result shouldn't appear there since that's not the speciality with the most medics:
max(total) especialidad
2 CIRUJANO
2 PSIQUIATRA

E.g.:
SELECT a.*
FROM
(SELECT especialidad,COUNT(*) total FROM medics GROUP BY especialidad) a
JOIN
(SELECT COUNT(*) total FROM medics GROUP BY especialidad ORDER BY total DESC LIMIT 1) b
ON b.total = a.total;
Yes. It really can be that crude sometimes.

Related

in MYSQL return Student name with highest score overall

i.e. a table called students (Assume there is only one student with that name, e.g. there isnt two students called John Smith) with
studentName, studentScore, subject
assume table has this
John Smith 40 maths
bob grey 20 english
anne hank 23 english
John Smith 30 english
anne grey 10 maths
I am trying to find the most highest scored student by calculating the maximum of averages of all students
this here will select the highest average but not the student name with that average:
SELECT MAX(avgStudentScore)
FROM (SELECT AVG(studentScore) AS avgStudentScore FROM students GROUP BY studentName) t
thx
If all you need is the name, you can do something like this:
select studentName, avg(studentScore) as avgStudentScore
from students
group by studentName
order by avgStudentScore desc
limit 1
This will return only the first row of the query. Since it's ordered by avgStudentScore, it will return the student with the top average.
The above solution is the simplest and fastests, but it's not the only one. If you want to do it "the hard way" (with subqueries), what you need to do is:
Calculate the average for each student
Get the highest average
Filter the student with the highest average
So... let's do it the hard way ;)
select a.studentName
from
(
select studentName, avg(studentScore) as avgStudentScore
from students
group by studentName
) as a
where
a.avgStudentScore = (
select max(avgStudentScore)
from (
select avg(studentScore) as avgStudentScore
from students
group by studentName
) as a
)
Notice that with this approach the final result might not be unique (i.e. there may be one ore more students with the same average and that average is the highest).
Use this query:
SELECT studentName, max( mark ) as maxMark FROM `student` GROUP BY studentName

How can I write a query that aggregate a single row with latest date among multiple set of rows?

I have a MySQL table where there are many rows for each person, and I want to write a query which aggregates rows with special constraint. (one per person)
For example, lets say the table is consist of following data.
name date reason
---------------------------------------
John 2013-04-01 14:00:00 Vacation
John 2013-03-31 18:00:00 Sick
Ted 2012-05-06 20:00:00 Sick
Ted 2012-02-20 01:00:00 Vacation
John 2011-12-21 00:00:00 Sick
Bob 2011-04-02 20:00:00 Sick
I want to see the distribution of 'reason' column. If I just write a query like below
select reason, count(*) as count from table group by reason
then I will be able to see number of reasons for this table overall.
reason count
------------------
Sick 4
Vacation 2
However, I am only interested in single reason from each person. The reason that should be counted should be from a row with latest date from the person's records. For example, John's latest reason would be Vacation while Ted's latest reason would be Sick. And Bob's latest reason (and the only reason) is Sick.
The expected result for that query should be like below. (Sum of count will be 3 because there are only 3 people)
reason count
-----------------
Sick 2
Vacation 1
Is it possible to write a query such that single latest reason will be counted when I want to see distribution(count) of reasons?
Here are some facts about the table.
The table has tens of millions of rows
For most of times, each person has one reason.
Some people have multiple reasons, but 99.99% of people have fewer than 5 reasons.
There are about 30 different reasons while there are millions of distinct names.
The table is partitioned based on date range.
SELECT T.REASON, COUNT(*)
FROM
(
SELECT PERSON, MAX(DATE) AS MAX_DATE
FROM TABLE-NAME
GROUP BY PERSON
) A, TABLE-NAME T
WHERE T.PERSON = A.PERSON AND T.DATE = A.MAX_DATE
GROUP BY T.REASON
Try this
select reason, count(*) from
(select reason from table where date in
(select max(date) from table group by name)) t
group by reason
In MySQL, it's not very efficient to do this kind of query since you don't have access to tools like partitionning query in SQL Server or Oracle.
You can still emulate it by doing a subquery and retrieve the rows based on the condition you need, here the maximum date :
SELECT t.reason, COUNT(1)
FROM
(
SELECT name, MAX(adate) AS maxDate
FROM #aTable
GROUP BY name
) maxDateRows
INNER JOIN #aTable t ON maxDateRows.name = t.name
AND maxDateRows.maxDate = t.adate
GROUP BY t.reason
You can see a sample here.
Test this query on your samples, but I'm afraid that it will be slow as hell.
For your information, you can do the same thing in a more elegant and much much faster way in SQL Server :
SELECT reason, COUNT(1)
FROM
(
SELECT name
, reason
, RANK() OVER(PARTITION BY name ORDER BY adate DESC) as Rank
FROM #aTable
) AS rankTable
WHERE Rank = 1
GROUP BY reason
The sample is here
If you are really stuck to MySql, and the first query is too slow, then you can split the problem.
Do a first query creating a table:
CREATE TABLE maxDateRows AS
SELECT name, MAX(adate) AS maxDate
FROM #aTable
GROUP BY name
Then create index on both name and maxDate.
Finally, get the results :
SELECT t.reason, COUNT(1)
FROM maxDateRows m
INNER JOIN #aTable t ON m.name = t.name
AND m.maxDate = t.adate
GROUP BY t.reason
The solution you are looking for seems to be solved by this query :
select
reason,
count(*)
from (select * from tablename group by name) abc
group by
reason
It is quite fast and simple. You can view the SQL Fiddle
Apologies if this answer duplicates an existing. Maybe I'm suffering from some form aphasia but I cannot see it...
SELECT x.reason
, COUNT(*)
FROM absentism x
JOIN
( SELECT name,MAX(date) max_date FROM absentism GROUP BY name) y
ON y.name = x.name
AND y.max_date = x.date
GROUP
BY reason;

Queries and subqueries in MySQL, show customers with products purchased

i have one problem this is my simple diagram:
Well I tried a lot and I can not do what I need, I need to display a list of customers who have made purchases, with most purchased products in every purchase made, for example
Customer number 1 ---> BUY:
4 chicken soups
3 meat soups
2 shrimp soup
Customer number 2 --->BUY:
1 chicken soups
1 meat soups
2 shrimp soup
Customer number 3 --->BUY:
4 chicken soups
8 meat soups
1 shrimp soup
The result of my query must be:
Please help me make this mysql query correctly
I can think of two approaches to this. The first is to use a subselect to query the most popular item for each customer:
SELECT nameCustomer AS customer_name, (
SELECT idItem
FROM detailSale
INNER JOIN sales on sales.idDetail = detailSale.idDetail
WHERE sales.idCustomer = cliente.idCustomer
GROUP BY idItem
ORDER BY SUM(amountBuyItem) DESC
LIMIT 1
)
FROM cliente
Note that this query is incomplete as it only shows you the id of the item, so you would need to join the itemForSale table to retrieve its name.
The other approach is to start with a query that will show you all of the items for each customer in order of popularity:
SELECT idCustomer, idItem, SUM(amountBuyItem) AS totalBuyItem
FROM detailSale
INNER JOIN sales on sales.idDetail = detailSale.idDetail
GROUP BY idCustomer, idItem
ORDER BY idCustomer, idItem DESC
You could then create an aggregate of this query to find the count of the most popular item, and then join back to it to find out which item that was. This has the disadvantage of potentially returning more than one row for each customer.

Select distinct column along with some other columns in MySQL

I can't seem to find a suitable solution for the following (probably an age old) problem so hoping someone can shed some light. I need to return 1 distinct column along with other non distinct columns in mySQL.
I have the following table in mySQL:
id name destination rating country
----------------------------------------------------
1 James Barbados 5 WI
2 Andrew Antigua 6 WI
3 James Barbados 3 WI
4 Declan Trinidad 2 WI
5 Steve Barbados 4 WI
6 Declan Trinidad 3 WI
I would like SQL statement to return the DISTINCT name along with the destination, rating based on country.
id name destination rating country
----------------------------------------------------
1 James Barbados 5 WI
2 Andrew Antigua 6 WI
4 Declan Trinidad 2 WI
5 Steve Barbados 4 WI
As you can see, James and Declan have different ratings, but the same name, so they are returned only once.
The following query returns all rows because the ratings are different. Is there anyway I can return the above result set?
SELECT (distinct name), destination, rating
FROM table
WHERE country = 'WI'
ORDER BY id
Using a subquery, you can get the highest id for each name, then select the rest of the rows based on that:
SELECT * FROM table
WHERE id IN (
SELECT MAX(id) FROM table GROUP BY name
)
If you'd prefer, use MIN(id) to get the first record for each name instead of the last.
It can also be done with an INNER JOIN against the subquery. For this purpose the performance should be similar, and sometimes you need to join on two columns from the subquery.
SELECT
table.*
FROM
table
INNER JOIN (
SELECT MAX(id) AS id FROM table GROUP BY name
) maxid ON table.id = maxid.id
The problem is that distinct works across the entire return set and not just the first field. Otherwise MySQL wouldn't know what record to return. So, you want to have some sort of group function on rating, whether MAX, MIN, GROUP_CONCAT, AVG, or several other functions.
Michael has already posted a good answer, so I'm not going to re-write the query.
I agree with #rcdmk . Using a DEPENDENT subquery can kill performance, GROUP BY seems more suitable provided that you have already INDEXed the country field and only a few rows will reach the server. Rewriting the query giben by #rcdmk , I added the ORDER BY NULL clause to suppress the implicit ordering by GROUP BY, to make it a little faster:
SELECT MIN(id) as id, name, destination as rating, country
FROM table WHERE country = 'WI'
GROUP BY name, destination ORDER BY NULL
You can do a GROUP BY clause:
SELECT MIN(id) AS id, name, destination, AVG(rating) AS rating, country
FROM TABLE_NAME
GROUP BY name, destination, country
This query would perform better in large datasets than the subquery alternatives and it can be easier to read as well.

Show biggest margin between 1st and 2nd within group

Name Day Points
Brian 1 6
Tom 1 11
Freddy 1 7
Kim 2 10
Sandra 2 1
Brian 2 3
I need to know who has won with the biggest margin to number two - but only between people on the same day.
Thus if done properly it would tell me Kim has won by the biggest margin.
I don't quite know how to handle on this one.
select
first_place.name,
max_points-max(points) as max_margin
from the_table
inner join
(select name, day, max(points) as max_points
from the_table group by day) as first_place
on the_table.day=first_place.day
where the_table.points<max_points
group by the_table.day
order by max_margin desc limit 1 ;
This would need to be done with two sub queries... Inner most to get the highest score for a single day, then, find the next highest scrore under the first place position, then find the margin... However, due to your sample data of just names, no consideration for unique names which would otherwise be by some internal ID... Say "Brian" in your sample data... is it the same Brian on both days, or is it a different person. Additionally, what if two people are tied for first place with 11 points, then my query would show BOTH people in first place before the margin to the now "3rd" place person as the detected margin. You will probably have to modify some to accommodate such conditions described..
SELECT
FS.Day,
FS.FirstPlace,
FS.SecondPlace,
FS.FirstPlace - FS.SecondPlace as Margin,
G.Name
FROM
( SELECT G2.Day,
FirstPlace.FirstPlacePoints FirstPlace,
MAX( G2.Points ) as SecondPlace
FROM
Games G2,
( SELECT Day,
MAX( Points ) as FirstPlacePoints
FROM
Games
GROUP BY
Day ) FirstPlace
WHERE
G2.Day = FirstPlace.Day
AND G2.Points < FirstPlace.FirstPlacePoints
GROUP BY
1, 2 ) as FS,
Games G
WHERE
FS.Day = G.Day
and FS.FirstPlace = G.Points
ORDER BY
Margin desc
LIMIT 1