Multiple INNER JOIN subqueries sql - mysql

I have the below query which comes from this post count number of items in a row in mysql which counts how many times in a row a student has been present/absent from a class.
SELECT
classlist.studentid,
student.name,
classStatus.name status,
COUNT(*) presentcnt
FROM
classlist
INNER JOIN student ON classlist.studentid=student.id
INNER JOIN classstatus ON classlist.presentid=classstatus.id
INNER JOIN (
SELECT
studentid,
max(CASE WHEN presentid=0 THEN id END) max_0,
max(CASE WHEN presentid=1 THEN id END) max_1
FROM classlist
GROUP BY studentid
) s
ON coalesce(classlist.id>least(max_0,max_1) AND classlist.id<=greatest(max_0,max_1),1) AND s.studentid=classlist.studentid
GROUP BY classlist.studentid
This works as expected,
STUDENTID NAME STATUS PRESENTCNT
111 John Present 1
222 Kate Absent 2
333 Matt Present 5
I want to extend the query so that I have a column showing if the student particpated in the class.
If I run an independent query I get the results I want
SELECT
classlist.studentid,
student.name,
participatedStatus.name status,
COUNT(*) participatedcnt
FROM
classlist
INNER JOIN student ON classlist.studentid=student.id
INNER JOIN participatedStatus ON classlist.participatedid=participatedStatus.id
INNER JOIN (
SELECT
studentid,
max(CASE WHEN participatedid=0 THEN id END) max_0,
max(CASE WHEN participatedid=1 THEN id END) max_1
FROM classlist
group by studentid
) s
ON coalesce(classlist.id>least(max_0,max_1)
AND classlist.id<=greatest(max_0,max_1),1)
AND s.studentid=classlist.studentid
group by classlist.studentid
STUDENTID NAME STATUS PARTICIPATEDCNT
111 John Yes 1
222 Kate No 2
333 Matt Yes 2
However I want to merge them into the one query so I get
STUDENTID NAME STATUS PRESENTCNT STATUS2 PARTICIPATEDCNT
111 John Present 1 Yes 1
222 Kate Absent 2 No 2
333 Matt Present 5 Yes 2
I am confused about how this can be achieved as I am selecting count *, how can I acheive this?
A sample of the data I am using is in this fiddle and below
CREATE TABLE classlist
(`id` int, `studentid` int, `subjectid` int, `presentid` int, `participatedid` int);
CREATE TABLE student
(`id` int, `name` varchar(4));
CREATE TABLE subject
(`id` int, `name` varchar(4));
CREATE TABLE classStatus
(`id` int, `name` varchar(8));
CREATE TABLE participatedStatus
(`id` int, `name` varchar(8));
INSERT INTO classlist (`id`, `studentid`, `subjectid`, `presentid`, `participatedid`)
VALUES (1, 111, 1, 1, 0), (2, 222, 3, 0, 0), (3, 333, 2, 1, 0), (4, 111, 4, 0, 0), (5, 111, 1, 1, 0), (6, 222, 3, 0, 0), (7, 333, 2, 1, 1), (8, 111, 4, 0, 0), (9, 111, 4, 0, 0), (10, 111, 4, 0, 0), (11, 111, 1, 1, 1), (12, 333, 3, 1, 0), (13, 333, 2, 1, 1), (14, 333, 3, 1, 1);
INSERT INTO student (`id`, `name`)
VALUES (111, 'John'),(222, 'Kate'),(333, 'Matt');
INSERT INTO subject (`id`, `name`)
VALUES (1, 'MATH'),(2, 'ENG'),(3, 'SCI'),(4, 'GEO');
INSERT INTO classStatus (`id`, `name`)
VALUES (0, 'Absent'), (1, 'Present');
INSERT INTO participatedStatus (`id`, `name`)
VALUES (0, 'No'),(1, 'Yes');

