I have a MySQL table of companies, each company has a popularity score, (the higher the integer, the more popular the company). I'm wanting to select 10 random companies then order by most popular to least. However, I want to favor companies that have a higher popularity score, but not totally exclude companies with a lower popularity score. So in other words, I'm looking for some kind of tolerance system that favors more popular companies.
This is the solution I've come up with so far:
SELECT c.company FROM (
(
SELECT company, popularity FROM companies
WHERE popularity >= (
SELECT ROUND(AVG(popularity) * 0.8) FROM companies
)
ORDER BY RAND() LIMIT 5
)
UNION
(
SELECT company, popularity FROM companies
WHERE popularity >= (
SELECT ROUND(AVG(popularity) * 0.6) FROM companies
)
AND popularity < (
SELECT ROUND(AVG(popularity) * 0.8) FROM companies
)
ORDER BY RAND() LIMIT 3
)
UNION
(
SELECT company, popularity FROM companies
WHERE popularity < (
SELECT ROUND(AVG(popularity) * 0.6) FROM companies
)
ORDER BY RAND() LIMIT 2
)
) AS c ORDER BY c.popularity DESC;
What I'm doing is combining three different selects.
The first is selecting 5 random companies whose popularity score is
equal to or greater than the top 20% of the average popularity score.
The second is selecting 3 random companies whose popularity score is
equal to or greater than the top 40% of the average popularity score,
but lower than the top 20%.
And finally the third is selecting 2 random companies whose
popularity score is lower than the top 40% of the average popularity
score.
I've simplified the SQL a bit, I'm also selecting logo locations, the company URL and any affiliate codes. The goal is to use the SQL on a website to basically say "here are just some of the brands we stock".
This solution works, but it just seems a bit clunky and over-engineered. Can anybody suggest a more efficient way of including a tolerance system?
Related
I have a RatingTable in MySQL:
player_id INT, game_type INT, rating INT
There are 5 different game types (e.g. chess, checks, go, etc.)
Higher rating in game is better
I want to get top 10 player_id with highest TOTAL rating. Total rating = sum of all ratings for all game types.
I can solve it like this:
SELECT player_id, SUM(rating) as TotalRating
FROM RatingTable
GROUP BY player_id
ORDER BY TotalRating DESC
LIMIT 10;
But my concern is that it will take to much memory&cpu if there're 10 million players with 5 different game types. It will sum all ratings for all 10mln players and then sort it. How can I optimize it?
I have a DB Table user_points which contains user's points and I am trying to calculate ranking based on points. It is working fine for all users except users having 1 point.
If user have 1 point it is showing it's rank as 0 but it should display it's rank as last or in last numbers like: 12083 etc.
Higher points are, ranking should be higher as well. For example:
1000 points = rank 1
1 point = rank 1223
Following is the query.
SELECT id, mobileNo, points,
FIND_IN_SET( points, (SELECT GROUP_CONCAT( points ORDER BY points DESC )
FROM users_points )) AS rank
FROM users_points
WHERE mobileNo = '03214701777'
What should I change to fix it?
SELECT a.id, a.mobileNo, a.points,
IFNULL((SELECT COUNT(*) AS rank
FROM users_points b
WHERE b.points<a.points), 0)+1 as rank
FROM user_points a
WHERE a.mobileNo = '03214701777'
Seems to be what you are looking for. While it is still very innefficient it is better than your approach using FIND_IN_SET(). If you really want to use FIND_IN_SET() then you need to pad the scores to a consistent width and divide by the width+1 to get the rank.
I have a table in which student marks in each subject and i have to get query in such a way that i will able to get all top 5 student in every subject who secure highest marks.
Here is a sample table:
My expected output look somthing like :
Top five student in PCM, ART, PCB on the basis of students marks,And also if two or more student secure same than those record also need to be in list with single query.
Original Answer
Technically, what you want to accomplish is not possible using a single SQL query. Had you only wanted one student per subject you could have achieved that using GROUP BY, but in your case it won't work.
The only way I can think of to get 5 students for each subject would be to write x queries, one for each subject and use UNION to glue them together. Such query will return a maximum of 5x rows.
Since you want to get the top 5 students based on the mark, you will have to use an ORDER BY clause, which, in combination with the UNION clauses will cause an error. To avoid that, you will have to use subqueries, so that UNION and ORDER BY clauses are not on the same level.
Query:
-- Select the 5 students with the highest mark in the `PCM` subject.
(
SELECT *
FROM student
WHERE subject = 'PCM'
ORDER BY studentMarks DESC
LIMIT 5
)
UNION
(
SELECT *
FROM student
WHERE subject = 'PCB'
ORDER BY studentMarks DESC
LIMIT 5
)
UNION
(
SELECT *
FROM student
WHERE subject = 'ART'
ORDER BY studentMarks DESC
LIMIT 5
);
Check out this SQLFiddle to evaluate the result yourself.
Updated Answer
This update aims to allow getting more than 5 students in the scenario that many students share the same grade in a particular subject.
Instead of using LIMIT 5 to get the top 5 rows, we use LIMIT 4,1 to get the fifth highest grade and use that to get all students that have a grade more or equal to that in a given subject. Though, if there are < 5 students in a subject LIMIT 4,1 will return NULL. In that case, we want essentially every student, so we use the minimum grade.
To achieve what is described above, you will need to use the following piece of code x times, as many as the subjects you have and join them together using UNION. As can be easily understood, this solution can be used for a small handful of different subjects or the query's extent will become unmaintainable.
Code:
-- Select the students with the top 5 highest marks in the `x` subject.
SELECT *
FROM student
WHERE studentMarks >= (
-- If there are less than 5 students in the subject return them all.
IFNULL (
(
-- Get the fifth highest grade.
SELECT studentMarks
FROM student
WHERE subject = 'x'
ORDER BY studentMarks DESC
LIMIT 4,1
), (
-- Get the lowest grade.
SELECT MIN(studentMarks)
FROM student
WHERE subject = 'x'
)
)
) AND subject = 'x';
Check out this SQLFiddle to evaluate the result yourself.
Alternative:
After some research I found an alternative, simpler query that will yield the same result as the one presented above based on the data you have provided without the need of "hardcoding" every subject in its own query.
In the following solution, we define a couple of variables that help us control the data:
one to cache the subject of the previous row and
one to save an incremental value that differentiates the rows having the same subject.
Query:
-- Select the students having the top 5 marks in each subject.
SELECT studentID, studentName, studentMarks, subject FROM
(
-- Use an incremented value to differentiate rows with the same subject.
SELECT *, (#n := if(#s = subject, #n +1, 1)) as n, #s:= subject
FROM student
CROSS JOIN (SELECT #n := 0, #s:= NULL) AS b
) AS a
WHERE n <= 5
ORDER BY subject, studentMarks DESC;
Check out this SQLFiddle to evaluate the result yourself.
Ideas were taken by the following threads:
Get top n records for each group of grouped results
How to SELECT the newest four items per category?
Select X items from every type
Getting the latest n records for each group
Below query produces almost what I desired, may this query helps others in future.
SELECT a.studentId, a.studentName, a.StudentMarks,a.subject FROM testquery AS a WHERE
(SELECT COUNT(*) FROM testquery AS b
WHERE b.subject = a.subject AND b.StudentMarks >= a.StudentMarks) <= 2
ORDER BY a.subject ASC, a.StudentMarks DESC
This question already has an answer here:
Mysql min and max values and corresponding "date" for each month
(1 answer)
Closed 8 years ago.
So I want to be able to show the most expensive movie to rent and the least expensive movie to rent. Basically I want to show the movie title along with its price. Now I am able to get the highest and lowest rental price with this query:
SELECT MAX(rental_rate) as MaxRate, MIN(rental_rate) as MinRate
FROM film;
However how do I edit that query above to show the movie titles in relation to their rates?
I have also tried:
SELECT title (SELECT MAX(rental_rate) FROM film as MaxRate),
(SELECT MIN(rental_rate) FROM film as MinRate)
FROM film;
But that did not work.
Any suggestions?
A proper solution can be found in the question I referred to in comments, but it is a bit complex. I think another, simpler solution would be to ORDER BY rate and use LIMIT to get 1 record. You can use UNION to combine two similar queries to get the lowest and highest rate:
(SELECT title, rental_rate
FROM film
ORDER BY rental_rate
LIMIT 1)
UNION
(SELECT title, rental_rate
FROM film
ORDER BY rental_rate DESC
LIMIT 1)
UNION ALL could be used too, but then you could get the same title if the table contains only one film.
Note that this query will return one lowest rate title and one highest rate title. If you want all titles that share the lowest or highest rate, you'll need that other solution.
What you want is two rows with different conditions, so keep them separate:
(SELECT title, rental_rate, "most expensive" as which_title
from film
ORDER BY rental_rate DESC LIMIT 1)
UNION ALL
(SELECT title, rental_rate, "least expensive" from film
ORDER BY rental_rate ASC LIMIT 1)
with working sqlfiddle: http://sqlfiddle.com/#!2/1e290/3
you can also use below query if you want the min and max of rates with the title of them:
select title, (SELECT MAX(rental_rate) FROM film) from film
where rental_rate=(SELECT MAX(rental_rate) FROM film)
union
select title, (SELECT MIN(rental_rate) FROM film) from film
where rental_rate=(SELECT MIN(rental_rate) FROM film)
In my table i have team and points column and I want to get top 5 teams .Teams with same points should be grouped and consider as one of the ranks so if 5 teams are having same points then all should come as one of the rank and next suceeding records according to team points
TRY
SELECT DISTINCT(point), team
FROM tableTeam
ORDER BY points DESC LIMIT 5
SELECT team,
points,
(SELECT COUNT(*)
FROM teams t2
WHERE t2.points > t1.points) + 1 rank
FROM teams t1
ORDER BY points DESC
LIMIT 5
There's no window functions in MySQL, so you'll want to extract the rank in your scripts.
Also, if I'm making sense of your ranking criteria, you're actually interested in getting the top 5 teams plus any additional teams that might have the same number of points as that in the 5th rank.
If so, your limit should be applied to a subquery on the point criteria:
select name, points
from teams
where points >= (
select points
from teams
order by points desc
limit 1 offset 4
)
order by points desc, name
If not, a simple order by/limit will do:
select name, points
from teams
order by points desc, name
limit 5