How to write this SQL statement? - mysql

Here is the table:
User
Name: Subject:
Peter Math
Mary Chinese
Mary Computer
Mary Hist
Mary PE
Mary English
Peter Art
Chris English
Chris Computer
Peter Computer
Paul Math
I would like to get the the top appear in name, and return top 4 result should be subject name. For example, in this case top appear name is Mary, and base on the order in subject, the Chinese , Computer, English, so I would like to have the result:
Mary Chinese
Mary Computer
Mary English
Mary Hist
If Mary is not the most enough to show the result, the second people will be the follow, like, let say the table will like this:
Name: Subject:
Peter Math
Mary Chinese
Mary Computer
Mary Hist
Peter Art
Chris English
Chris Computer
Peter Computer
Paul Math
The result will be,
Mary Chinese
Mary Computer
Mary Hist
Peter Art
Because Mary is the most appear, so Mary will return, but Mary is not enough to fill in 4 positions, so the second most appear will take the place, in this case, we use Peter.

SELECT user.name, user.subject
FROM user
INNER JOIN (
SELECT name, COUNT(1) AS occurrences
FROM user
GROUP BY name
) AS user_occurrences
ON user.name = user_occurrences.name
ORDER BY user_occurrences.occurrences DESC, user.name ASC, user.subject ASC
LIMIT 4
edit This might perform better, depending on the RDBMS you're using and the size of the dataset. Try both and compare.
SELECT user.name, user.subject
FROM user
INNER JOIN user AS user_occurrences
ON user.name = user_occurrences.name
GROUP BY user.name --, user.subject Second GROUP BY not needed on MySQL, but it should logically be there
ORDER BY COUNT(user_occurrences.subject) DESC, user.name ASC, user.subject ASC
LIMIT 4

select top 4 from group by Name, Subject and sort by count
MSSQL Code:
select top 4 q.marketname, cc.countryname from (
select top 100 m.MarketName, m.MarketId, COUNT(m.marketname) as [count]
from Common.Country c inner join Common.Market m on c.MarketId = m.MarketId
group by m.MarketName, m.MarketId order by COUNT(m.marketname) desc)
q inner join Common.Country cc
on cc.MarketId = q.MarketId order by [Count] desc
You can make similar mySQL code
Here is relevent MySQL Code
select q.name, cc.subject from (
select m.Name, count(*) as Count
from User m
group by m.Name
order by COUNT(*) desc
LIMIT 100
) q
inner join user cc on cc.Name= q.name
order by Count desc
LIMIT 4
This is weired, you want solution with no effort? Can't you implemet logic in your technology? You should not downvote without understanding solution suggested.

Related

SQL nested query under WHERE

One of the test questions came by with following schemas, to look for the best doctor in terms of:
Best scored;
The most times/attempts;
For each medical procedures (in terms of name)
[doctor] table
id
first_name
last_name
age
1
Phillip
Singleton
50
2
Heidi
Elliott
34
3
Beulah
Townsend
35
4
Gary
Pena
36
5
Doug
Lowe
45
[medical_procedure] table
id
doctor_id
name
score
1
3
colonoscopy
44
2
1
colonoscopy
37
3
4
ulcer surgery
98
4
2
angiography
79
5
3
angiography
84
6
3
embolization
87
and list goes on...
Given solution as follow:
WITH cte AS(
SELECT
name,
first_name,
last_name,
COUNT(*) AS procedure_count,
RANK() OVER(
PARTITION BY name
ORDER BY COUNT(*) DESC) AS place
FROM
medical_procedure p JOIN doctor d
ON p.doctor_id = d.id
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
GROUP BY
name,
first_name,
last_name
)
SELECT
name,
first_name,
last_name
FROM cte
WHERE place = 1;
It'll mean a lot to be clarified on/explain on how the WHERE clause worked out under the subquery:
How it worked out in general
Why must we match the two pp.name and p.name for it to reflect the correct rows...
...
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
...
Thanks a heap!
Above is join with doctor and medical procedure and group by procedure name and you need doctor names with most attempt and best scored.
Subquery will join by procedure avg score and those who have better score than avg will be filtered.
Now there can be multiple doctor better than avg so taken rank by procedure count so most attempted will come first and then you taken first to pick top one

How can I get how many ocurrences are of an event over the event's own mean?