SELECT
studid,
studname,
status,
presentcnt,
status1,
participatedcnt FROM
(SELECT
classlist.studentid studid,
student.name studname,
classStatus.name status,
COUNT(*) presentcnt
FROM
classlist
INNER JOIN student ON classlist.studentid=student.id
INNER JOIN classstatus ON classlist.presentid=classstatus.id
INNER JOIN (
SELECT
studentid,
max(CASE WHEN presentid=0 THEN id END) max_0,
max(CASE WHEN presentid=1 THEN id END) max_1
FROM classlist
GROUP BY studentid
) s
ON coalesce(classlist.id>least(max_0,max_1) AND classlist.id<=greatest(max_0,max_1),1) AND s.studentid=classlist.studentid
GROUP BY classlist.studentid)x
JOIN
(SELECT
classlist.studentid,
student.name,
participatedStatus.name status1,
COUNT(*) participatedcnt
FROM
classlist
INNER JOIN student ON classlist.studentid=student.id
INNER JOIN participatedStatus ON classlist.participatedid=participatedStatus.id
INNER JOIN (
SELECT
studentid,
max(CASE WHEN participatedid=0 THEN id END) max_0,
max(CASE WHEN participatedid=1 THEN id END) max_1
FROM classlist
group by studentid
) s
ON coalesce(classlist.id>least(max_0,max_1)
AND classlist.id<=greatest(max_0,max_1),1)
AND s.studentid=classlist.studentid
group by classlist.studentid)y
ON x.studid=y.studentid
Fiddle

Related

How can I rewrite this to remove select statement joins?

As it is now each time I am selecting from Bill in my actual database I am searching over 1 million rows. There has to be a better way to write this query, but I haven't been able to figure it out. There has to be some way I can write this that doesn't join using the select statements.
If you notice I have some form of this join three seperate times. What should I do to remove this join?
join
(
select b.year
, sum(bli.amount) amt
from Bill b
join BillLine bli
on bli.bill_id = b.id
where bli.payment = 0
and bli.refund = 0
and bli.type = 3
group
by b.year
) org
on org.year = b.year
select b.Year,
Round(case when b.year = (select collectoryear -1 from Entity) then 0 else beg.amt end,2) Beginning,
Round(case when b.year >= (select collectoryear -1 from Entity) then ifnull(org.amt,0) else 0 end,2) Additions,
Round(case when b.year = (select collectoryear -1 from Entity) then 0 else beg.amt end + case when b.year >= (select collectoryear -1 from Entity) then ifnull(org.amt,0) else 0 end - ending.amt ,2) Collections,
Round(ending.amt,2) Ending
from Bill b
left join Levy l on l.year = b.year
join Entity e on b.year = e.year
join( select b.year, sum(bli.amount) amt from Bill b join BillLine bli on bli.bill_id = b.id where bli.payment = 0 and bli.refund = 0 and bli.type = 3 group by b.year) org on org.year = b.year
join( select b.year, sum(bli.amount) amt from Bill b join BillLine bli on bli.bill_id = b.id where bli.type = 2 group by b.year ) beg on beg.year = b.year
join( select b.year, sum(bli.amount) amt from Bill b join BillLine bli on bli.bill_id = b.id where bli.type = 2 group by b.year ) ending on ending.year = b.year
where b.year > (select collectoryear -11 from Entity)
and b.year < (select collectoryear from Entity)
group by b.year
order by Year desc ;
Here is the db-fiddle databases
create table Bill
( id varchar(20) PRIMARY KEY
, year varchar(20) FOREIGN KEY
);
Insert into Bill (id, year) values
(1, 2020),
(2, 2020),
(3, 2020),
(4, 2019),
(5, 2019),
(6, 2018),
(7, 2017),
(8, 2016),
(9, 2015),
(10, 2013);
create table BillLine
( id varchar(20) PRIMARY KEY
, bill_id varchar(20) FOREIGN KEY
, amount varchar(20)
, refund varchar(20)
, payment varchar(20)
, type varchar(20)
);
Insert into BillLine (id, bill_id, amount, refund, payment, type) values
(1, 10, 100.00, 0, 0, 3),
(2, 9, 250.00, 1, 1, 5),
(3, 8, 102.00, 0, 0, 3),
(4, 7, 85.00, 1, 1, 5),
(5, 6, 20.00, 0, 0, 3),
(6, 5, 43.75, 0, 1, 2),
(7, 4, 22.22, 0, 0, 3),
(8, 3, 125.25, 0, 1, 2),
(9, 2, 77.70, 0, 0, 3),
(10, 1, 100.75, 1, 1, 5);
create table Entity
( id varchar(20) PRIMARY KEY
, collectoryear varchar(20)
, year varchar(20)
);
Insert into Entity (id, collectoryear, year) values (1, 2021, 2020);
create table Levy
( id varchar(20) PRIMARY KEY, county varchar(20), year varchar(20)
);
Insert into Levy (id, county, year) values (1, null, null);

Get summary grouped by category with three tables on mysql

