This is a simplification table structure that i have.
I have two tables.
TABLE 1
ID PTS PID TID
----------------------------------------
1 3 1 22
2 10 1 22
3 5 1 22
4 1 2 58
5 0 2 58
7 0 3 47
8 5 3 47
15 5 4 51
15 3 4 51
9 7 3 6
10 0 1 8
11 2 1 8
12 4 5 1
13 1 6 9
14 2 5 12
15 5 4 61
15 6 4 61
15 2 4 61
16 0 7 100
ect. ect. ect. ect.
TABLE 2
NAME PID
-------------------
Jhon 1
Peter 2
Lisa 3
Doe 4
Joey 5
Mike 6
Debby 7
ect. ect.
Now i need to select the latest two PTS from each TID and match them whit PID row on table 2. and calculate the Average.
THE DESIRED OUTCOME
NAME AVG
-------------------
Jhon 4,25
Peter 1,00
Lisa 6,00
Doe 4,00
Joey 3,00
Mike 1,00
Debby 0,00
ect. ect.
Clarification: the PID row are relational. Multiple TIDs can have the same PID and TIDs can have multiple PTS. I am using PDO socket.
At the time my query is:
$query = $db->prepare("SELECT IFNULL(AVG(pts), 0) AS P, TA1.PID AS TA1PID, name AS N FROM ".
"table1 TA1 LEFT JOIN table2 TA2 ON TA1.PID = TA2.PID ".
"GROUP BY name, TA2.PID ".
"ORDER BY TA1.id DESC");
But this calculates all the points(PTS) from TID. but i would like to only calculate the Two latest points(PTS) per TID. I have tried different queries the whole day but i cant figure it out. I am pretty new to SQL I managed to get one example working but with pure PHP and its not pretty :D
sqlFiddle: LINK
The problem is that it calculates all the TID points in Average. It only should calculate the two last entries per TID
I hope this is a clear question. I have tried my best to explain my problem. If any questions please ask. I have read other similar problems than mine here on Stackoverflow but i could not modify them to work for me.
You can select only the most recent 2 rows from the first table using this query
select t1.id, t1.pts, t1.pid, t1.tid
from table1 t1
join table1 t2 on t2.id >= t1.id and t1.tid = t2.tid
group by t1.id
having count(*) <= 2
and plug it into your original query instead of table 1
SELECT IFNULL(AVG(pts), 0) AS AVG, TA1.PID AS
Table1_ID, name AS Name FROM
(
select t1.id, t1.pts, t1.pid, t1.tid
from table1 t1
join table1 t2 on t2.id >= t1.id and t1.tid = t2.tid
group by t1.id
having count(*) <= 2
)
TA1 LEFT JOIN table2 TA2 ON TA1.PID = TA2.PID
GROUP BY name, TA2.PID
ORDER BY TA1.id DESC
Group Per N are pretty easy in Most DBs. Just use ROW_NUMBER. Unfortunately MySQL doesn't have them so we must simulate it
SELECT name,
Avg(PTS)
FROM
(
SELECT *,
CASE
WHEN #previousPID IS NULL
OR #previousTID IS NULL
OR ( #previousPID = ORDERED.pid
AND #previousTID = ORDERED.tid )
THEN #rownum := #rownum + 1
ELSE #rownum := 1
end rn,
#previousPID := ORDERED.pid ,
#previousTID := ORDERED.tid
FROM (SELECT t2.name,
t2.pid,
t1.id,
t1.tid ,
t1.Pts
FROM table2 t2
INNER JOIN table1 t1
ON T2.pid = t1.pid
ORDER BY t1.pid,
t1.tid,
t1.id DESC)ORDERED,
(SELECT #rownum := 0,
#previousPID := NULL,
#previousTID := NULL) t) CTE
WHERE CTE.rn <= 2
GROUP BY name
Which has the following Results
| NAME | AVG(PTS) |
|-----------|----------|
| Doe | 4 |
| Jhon | 4.25 |
| Joey | 3 |
| Lisa | 4 |
| Mike | 1 |
| No points | 0 |
| Peter | 0.5 |
DEMO
Looking at the intermediate results may help to understand how the CASE statements generate the rownumbers
| NAME | PID | ID | TID | PTS | #ROWNUM := 0 | #PREVIOUSPID := NULL | #PREVIOUSTID := NULL | RN | #PREVIOUSPID := ORDERED.PID | #PREVIOUSTID := ORDERED.TID |
|-----------|-----|----|-----|-----|--------------|----------------------|----------------------|----|-----------------------------|-----------------------------|
| Jhon | 1 | 3 | 22 | 5 | 0 | (null) | (null) | 1 | 1 | 22 |
| Jhon | 1 | 2 | 22 | 10 | 0 | (null) | (null) | 2 | 1 | 22 |
| Jhon | 1 | 1 | 22 | 3 | 0 | (null) | (null) | 3 | 1 | 22 |
| Jhon | 1 | 12 | 8 | 2 | 0 | (null) | (null) | 1 | 1 | 8 |
| Jhon | 1 | 11 | 8 | 0 | 0 | (null) | (null) | 2 | 1 | 8 |
| Peter | 2 | 5 | 58 | 0 | 0 | (null) | (null) | 1 | 2 | 58 |
| Peter | 2 | 4 | 58 | 1 | 0 | (null) | (null) | 2 | 2 | 58 |
| Lisa | 3 | 7 | 47 | 5 | 0 | (null) | (null) | 1 | 3 | 47 |
| Lisa | 3 | 6 | 47 | 0 | 0 | (null) | (null) | 2 | 3 | 47 |
| Lisa | 3 | 10 | 6 | 7 | 0 | (null) | (null) | 1 | 3 | 6 |
| Doe | 4 | 9 | 51 | 3 | 0 | (null) | (null) | 1 | 4 | 51 |
| Doe | 4 | 8 | 51 | 5 | 0 | (null) | (null) | 2 | 4 | 51 |
| Doe | 4 | 19 | 61 | 2 | 0 | (null) | (null) | 1 | 4 | 61 |
| Doe | 4 | 17 | 61 | 6 | 0 | (null) | (null) | 2 | 4 | 61 |
| Doe | 4 | 16 | 61 | 5 | 0 | (null) | (null) | 3 | 4 | 61 |
| Joey | 5 | 13 | 1 | 4 | 0 | (null) | (null) | 1 | 5 | 1 |
| Joey | 5 | 15 | 12 | 2 | 0 | (null) | (null) | 1 | 5 | 12 |
| Mike | 6 | 14 | 9 | 1 | 0 | (null) | (null) | 1 | 6 | 9 |
| No points | 7 | 18 | 100 | 0 | 0 | (null) | (null) | 1 | 7 | 100 |
Related
Hy I have 2 table
1.application
id | name | status
====================
1 | morvick | complete
2 | siti | prosess
3 | boby | complete`
2.application_test
id | application_id | test_id | result
======================================
1 | 1 | 1 | 70
2 | 1 | 2 | 80
3 | 1 | 3 | 90
4 | 2 | 1 | 60
5 | 2 | 2 | 80
6 | 2 | 3 | 70
7 | 3 | 1 | 90
8 | 3 | 2 | 70
9 | 3 | 3 | 60
10| 3 | 4 | 80
my Question is :
==================
1. how to find the maximum value at each test_id
2. how I can to get or total applicant_id where status complete
for example to be like this :
test_id | result_max | total_applicant_status(complete)
1 | 90 | 2
2 | 80 | 2
3 | 90 | 2
4 | 80 | 1
SELECT MAX(value) FROM table WHERE test_id = 1;
or perhaps SELECT value, test_id FROM table ORDER BY value DESC;
and for the next part, this may give what you want.
SELECT at.test_id, MAX(at.result), COUNT(IF(status='complete', 1, 0)) FROM application a LEFT JOIN application_test at ON a.id = at.application_id GROUP BY application_id;
Currently I am developing a system to support a competition. In this competition there are 11 judges that give a grade to the participant. This grade is stored in a table per judge. With the following Select statement I retrieve the total score from the MySQL database (and also some inner joins to select other information from other tables).
Now I want to have ranking in this system, where also ex aequo is taken into account. I have tried several mysql solutions from this forum. But somehow the statement is always incorrect. Hopefully, someone can help me.
SELECT optreden.id, optreden.wedstrijd, optreden.jaartal,
liedje.titel, club.volledige_clubnaam,
(SELECT SUM(score.score_lied) FROM score WHERE score.optreden=optreden.id) AS score
FROM optreden
INNER JOIN liedje ON optreden.liedje=liedje.id
INNER JOIN club ON liedje.uitvoerende_club=club.id
WHERE optreden.wedstrijd="voorselectie" AND optreden.jaartal=2014
GROUP BY optreden.id
ORDER BY score_lied DESC
With this query, I get the following result:
+----+--------------+------+----------+--------------+------+
| id | Competition | Year | Title | Artist (club)| Score|
+----+--------------+------+----------+--------------+------+
| 12 | voorselectie | 2014 | Song 1 | Club 1 | 792 |
| 16 | voorselectie | 2014 | Song 2 | Club 2 | 600 |
| 10 | voorselectie | 2014 | Song 3 | Club 3 | 600 |
| 11 | voorselectie | 2014 | Song 3 | Club 3 | 500 |
| 12 | voorselectie | 2014 | Song 3 | Club 3 | 400 |
The query fetches information from the following tables:
Score: in this table the scores given by the 11 judges is stored as
line-items (result is given below);
Optreden: in this table the information from the club and the song is linked;
Club: information from the club,
such as name;
Liedje: information about the song, performed by the
club.
All the tables are linked by id's. In the result, there should come a column, with the ranking of the club.
The result from the table score is as follows:
+----+----+--------+----+
| ID | Pid| Userid | 85 |
+----+----+--------+----+
| 1 | 12 | 444 | 85 |
| 12 | 12 | 454 | 92 |
| 13 | 12 | 445 | 87 |
| 14 | 12 | 446 | 56 |
| 15 | 12 | 447 | 81 |
| 16 | 12 | 448 | 78 |
| 17 | 12 | 449 | 55 |
| 18 | 12 | 450 | 69 |
| 19 | 12 | 451 | 88 |
| 20 | 12 | 452 | 69 |
| 21 | 12 | 453 | 32 |
+----+----+--------+----+
Where the columns represent the following:
- ID: the id of the score in the table;
- Pid: the id of the performance (with this performing club and song are linked);
- Userid: the userid of the 11 judges;
- Score: the score given by each judge.
The end result should be as follows:
+----+--------------+------+----------+--------------+------+------+
| id | Competition | Year | Title | Artist (club)| Score| Rank |
+----+--------------+------+----------+--------------+------+------+
| 12 | voorselectie | 2014 | Song 1 | Club 1 | 792 | 1 |
| 16 | voorselectie | 2014 | Song 2 | Club 2 | 600 | 2 |
| 10 | voorselectie | 2014 | Song 3 | Club 3 | 600 | 2 |
| 11 | voorselectie | 2014 | Song 3 | Club 3 | 500 | 4 |
| 12 | voorselectie | 2014 | Song 3 | Club 3 | 400 | 5 |
I have tried the solution from Neville before and now again. I have rewritten the SQL statement as follows:
SELECT optreden.id, optreden.wedstrijd, optreden.jaartal,
liedje.titel, club.volledige_clubnaam,
(SELECT SUM(score.score_lied) FROM score WHERE score.optreden=optreden.id) AS score, CASE
WHEN #prev_value = score THEN #rank_count
WHEN #prev_value := score THEN #rank_count := #rank_count + 1
END AS rank,
FROM optreden
INNER JOIN liedje ON optreden.liedje=liedje.id
INNER JOIN club ON liedje.uitvoerende_club=club.id
WHERE optreden.wedstrijd="voorselectie" AND optreden.jaartal=2014
GROUP BY optreden.id
ORDER BY score_lied DESC
Here I get the error: Unknown column 'score' in 'field list'..
It seems that you are getting this error simply because there is no field score... I have extracted your inner SELECT into a JOIN so the real field can be used directly. (Not tested though, sorry)
SELECT optreden.id, optreden.wedstrijd, optreden.jaartal,
liedje.titel, club.volledige_clubnaam,
SUM(score.score_lied) AS score, CASE
WHEN #prev_value = SUM(score.score_lied) THEN #rank_count
WHEN #prev_value := SUM(score.score_lied) THEN #rank_count := #rank_count + 1
END AS rank,
FROM optreden
INNER JOIN liedje ON optreden.liedje=liedje.id
INNER JOIN club ON liedje.uitvoerende_club=club.id
LEFT JOIN score ON score.optreden=optreden.id
WHERE optreden.wedstrijd="voorselectie" AND optreden.jaartal=2014
GROUP BY optreden.id
ORDER BY score_lied DESC
Please note that you might face another little problem with this kind of ranking: Normally, when two entities end up on the same rank, the next rank is not given to the next person. Here's what I mean with an example, with two persons ranking in 3:
1, 2, 3, 3, 5
But in your code, it will give:
1, 2, 3, 3, 4
I hope this helps.
I have tried several things. However, I had forgotten to tell you one important feature I was gonna use. The ranking of the clubs should be presented through a presentation on a big screen. Therefor the MySQL code changed a little.
I have added a support table to my database. In this support table the sum of the scores is recorded with a INSERT INTO...SELECT statement.
When this insert is done. An update script is launched which makes the ranking in the table and also makes a correct calculation for ex aequo.
Here's the update statement:
SET #rank=0, #last_score = null, #tie_build_up = 0;
UPDATE ranking_voorselectie_lied
SET rank= #rank:= if(#last_score = totaal_score_lied, #rank, #rank+#tie_build_up+1),
tie_build_up= #tie_build_up:= if(#last_score = totaal_score_lied, #tie_build_up+1, 0),
last_score= #last_score:= totaal_score_lied
WHERE wedstrijd="voorselectie" AND jaar=2014
ORDER BY totaal_score_lied DESC;
This gives me the following result:
+--------------+------+----------+-------------------+------+-----+------------+
| Wedstrijd | Jaar | Optreden | totaal_score_lied | Rank | Tie | last_score |
+--------------+------+----------+-------------------+------+-----+------------+
| voorselectie | 2014 | 12 | 792 | 1 | 0 | 792 |
| voorselectie | 2014 | 16 | 82 | 2 | 0 | 82 |
| voorselectie | 2014 | 10 | 73 | 3 | 0 | 73 |
| voorselectie | 2014 | 15 | 51 | 4 | 0 | 51 |
| voorselectie | 2014 | 3 | 50 | 5 | 0 | 50 |
| voorselectie | 2014 | 11 | 42 | 6 | 0 | 42 |
| voorselectie | 2014 | 13 | 38 | 7 | 0 | 38 |
| voorselectie | 2014 | 8 | 38 | 7 | 1 | 38 |
| voorselectie | 2014 | 14 | 37 | 9 | 0 | 37 |
| voorselectie | 2014 | 5 | 35 | 10 | 0 | 35 |
| voorselectie | 2014 | 4 | 33 | 11 | 0 | 33 |
| voorselectie | 2014 | 7 | 25 | 12 | 0 | 25 |
| voorselectie | 2014 | 9 | 23 | 13 | 0 | 23 |
+--------------+------+----------+-------------------+------+-----+------------+
I have 2 tables which I need to join and sum the qty based on a requirement. Here's the tables structure.
Customer
---------------------------------------------------
ID | Name | Tel | Sex
---------------------------------------------------
1 | John | 123 XXXX | M
2 | Peter | 456 XXXX | M
3 | Alice | 789 XXXX | F
4 | Amy | 147 XXXX | F
Transaction
---------------------------------------------------
ID | CustID | TranID | Books | Pens | Rulers
---------------------------------------------------
1 | 1 | Jan | 1 | 1 | 0
2 | 1 | Feb | 1 | 0 | 0
3 | 2 | Jan | 1 | 0 | 1
4 | 2 | Jan | 1 | 0 | 0
5 | 3 | Feb | 0 | 1 | 1
6 | 4 | Feb | 1 | 1 | 0
7 | 3 | Feb | 1 | 1 | 0
8 | 4 | Feb | 0 | 0 | 1
9 | 3 | Jan | 1 | 0 | 0
10 | 2 | Jan | 1 | 1 | 0
Required Results (Sex=F, TranID=Jan, Sum:Books, Pens & Rulers)
--------------------------------------------------------------
Name | Tel | Sex | B.TOT | P.TOT | R.TOT
--------------------------------------------------------------
Alice | 789 XXXX | F | 1 | 0 | 0
Amy | 147 XXXX | F | 0 | 0 | 0
I've tried with the following SQL statement and it is working as long as the Transaction Table is NOT EMPTY.
select
`customer`.name,
`customer`.tel,
`customer`.sex,
sum(if(`transaction`.TranID = 'JAN',books,0)) as B.Tot,
sum(if(`transaction`.TranID = 'JAN',pens,0)) as P.Tot,
sum(if(`transaction`.TranID = 'JAN',rulers,0)) as R.tot,
from
`customer`
left join
`transaction`
on
`customer`.id = `transaction`.custid
where
`customer`.sex = 'F'
Group by
`customer`.id,
order by
`customer`.name ASC
How do I modify the above to show the Customer List where SEX='F' even the transaction table is totally empty?
SQL QUERY
I hope this query will resolve your problem :
SELECT
c.name,
c.tel,
c.sex,
IFNULL(SUM(t.books), 0) 'B.TOT',
IFNULL(SUM(t.pens), 0) 'P.TOT',
IFNULL(SUM(t.rulers), 0) 'R.TOT'
FROM
customer c
LEFT JOIN
`transaction` t ON t.custid = c.id AND t.TranID = 'Jan'
WHERE
c.sex = 'F'
GROUP BY c.id
ORDER BY c.name;
I am using the query to find the rank of a user in a given test id, and this gives me rank for just one test at a time and I have to use foreach to get the test rank of all the test.
SELECT * , t.UserRank
FROM (
SELECT * , (
#rownum := #rownum +1
)UserRank
FROM User_Date_Table, (
SELECT #rownum :=0
)t
WHERE my_test_id = '$test_id'
ORDER BY test_score DESC
)t
WHERE user_id = '$my_user_id'
Can a query be generated which can give me user rank for all the test taken by user in single query as this will reduced the db hitting multiple times.
I have to find the all the ranks of user with Test_Type_Id = $my_test_type_id(say), joining a.id with b.my_test_id for a user with user_id = $my_user_id(say)
TABLE STRUCTURE
My_Test_Table (a)
id | name | Test_Type_Id
----------------------------------------------
1 | name_1 | 1
2 | name_2 | 1
3 | name_3 | 2
4 | name_4 | 1
5 | name_5 | 1
6 | name_6 | 2
7 | name_7 | 1
8 | name_8 | 2
9 | name_9 | 1
User_Date_Table (b)
id | my_test_id | user_id | test_score
---------------------------------------------------------
1 | 1 | 32 | 34
2 | 1 | 2 | 345
3 | 2 | 4 | 654
4 | 1 | 76 | 87
5 | 3 | 23 | 453
6 | 2 | 5 | 45
7 | 1 | 43 | 22
8 | 2 | 7 | 987
9 | 2 | 32 | 45
10 | 1 | 1 | 12
11 | 1 | 9 | 35
12 | 3 | 67 | 765
13 | 1 | 88 | 23
14 | 2 | 34 | 76
15 | 3 | 1 | 765
16 | 2 | 54 | 45
17 | 1 | 10 | 87
18 | 1 | 23 | 3
19 | 3 | 44 | 345
20 | 1 | 55 | 232
21 | 2 | 28 | 234
22 | 3 | 32 | 231
Simplest is probably to just remove the user variable for ranking and just do it using a subquery;
SELECT *, (SELECT COUNT(*)+1
FROM User_Date_Table b
WHERE a.my_test_id = b.my_test_id
AND a.test_score < b.test_score) userrank
FROM User_Date_Table a
WHERE user_id = '1';
An SQLfiddle to test with.
SELECT * , t.UserRank
FROM (
SELECT * , #rownum :=if(#user!=user_id,0, #rownum +1),#user:=user_id UserRank
FROM User_Date_Table , (SELECT #rownum :=0,#user :=-1) t
ORDER BY my_test_id ,test_score DESC
)t
WHERE user_id = '$my_user_id'
We introducean additional variable #user to reset #rownum on user_id change
I have this table similar to MySQL - get sum() grouped max() of group
id | person | score | date | g | b | m |
-----------------------------------------------------------
1 | 32 | 444 | 2011-05 | 0 | 1 | o |
2 | 65 | 528 | 2011-05 | 1 | 0 | 1 |
3 | 77 | 455 | 2011-05 | 0 | 0 | 1 |
4 | 32 | 266 | 2011-05 | 1 | 1 | 0 |
5 | 77 | 100 | 2011-05 | 0 | 0 | 1 |
6 | 77 | 457 | 2011-05 | 1 | 0 | 0 |
7 | 77 | 457 | 2011-05 | 1 | 0 | 1 |
8 | 65 | 999 | 2011-05 | 0 | 0 | 1 |
9 | 32 | 222 | 2011-05 | 0 | 1 | 1 |
The result should be:
person | score | date | g | b | m |
-----------------------------------------------------
32 | 932 | 2012-05 | 1 | 3 | 1 |
66 | 1527 | 2012-05 | 1 | 0 | 2 |
77 | 1469 | 2012-05 | 2 | 0 | 3 |
But I couldn't achieve what I want to. I am trying to get for each person sum of its scores, g, m and b fields in each month. I'm very newbie about subqueries.
Thank you!
You can't just group by month, since the same month can appear in more than one year, so group by year as well, e.g.:
SELECT person, YEAR(`date`), MONTH(`date`),
SUM(score), SUM(b), SUM(g), SUM(m)
FROM MyTable
GROUP BY person, YEAR(`date`), MONTH(`date`)
If your date column is actually not a date type but a varchar or similar, and it contains yyyy-dd as shown, than you can just do:
SELECT person, `date`,
SUM(score), SUM(b), SUM(g), SUM(m)
FROM MyTable
GROUP BY person, `date`