I have a table with grades:
name grade
peter 10
paul 8
mary 7
peter 6
mary 10
paul 5
paul 7
paul 6
mary 2
I would like to count how many grades does each of one have above their own mean.
For example, Peter has two grades, so Peter's mean is 8. Mary has 3 grades, but only 2 are above her own mean. Paul, has 4 grades, but only 2 are above his own mean. I would like to get as result something like:
name count
peter 1
mary 2
paul 2
I tried to do this using subqueries or adding a condition inside a count() column, but I usually get an error that tells me that my subquery returns more than 1 result. How can I achieve this?
Here is one of my attempts:
with staging as (
select name, count(*) as total,MAX(grade) as max_g, MIN(grade) as min_g, AVG(grade) as avg, STD(grade) as std
from students
where course = 'stats'
group by name)
select name,count(IF(grade > (select avg from staging),1,0)) as grades_over_mean from students
GROUP BY name
You can use window functions:
select s.*,
sum(grade > avg_grade) as num_above_average
from (select s.*,
avg(grade) over (partition by name) as avg_grade
from students s
) s;

How to order by from third table (related to Max(), join)

Let me explain the question with the following example: (long but easy to understand)
This is how my MySQL database looks like:
table name: general_info
Movie_ID movie_title
1 Iron Man
2 Superman
3 Batman
table name: cast
Movie_ID Cast_Name
1 Robert Downey Jr.
1 Gwyneth Paltrow
2 Henry Cavill
2 Amy Adams
3 Christian Bale
3 Heath Ledger
Table name production_companies
Movie_ID Production_name
1 Marvel
1 Paramount
2 Legendary Pictures
2 DC Entertainment
3 Snycopy
table name user_cast_preference
user_id user_cast_name user_cast_rating
1 Robert Downey Jr. 95
1 Gwyneth Paltrow 45
1 Christian Bale 80
1 Heath Ledger 90
table name user_production_preference
user_id user_production_name user_production_rating
1 Marvel 85
1 Paraamount 70
1 Syncopy 65
Now, I am able to fetch all movies that are in user's preferred cast + preferred production company, using this query
select general_info.movie_id,movie_title from general_info
inner join cast on general_info.movie_id = cast.movie_id
inner join production_companies on general_info.movie_id = production_companies.movie_id
where cast.cast_name in (select user_cast_name from user_cast_preference)
or production_companies.production_companie_name in (select user_production_name from user_production_preference)
group by movie_title
Current Result:
movie_id moive_title
3 Batman
1 Iron Man
Only batman and Ironman got fetched because at least one of the actor or production company was involved in it (which is also in user's preferred list)
Till now everything is just fine. But I want to do this:
I want to order movies by this algorithem.
I will compare all fetched movies with the given rating in my tables and order them by Top to bottom.
In my case, let's compare Batman and Iron Man.
This is how, I decided to compare.
Take the top rated cast from Ironman + Top rated production company from iron man
Take the top rated cast from Batman + Top rated production company from batman
that is:
95 (iron man) + 85 (marvel) = 180
90 (heath ledger) + 65 (syncopy) = 155
Now Iron man has more rating than Batman, so expected result will be:
movie_id moive_title total_rating
1 Iron Man 180
3 BatMan 155
I hope, I made myself clear.
You want all movies where the user either have an actor or a company (or both) in their preferences. Then you want to sort by the sum of top preferences.
I'd look up the maximum points of user-rated casts and maximum points of user-rated companies per movie first and then outer join these aggregation results to the movie table:
select gi.*
from general_info
left join
(
select c.movie_id, max(ucp.user_cast_rating) as points
from cast c
join user_cast_preference ucp on ucp.user_cast_name = c.cast_name
where ucp.user_id = 1
group by c.movie_id
) p1 on p1.movie_id = gi.movie_id
left join
(
select pc.movie_id, max(upp.user_cast_rating) as points
from production_companies pc
join user_production_preference upp on upp.user_cast_name = pc.cast_name
where upp.user_id = 1
group by pc.movie_id
) p2 on p1.movie_id = gi.movie_id
where p1.points > 0 or p2.points > 0
order by coalesce(p1.points, 0) + coalesce(p2.points, 0) desc;
Not sure where you are getting "production_companies.production_companie_name" in your original code
SELECT general_info.Movie_ID,
general_info.movie_title,
MAX(user_cast_preference.user_cast_rating + user_production_preference.user_production_rating) AS total_rating
FROM ((((general_info
INNER JOIN CAST ON general_info.Movie_ID = cast.Movie_ID)
INNER JOIN production_companies ON cast.Movie_ID = production_companies.Movie_ID)
INNER JOIN user_production_preference ON production_companies.Production_name = user_production_preference.user_production_name)
INNER JOIN user_cast_preference ON cast.Cast_Name = user_cast_preference.user_cast_name)
GROUP BY movie_title
ORDER BY total_rating DESC;

Selecting everything from table 1 with average AND distinct values from table 2 and table 3

I am not entirely sure even how to name this post, because I do not know exactly how to ask it.
I have three tables. One with users, one with foods and one with the users rating of the foods, like such (simplified example):
Foods
id name type
---------------------
1 Apple fruit
2 Banana fruit
3 Steak meat
Users
id username
-----------------
1 Mark
2 Harrison
3 Carrie
Scores (fid = food id, uid = user id)
fid uid score
---------------------
1 1 3
1 2 5
2 1 2
3 2 3
Now, I have this query, which works perfectly:
SELECT fn.name as Food, ROUND(AVG(s.score),1) AS AvgScore FROM Foods fn LEFT JOIN Scores s ON fn.id = s.fid GROUP BY fn.id ORDER BY fn.name ASC
As you can tell, it lists all names from Foods including an average from all users ratings of the food.
I also want to add the unique users own score. (Assume that when Mark is logged in, his uid is set in a session variable or whatever)
I need the following output, if you are logged in as Mark:
Food AvgScore Your Score
Apple 4 3
I have made several attempts to make this happen, but I cannot find the solution. I have learned that if you have a question, it is very likely that someone else has asked it before you do, but I do not quite know how to phrase the question, so I get no answers when googling. A pointer in the right direction would be much appreciated.
You can try with case:
SELECT fn.name as Food,
ROUND(AVG(s.score),1) AS AvgScore,
sum(case s.uid = $uid when s.score else 0 end) as YourScore
FROM Foods fn
LEFT JOIN Scores s ON fn.id = s.fid
GROUP BY fn.id
ORDER BY fn.name ASC
$uid is variable off course.

MYSQL sum of item purchases and its 'up'/'down' votes are multiplied by a factor of 22?

I'm trying to count the number of purchases and 'up'/'down' votes for all items that match a given search term. Unfortunately, as I've set up my query now, the purchases and vote counts are multiplied by a mysterious factor of 22 that I can not figure out where it comes from.
As an example for one of the items: the purchases, up, and down votes should be 7, 2, and 1 respectively but they are instead 154, 44, and 22.
here's my SQL code:
SELECT *
sum(purchaseyesno) as tots,
sum(rating=1) as yes,
sum(rating=0) as no
from items
join items_purchased
on items_purchased.item_id=items.item_id
join accounts
on items.account_id=accounts.account_id
like subject='%album by joe%' or description='%album by joe%'
group by item_id
order by tots desc, yes desc
Here's some sample data:
subject tots yes no full_name
album by joe 154 44 22 joe smith
album by fred 88 44 0 fred jones
Here's how i'd like the data to look:
subject tots yes no full_name
album by joe 7 2 1 joe smith
album by fred 4 2 0 fred jones
Would someone be able to help me figure out what is going on here? I can't figure out this factor of 22 issue which persists despite changing the group by and other things (meaning, this 22 number is independent of the # of returned rows).
You don't show your schema but it might help to use a subquery:
SELECT subject, tots, yes, no, fullnane
FROM (
SELECT item_id, SUM(purchaseyesno) AS tots, SUM(rating=1) AS yes, SUM(rating=0) AS no
FROM items_purchased
GROUP BY item_id
) i
JOIN items ON i.item_id = items.item_id
JOIN accounts ON items.account_id=accounts.account_id
WHERE subject LIKE '%album by joe%' OR description LIKE '%album by joe%'
ORDER BY tots DESC, yes DESC
Firstly, don't do select * if you're grouping by. Remember you must group by all non-aggregated fields.
Secondly, the high amount of results must be comming from those joins. Remove the group by and the aggregated columns and inspect the results and you'll see why you're getting so many records.
Finally... you're missing a where clause there...
SELECT subject,
sum(purchaseyesno)/22 as tots,
sum(CASE WHEN rating=1 THEN 1 END)/22 as yes,
sum(CASE WHEN rating=0 THEN 1 END)/22 as no,
full_name
from items
join items_purchased
on items_purchased.item_id=items.item_id
join accounts
on items.account_id=accounts.account_id
like subject='%album by joe%' or description='%album by joe%'
group by subject,full_name
order by tots desc, yes desc
In this query,the SUM function basically works on the columns purchaseyesno,rating=1,rating=0.
For eg.
Subject purchaseyesno rating full_name
1 5 1 Mark
1 6 0 Mark
2 7 1 Robert
2 8 0 Robert
The above query will return as below.
Subject tots yes no full_name
1 11 1 1 Mark
2 15 1 1 Robert