category
---------------------------
id_category primary key
category
id_user foreign key
counterpart
---------------------------
id_counterpart primary key
counterpart
id_category foreign key
id_user foreign key
transaction
---------------------------
transaction primary key
date
id_counterpart foreign key
amount
id_card foreign key
id_user foreign key
Hello,
I have thoses table on mysql database and i want to have summary of each category (with 0 if there is any transaction) by month and year based on id_user.
I tried this command to have grouped by counterpart and it works but cannot reach when i add category and group by id_category.
select counterpart, s2.total from counterpart as s1
left join (select coalesce(sum(amount),0) as total, id_counterpart from transaction where year(date) = 2019 and month(date) = 7 and id_user = 2 group by id_counterpart) as s2
on s1.id_counterpart = s2.id_counterpart
left join category on s1.id_category = category.id_category
group by counterpart;
Do you have any idea to do that ? Else, i will do with php.
Thank you.
Edit : Add example
INSERT INTO `category` (`id_category`, `category`, `id_user`) VALUES
(1, 'cat_a', 1),
(2, 'cat_b', 1),
(3, 'cat_c', 1);
INSERT INTO `counterpart` (`id_counterpart`, `counterpart`, `id_category`, `id_user`) VALUES
(1, 'cp_a', 1, 1),
(2, 'cp_b', 2, 1),
(3, 'cp_c', 2, 1);
INSERT INTO `transaction` (`id_transaction`, `date`, `id_counterpart`, `amount`, `id_card`, `id_user`) VALUES
(1, '2019-07-01 00:00:00', 1, 400.00, 2, 1),
(2, '2019-07-01 00:00:00', 1, -24.95, 2, 1),
(3, '2019-07-31 00:00:00', 2, -20.04, 2, 1);
(4, '2019-07-30 00:00:00', 2, -1.00, 2, 1);
(5, '2019-07-29 00:00:00', 3, -2.00, 2, 1);
(6, '2019-07-28 00:00:00', 1, -3.00, 2, 1);
(7, '2019-07-27 00:00:00', 3, 2.00, 2, 1);
(8, '2019-07-26 00:00:00', 2, 5.00, 2, 1);
On july 2019 i want to have this, for user 1 :
cat_a 372.05
cat_b 16.04
cat_c 0.00
Join the tables and then group by category:
select c.category, coalesce(sum(t.amount), 0) total
from category c
left join counterpart as cp
on c.id_category = cp.id_category and c.id_user = cp.id_user
left join transaction t
on t.id_counterpart = cp.id_counterpart and t.id_user = cp.id_user and year(t.date) = 2019 and month(t.date) = 7 and t.id_user = 1
group by c.id_category, c.category
See the demo.
Results:
| category | total |
| -------- | ------ |
| cat_a | 372.05 |
| cat_b | -16.04 |
| cat_c | 0 |

mySQL - SUM and COUNT and JOIN to display all records [duplicate]

