Assume 2 tables:
TABLE: team
-----------
| team_id |
===========
| 1 |
-----------
| 2 |
-----------
TABLE: team_member
-------------------------------------------
| team_member_id | team_id | age | gender |
===========================================
| 1 | 1 | 20 | Male |
-------------------------------------------
| 1 | 2 | 25 | Male |
-------------------------------------------
| 1 | 2 | 23 | Female |
-------------------------------------------
How do I search for things like this:
List of all teams that are only male, between ages of 18 and 25.
List of all teams that are male and female, where either gender is between ages of 18 and 25.
Etc.
I'm basically looking for general strategies on how to apply certain filters (e.g. age) across all the children records of team. Suggestions?
team, team_member
You can't just apply a simple WHERE clause if you are checking for only an all male or all female team... it needs to be compared against the full set of team members. Try
select
tm.team_id,
sum( if( tm.gender = 'Male' AND tm.age between 18 and 25, 1, 0 )) as MaleInRange,
count(*) AllTeamMembers
from
team_member tm
group by
tm.Team_ID
having
MaleInRange = AllTeamMembers
Similarly, to ensure you have BOTH male AND female...
select
tm.team_id,
sum( if( tm.age between 18 and 25, 1, 0 )) as AgeInRange,
sum( if( tm.gender = 'Male', 1, 0 )) as MaleCnt,
sum( if( tm.gender = 'Female', 1, 0 )) as FemaleCnt,
count(*) AllTeamMembers
from
team_member tm
group by
tm.Team_ID
having
AgeInRange = AllTeamMembers
AND MaleCnt > 0
AND FemaleCnt > 0
Something like -
SELECT * FROM t1
WHERE key_col > 1
AND key_col < 10;
Check this example - http://www.roseindia.net/sql/mysql-example/mysql-range.shtml. Hope it helps!
You can do it as below:
1. SELECT * FROM T2 WHERE Gender='Male' and Age BETWEEN 18 AND 25.
2. SELECT * FROM T2 WHERE Age BETWEEN 18 AND 25.
Hope it helps!
BTW team_member_id must be unique or this column is useless !!
List of all teams that are male and female, where either gender is between ages of 18 and 25.
SELECT * FROM team_member WHERE Age BETWEEN 18 AND 25 GROUP BY team_id.
result
| team_member_id | team_id | age | gender |
===========================================
| 1 | 1 | 20 | Male |
-------------------------------------------
| 1 | 2 | 25 | Male |
-------------------------------------------
Related
I have two tables. price_code table has a foreign key that references on site table.
price_code
___________
priceCodeID
siteID
price
and
site
______________
siteID
operatorName
country
I need to select the rows that, for example, country = "Peru" and have a price = 0 as I want, but in price_code there are some rows that have the same siteID and have the two validations.
I need not consider a row if the siteID has a price different than zero in any register.
SELECT s.siteID, pc1.price, s.country, s.operatorName FROM price_code AS pc1
INNER JOIN site AS s ON s.siteID = pc1.siteID
WHERE country = "Peru"
AND operatorName = "Movistar" AND price = 0
AND pc1.siteID NOT IN (
SELECT siteID FROM price_code WHERE pc1.price <> 0
);
Some data on price_code:
priceCodeID | siteID | price
_____________________________
1000 | 64 | 0
1001 | 64 | 100
1002 | 27 | 0
1003 | 18 | 100
1004 | 17 | 1
And for site
siteID | operatorName | country
___________________________________
64 | Peru | Movistar
27 | Peru | Movistar
18 | Argentina | Movistar
27 | Bolivia | Claro
And my result might be:
siteID | price | country | operatorName
____________________________________________
27 | 0 | Peru | Movistar
Use NOT EXISTS:
SELECT s.siteID, p.price, s.country, s.operatorName
FROM price_code AS p INNER JOIN site AS s
ON s.siteID = p.siteID
WHERE s.country = "Peru" AND s.operatorName = "Movistar" AND p.price = 0
AND NOT EXISTS (
SELECT 1 FROM price_code
WHERE siteID = p.siteID AND price <> 0
)
See the demo.
Results:
> siteID | price | country | operatorName
> -----: | ----: | :------ | :-----------
> 27 | 0 | Peru | Movistar
Your query is almost correct. You need to change WHERE pc1.price_code <> 0 to WHERE price_code <> 0. By using the pc1 prefix you're making this a correlated subquery, so it's testing the row in the main query, rather than filtering rows to be returned by the subquery.
You can make it clearer by adding an alias to the subquery:
AND pc1.siteID NOT IN (
SELECT siteID FROM price_code AS pc2 WHERE pc2.price <> 0
);
Suppose you have a a multi-event competition where competitors can attempt any event an arbitrary number of times. (weird, I know.)
How do pull out a desired player's best time for each event,
and assign it a placing? (1st 2nd 3rd...)
Data example: Desired output:
Name | Event | Score Name | Event | Score | Rank
-------------------- ----------------------------
Bob 1 50 Given input: "Bob"
Bob 1 100 Bob 1 100 1
Bob 2 75 Bob 2 75 3
Bob 3 80 Bob 3 80 2
Bob 3 65
Given input: "Jill"
Jill 2 75 Jill 2 90 1
Jill 2 90 Jill 3 60 3
Jill 3 60
Given input: "Chris"
Chris 1 70 Chris 1 70 2
Chris 2 50 Chris 2 85 2
Chris 2 85 Chris 3 100 1
Chris 3 100
This is a build up of my previous question:
Multi-event tournament standings
I feel understand that problem much better (Thanks!), but I cannot bridge the gap to this version of the problem.
I have SQL 5.x so I cant use stuff like Rank(). This will also be crunching many thousands of scores.
Desired output can be acheaved with this query:
select
IF(event is NULL, CONCAT('Given input: "', name,'"'), name) as name,
IF(event is NULL, '', event) as event,
IF(event is NULL, '', max(score)) as score,
IF(event is NULL, '', (
select count(s2.name) + 1
from (
select name, max(score) as score
from scores es
where es.event = s.event
group by es.name
order by score desc
) s2
where s2.score > max(s.score)
)) as `rank`
from scores s
group by name, event with rollup
having name is not NULL
order by name, event;
And output (if run query in mysql cli):
+----------------------+-------+-------+------+
| name | event | score | rank |
+----------------------+-------+-------+------+
| Given input: "Bob" | | | |
| Bob | 1 | 100 | 1 |
| Bob | 2 | 75 | 3 |
| Bob | 3 | 80 | 2 |
| Given input: "Chris" | | | |
| Chris | 1 | 70 | 2 |
| Chris | 2 | 85 | 2 |
| Chris | 3 | 100 | 1 |
| Given input: "Jill" | | | |
| Jill | 2 | 90 | 1 |
| Jill | 3 | 60 | 3 |
+----------------------+-------+-------+------+
11 rows in set, 3 warnings (0.00 sec)
Should work on any Mysql 5.
You can get the highest score per event by an aggregation by event taking the max(). To simulate a dense_rank() you can use a subquery counting the scores higher than or equal to the current score per event.
For a particular contestant (here Bob) that makes:
SELECT d1.name,
d1.event,
max(d1.score) score,
(SELECT count(*)
FROM (SELECT d2.event,
max(d2.score) score
FROM data d2
GROUP BY d2.event,
d2.name) x1
WHERE x1.score >= max(d1.score)
AND x1.event = d1.event) rank
FROM data d1
WHERE d1.name = 'Bob'
GROUP BY d1.event
ORDER BY d1.event;
And for all of them at once:
SELECT d1.name,
d1.event,
max(d1.score) score,
(SELECT count(*)
FROM (SELECT d2.event,
max(d2.score) score
FROM data d2
GROUP BY d2.event,
d2.name) x1
WHERE x1.score >= max(d1.score)
AND x1.event = d1.event) rank
FROM data d1
GROUP BY d1.name,
d1.event
ORDER BY d1.name,
d1.event;
db<>fiddle
E.g.:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,name VARCHAR(12) NOT NULL
,event INT NOT NULL
,score INT NOT NULL
);
INSERT INTO my_table (name,event,score) VALUES
('Bob' ,1, 50),
('Bob' ,1,100),
('Bob' ,2, 75),
('Bob' ,3, 80),
('Bob' ,3, 65),
('Jill' ,2, 75),
('Jill' ,2, 90),
('Jill' ,3, 60),
('Chris',1, 70),
('Chris',2, 50),
('Chris',2, 85),
('Chris',3,100);
SELECT a.*
, FIND_IN_SET(a.score,b.scores) my_rank
FROM my_table a -- it's possible that this really needs to be a repeat of the subquery below, so
-- ( SELECT m.* FROM my_table m JOIN (SELECT name,event,MAX(score) score FROM my_table
-- GROUP BY name, event) n ON n.name = m.name AND n.event = m.event AND n.score = m.score) AS a
JOIN
(
SELECT x.event
, GROUP_CONCAT(DISTINCT x.score ORDER BY x.score DESC) scores
FROM my_table x
JOIN
( SELECT name
, event
, MAX(score) score
FROM my_table
GROUP
BY name
, event
) y
ON y.name = x.name
AND y.event = x.event
AND y.score = x.score
GROUP
BY x.event
) b
ON b.event = a.event
WHERE FIND_IN_SET(a.score,b.scores) >0;
+----+-------+-------+-------+------+
| id | name | event | score | rank |
+----+-------+-------+-------+------+
| 2 | Bob | 1 | 100 | 1 |
| 3 | Bob | 2 | 75 | 3 |
| 4 | Bob | 3 | 80 | 2 |
| 6 | Jill | 2 | 75 | 3 |
| 7 | Jill | 2 | 90 | 1 |
| 8 | Jill | 3 | 60 | 3 |
| 9 | Chris | 1 | 70 | 2 |
| 11 | Chris | 2 | 85 | 2 |
| 12 | Chris | 3 | 100 | 1 |
+----+-------+-------+-------+------+
An interesting SQL query CHALLENGE:
A table named athelets consisting of id, ath_id, name, score, date.
+----+--------+-----------------+--------+------------+
| id | ath_id | name | record | r_date |
+----+--------+-----------------+--------+------------+
| 1 | 2 | John Wayne | 79 | 2010-07-08 |
| 2 | 7 | Ronald Regan | 51 | 2000-03-22 |
| 3 | 1 | Ford Harrison | 85 | 2009-11-13 |
| 4 | 2 | John Wayne | 69 | 2017-01-01 |
Please write a sql query to list the average value of the top three scores of each athlete, something like:
ath_id: 1, the arithmetic mean of his/her top 3 records: 77
ath_id: 2, the arithmetic mean of his/her top 3 records: 73
ath_id: 3, the arithmetic mean of his/her top 3 records: 47
select ath_id, avg(record)
from
(select ath_id, record
from atheletes as t1
where
(select count(*) from atheletes where t1.ath_id=ath_id and record > t1.record) < 3) as d
group by ath_id;
The above query should works as expected.
Assuming combinations of athletes and records are unique...
SELECT ath_id
, ROUND(AVG(record),2) top3_avg
FROM
( SELECT x.*
FROM athletes x
JOIN athletes y
ON y.ath_id = x.ath_id
AND y.record >= x.record
GROUP
BY x.id
HAVING COUNT(*) <=3
) a
GROUP
BY ath_id;
I have a table of users with their scores for each level of a game:
id | user_id | level | score
1 | David | 1 | 20
2 | John | 1 | 40
3 | John | 2 | 30
4 | Mark | 1 | 60
5 | David | 2 | 10
6 | David | 3 | 80
7 | Mark | 2 | 20
8 | John | 3 | 70
9 | David | 4 | 50
10 | John | 4 | 30
What is the SQL query needed to get for each level, who has the highest score?
The result should be:
id | user_id | level | score
4 | Mark | 1 | 60
3 | John | 2 | 30
6 | David | 3 | 80
9 | David | 4 | 50
Thank you
If you want to get ties, then you can do something like this:
select s.*
from scores s
where s.score = (select max(s2.score) from scores s2 where s2.level = s.level);
You could get one row per level by aggregating this:
select s.level, s.score, group_concat(s.user_id)
from scores s
where s.score = (select max(s2.score) from scores s2 where s2.level = s.level)
group by s.level, s.score;
This combines the users (if there is more than one) into a single field.
order by score desc in sub query, then select max(score) group by level.
select id, user_id , level , max(score) as score
from
(select * from scores order by score desc)A
group by level
If you only want the user, who reached the highest score first (no ties per level):
select *
from users u1
where id = (
select id
from users u2
where u2.level = u1.level
order by score desc, id asc
limit 1
)
You should have indexes (id) and (level, score, id)
SELECT TestID, Grade FROM tests_points;
Returns:
+--------+-------+
| TestID | Grade |
+--------+-------+
| 10 | 125 |
| 11 | 110 |
| 12 | 100 |
| 13 | 75 |
| 14 | 50 |
| 15 | 65 |
| 16 | 70 |
| 17 | 100 |
| 18 | 100 |
+--------+-------+
But, tests ID 17 and 18 are "bonus tests", so I need replace the two lower grades by these two ones, and return the SUM of all grades.
So, how I can "replace"the two lower grades (From TestID 14 and 15) by testID 17 and 18 grades.
The "correct grade list" would be:
+--------+-------+
| TestID | Grade |
+--------+-------+
| 10 | 125 |
| 11 | 110 |
| 12 | 100 |
| 13 | 75 |
| 14 | 100|
| 15 | 100|
| 16 | 70 |
+--------+-------+
In the end I just need the SUM of all grades, fixing the lower grades.
SELECT SUM(Grade) FROM tests_points;
How can I do that?
This is what you need!
select testID, GREATEST(
IF(
grade = (select min(grade) from test_points),(select grade from test_points where testID = 17),
grade)
,
IF(grade = (select min(grade) from test_points WHERE grade > ( SELECT min(grade) FROM test_points))
,(select grade from test_points where testID = 18),
grade)) as Score
from test_points
where testID not in(17, 18)
Demo on SQLfiddle.com
the following query will get probably get what you want..
SELECT ttal-garba FROM
(SELECT sum(c.grade) as garba FROM (SELECT * from tests_points where testid < 17 order by grade asc limit 2) as c) as a,
(SELECT sum(grade) as ttal FROM tests_points) as b
(I know.. hard coding everything is bad ..)
If your just trying to add up all the values except the bottom two you can do something lik,e this.
First add a new field lets say test_type:
UPDATE test_points
SET test_type = 'test';
Now we can just select using group by the sum. For example
SELECT test_type, (SUM(Grade) OVER (ORDER BY Grade DESC))
FROM test_Points
LIMIT 7;
This will return the sum of all values, but it will show 7 columns. If you want only the total, Throw a group by into the mix, and it will narrow it down to one field, or just select the 7th row number.