This question already has answers here:
How can I do a FULL OUTER JOIN in MySQL?
(15 answers)
Closed 4 years ago.
This is a following question to this one Join two tables with SUM and COUNT.
What I try to do is to have all values displayed as some are in history table and not in rota table or vice-versa (999 and 777)
So my tables are:
create table history (
code int(10) primary key,
PN varchar(10) not null,
Qty int(10) not null,
LOC_ID int(10));
insert into history values (1, 'T1', 1, 1);
insert into history values (2, 'A1', 2,2);
insert into history values (3, 'J1', 3,3);
insert into history values (4, 'A2', 1,4);
insert into history values (5, 'J2', 2,1);
insert into history values (6, 'A3', 3,2);
insert into history values (7, 'J3', 4,3);
insert into history values (8, 'T1', 5,4);
insert into history values (9, 'A1', 1,1);
insert into history values (10, '999', 3,2);
insert into history values (11, 'J2', 4,3);
insert into history values (12, 'A1', 3,4);
insert into history values (13, 'J2', 5,1);
create table rota (
code int(10) primary key,
PN varchar(10) not null,
SN varchar(10) not null,
LOC_ID int(10));
insert into rota values (1, 'T1', 't1a',1);
insert into rota values (2, 'A1', 'a1a',2);
insert into rota values (3, 'J1', 'j1a',3);
insert into rota values (4, 'A2', 'a2a',4);
insert into rota values (5, 'J2', 'j2a',1);
insert into rota values (6, 'A3', 'a3a',2);
insert into rota values (7, 'J3', 'j3a',3);
insert into rota values (8, '777', 't1b',4);
insert into rota values (9, 'A1', 'a1b',1);
insert into rota values (10, 'J2', 'j2b',2);
insert into rota values (11, 'J2', 'j2c',3);
insert into rota values (12, 'A1', 'a1c',4);
insert into rota values (13, 'J2', 'j2d',1);
insert into rota values (14, 'J2', 'j2e',2);
insert into rota values (15, 'J2', 'j2f',3);
create table loca (
code1 int(10) primary key,
LOC varchar(10) not null);
insert into loca values (1, 'AAA');
insert into loca values (2, 'BBB');
insert into loca values (3, 'CCC');
insert into loca values (4, 'DDD');
The code I have got is
select CASE WHEN a.pn IS NULL THEN b.pn ELSE a.pn END AS PN
, a.q
, b.c
, a.LOC_ID
, b.LOC_ID
from
(select
h.pn
, sum(qty) q
, h.LOC_ID
from
history h
group by h.pn, h.LOC_ID) a
RIGHT JOIN
(select
r.pn
, count(sn) c
, r.LOC_ID
from
rota r
group by r.pn, r.LOC_ID) b
on a.pn = b.pn WHERE a.LOC_ID = b.LOC_ID
order by a.pn;
The above code works great for all PN that are in both tables. The problem is for values that are specific to one of the tables. I can remove the WHERE clause from JOIN but it is not corect. The question is - how to get all PNs from history and rota where some of them are present i just one table. I had some luck with RIGHT JOIN but that did not cover unique values from the other table. Any one came across solution before?
Results shoud look like the following table
PN LOC_ID Count Qty
T1 1 1 1
A1 2 1 2
J1 3 1 3
A2 4 1 1
J2 1 2 2
A3 2 1 3
J3 3 1 4
777 4 1 NULL
A1 1 1 1
J2 2 2 NULL
J2 3 2 4
A1 4 1 3
J2 1 2 2
J2 2 2 NULL
J2 3 2 4
999 2 NULL 3
use another join and that is left and make them union
select t.PN,t.q,t.c,t.LOC_ID,t.LOC_ID_b from
(
select CASE WHEN a.pn IS NULL THEN b.pn ELSE a.pn END AS PN
, a.q
, b.c
, a.LOC_ID
, b.LOC_ID as LOC_ID_b
from
(select
h.pn
, sum(qty) q
, h.LOC_ID
from
history h
group by h.pn, h.LOC_ID) a
RIGHT JOIN
(select
r.pn
, count(sn) c
, r.LOC_ID
from
rota r
group by r.pn, r.LOC_ID) b
on a.pn = b.pn and a.LOC_ID = b.LOC_ID
) as t
union
select t2.PN,t2.q,t2.c,t2.LOC_ID,t2.LOC_ID_b from
(
select CASE WHEN a.pn IS NULL THEN b.pn ELSE a.pn END AS PN
, a.q
, b.c
, a.LOC_ID
, b.LOC_ID as LOC_ID_b
from
(select
h.pn
, sum(qty) q
, h.LOC_ID
from
history h
group by h.pn, h.LOC_ID) a
left JOIN
(select
r.pn
, count(sn) c
, r.LOC_ID
from
rota r
group by r.pn, r.LOC_ID
) b
on a.pn = b.pn and a.LOC_ID = b.LOC_ID
) t2
http://sqlfiddle.com/#!9/c20c81/20

SELECT data based on result of previous row in table

I have a database of students.
CREATE TABLE classlist
(`id` int, `studentid` int, `subjectid` int, `presentid` int)
;
CREATE TABLE student
(`id` int, `name` varchar(4))
;
CREATE TABLE subject
(`id` int, `name` varchar(4))
;
CREATE TABLE classStatus
(`id` int, `name` varchar(8))
;
INSERT INTO classlist
(`id`, `studentid`, `subjectid`, `presentid`)
VALUES
(1, 111, 1, 1),
(2, 222, 3, 0),
(3, 333, 2, 1),
(4, 111, 4, 1),
(5, 111, 1, 0),
(6, 222, 3, 0),
(7, 333, 2, 1),
(8, 111, 4, 1),
(9, 111, 2, 0),
(10, 111, 4, 1),
(11, 111, 1, 1),
(12, 333, 3, 1),
(13, 333, 2, 1),
(14, 333, 3, 1)
;
INSERT INTO student
(`id`, `name`)
VALUES
(111, 'John'),
(222, 'Kate'),
(333, 'Matt')
;
INSERT INTO subject
(`id`, `name`)
VALUES
(1, 'MATH'),
(2, 'ENG'),
(3, 'SCI'),
(4, 'GEO')
;
INSERT INTO classStatus
(`id`, `name`)
VALUES
(0, 'Absent'),
(1, 'Present')
;
And I have a query which shows how many times they have been present or absent.
SELECT
studentid,
students.name AS NAME,
SUM(presentid = 1) AS present,
SUM(presentid = 0) AS absent
FROM classlist
INNER JOIN student as students ON classlist.studentid=students.id
GROUP BY studentid, NAME
See this fiddle below.
http://sqlfiddle.com/#!2/fe0b0/1
There seems to be a trend from looking at this sample data that after someone attends subjectid 4 they are often not coming to the next class. How can I capture this in a query. I want to ONLY show data WHERE last subjectid =4. So in my sample data rows matching my criteria would be.
(5, 111, 1, 0),
(9, 111, 2, 0),
(11, 111, 1, 1),
as these rows are all the next row of a studentid who had a subjectid=4.
My output would be
| STUDENTID | NAME | PRESENT | ABSENT|
| 111 | John | 1 | 2 |
To get the next class for a student, use a correlated subquery:
select cl.*,
(select min(cl2.id) from classlist cl2 where cl2.studentid = cl.studentid and cl2.id > cl.id) as nextcl
from classlist cl
Plugging this into your query example tell you you who is present and absent for the next class:
SELECT students.id, students.name AS NAME,
SUM(cl.presentid = 1) AS present, SUM(cl.presentid = 0) AS absent,
sum(clnext.presentid = 1) as presentnext
FROM (select cl.*,
(select min(cl2.id) from classlist cl2 where cl2.studentid = cl.studentid and cl2.id > cl.id) as nextcl
from classlist cl
) cl INNER JOIN
student as students
ON cl.studentid = students.id left outer join
classlist clnext
on cl.nextcl = clnext.id
GROUP BY students.id, students.NAME
Add a where cl.subjectid = 4 to get the answer for subject 4.
I fixed the query. The SQLFiddle is k.
A quick and dirty solution could be to get the Classlist.Id for all lines where subjectid=4 (let's call them n) then select all the lines where Id = n+1

count number of items in a row in mysql

I have a list of students that shows whether they were present or absent from a particular class.
CREATE TABLE classlist
(`id` int, `studentid` int, `subjectid` int, `presentid` int)
;
CREATE TABLE student
(`id` int, `name` varchar(4))
;
CREATE TABLE subject
(`id` int, `name` varchar(4))
;
CREATE TABLE classStatus
(`id` int, `name` varchar(8))
;
INSERT INTO classlist
(`id`, `studentid`, `subjectid`, `presentid`)
VALUES
(1, 111, 1, 1),
(2, 222, 3, 0),
(3, 333, 2, 1),
(4, 111, 4, 0),
(5, 111, 1, 1),
(6, 222, 3, 0),
(7, 333, 2, 1),
(8, 111, 4, 0),
(9, 111, 4, 0),
(10, 111, 4, 0),
(11, 111, 1, 1),
(12, 333, 3, 1),
(13, 333, 2, 1),
(14, 333, 3, 1)
;
INSERT INTO student
(`id`, `name`)
VALUES
(111, 'John'),
(222, 'Kate'),
(333, 'Matt')
;
INSERT INTO subject
(`id`, `name`)
VALUES
(1, 'MATH'),
(2, 'ENG'),
(3, 'SCI'),
(4, 'GEO')
;
INSERT INTO classStatus
(`id`, `name`)
VALUES
(0, 'Absent'),
(1, 'Present')
;
See Fiddle http://sqlfiddle.com/#!2/a2d93/5
I can count who is absent and present overall with something like the below.
SELECT
studentid,
students.name AS NAME,
SUM(presentid = 1) AS present,
SUM(presentid = 0) AS absent
FROM classlist
INNER JOIN student as students ON classlist.studentid=students.id
GROUP BY studentid, NAME
But I want to know how many classes in a row a student has attended/missed, so that the teachers can easily see if someone has missed a lot of time or someone is rewarded for good attendance etc. I have seen some posts which talk about streaks but none of them seem to match the way the data has been presented to me so I am not sure how to acheive this?
Just to be clear based on my sample data below the out put for the streaks should be.
(1, 111, 1, 1), /* John Present 1 times in a row */
(2, 222, 3, 0), /* Kate Absent 1 times in a row */
(3, 333, 2, 1), /* Matt Present 1 times in a row */
(4, 111, 4, 0), /* John Absent 1 times in a row */
(5, 111, 1, 1), /* John Present 1 times in a row */
(6, 222, 3, 0), /* Kate Absent 2 times in a row */
(7, 333, 2, 1), /* Matt Present 2 times in a row */
(8, 111, 4, 0), /* John Absent 1 times in a row */
(9, 111, 4, 0), /* John Absent 2 times in a row */
(10, 111, 4, 0), /* John Absent 2 times in a row */
(11, 111, 1, 1), /* John Present 1 times in a row */
(12, 333, 3, 1), /* Matt Present 3 times in a row */
(13, 333, 2, 1), /* Matt Present 4 times in a row */
(14, 333, 3, 1) /* Matt Present 5 times in a row */
/*Showing the latest status for each user*/
/* John Present 1 times in a row */
/* Kate Absent 2 times in a row */
/* Matt Present 5 times in a row */
John present 1.
Kate absent 2.
Matt Present 5.
This should give the count of the consecutive rows with the same value as the last row:
select
classlist.studentid,
student.name,
classStatus.name status,
count(*) presentcnt
from
classlist inner join student
on classlist.studentid=student.id
inner join classstatus
on classlist.presentid=classstatus.id
inner join (
select
studentid,
max(case when presentid=0 then id end) max_0,
max(case when presentid=1 then id end) max_1
from classlist
group by studentid
) s
on coalesce(classlist.id>least(max_0,max_1) and
classlist.id<=greatest(max_0,max_1),1)
and s.studentid=classlist.studentid
group by classlist.studentid
On the subquery I'm extracting the max id where presentid = 0 and the max id where presentid = 1.
On the outer query I'm extracting and counting all rows where id is > than the least of max_0 and max_1, and <= than the greatest of those two. No matter what the last value is, these are all the rows with the same value as the last one.
If either max_0 or max_1 is null, it means that all rows have just one value, 1 or 0, and we have to get all of them. If one of max_0 or max_1 is null, the whole conditions will be null as well. Using Coalesce( condition, 1) I'm returning all rows in that case.
Notice that I am grouping by classlist.studentid and showing some nonaggregated columns, but this is a situation in which it is allowed since all nonaggregated columns have the same value.
Do you want this?:
SELECT
studentid,
name,
SUM(present = 1) AS present,
SUM(present = 0) AS absent,
count(subject) as classTimes,
subject
FROM your_table GROUP BY studentid, name,subject
If id from classlist can be used for ordering then you will get your expected result with
SELECT
s.name,
CASE t1.presentid
WHEN 0 THEN 'absent'
WHEN 1 THEN 'present'
END state,
t1.pc
FROM (
SELECT
c.id,
c.studentid,
c.presentid,
#pcount := IF( #pid = presentid AND #sid = studentid, #pcount + 1, 1 ) as pc,
#pid := presentid,
#sid := studentid
FROM
classlist c
ORDER BY
studentid, id ) t1
JOIN student s
ON t1.studentid = s.id
WHERE
( t1.studentid, t1.id ) IN ( SELECT
studentid,
MAX( id )
FROM classlist
GROUP BY studentid );
SQL Fiddle DEMO
Try this:
SELECT A.studentid, s.name, IF(presentid = 1, 'present', 'absent') STATUS,
ABS(SUM(IF(presentid = 1, 1, -1))) AS presentcnt
FROM classlist A
INNER JOIN student s ON A.studentid = s.id
LEFT JOIN (SELECT MAX(id) id, studentid
FROM classlist GROUP BY studentid
) B ON A.studentid = B.studentid AND A.id = B.id
GROUP BY A.studentid
Check this SQL FIDDLE DEMO
OUTPUT
| STUDENTID | NAME | STATUS | PRESENTCNT |
-------------------------------------------
| 111 | John | present | 1 |
| 222 | Kate | absent | 2 |
| 333 | Matt | present | 5 |
I'm not sure what point about your question.
If this you want
John present 1.
Kate absent 2.
Matt Present 5.
U may try this:
SELECT
studentid,
students.name AS NAME,
SUM(presentid = 1) AS present,
SUM(presentid = 0) AS absent,
IF( SUM(presentid = 1)-SUM(presentid = 0)>=0,SUM(presentid = 1)-SUM(presentid = 0),SUM(presentid = 0)-SUM(presentid = 1)) AS aliase
FROM classlist
INNER JOIN student as students ON classlist.studentid=students.id
GROUP BY studentid, NAME