Mysql get total of matched items from two tables - mysql

I have two tables as given below...
INSERT INTO `tbl_project_skills` (`id`, `projectId`, `skillId`) VALUES
(17, 2, 44),
(27, 2, 43),
(31, 2, 78),
(33, 142, 79),
(35, 123, 88);
INSERT INTO `tbl_user_skills` (`id`, `userId`, `skillId`) VALUES
(2, 1, 44),
(3, 1, 78),
(5, 23, 45),
(6, 1, 36),
(7, 23, 88);
I want to find that how many skills are matched of user with each project like below
-----------------------------------------------------
userId | projectId | number_of_matched_skills
-----------------------------------------------------
1 | 2 | 2
1 | 142 | 0
1 | 123 | 0
23 | 2 | 0
23 | 142 | 0
23 | 123 | 1
------------------------------------------------------
I've tried below solution for the same...
SELECT ps.projectId, us.userId, ps.skillId AS pskillid, us.skillId AS uskillid, #points_veri:= (CASE WHEN '44' IN(ps.skillId) THEN 1 ELSE 0 END) + (CASE WHEN '43' IN(ps.skillId) THEN 1 ELSE 0 END) + (CASE WHEN '78' IN(ps.skillId) THEN 1 ELSE 0 END) AS number_of_matched_skills FROM tbl_user_skills AS us, tbl_project_skills AS ps
But I can't get it right. Hope you guys can give a hint.

I think you may have another table for store user and project, or this query will need get userId from tbl_user_skills and projectId from tbl_project_skills first, then do cross join, then join tbl_user_skills and tbl_project_skills to do aggregation:
select
main.userId,
main.projectId,
count(ps.projectId) as number_of_matched_skills
from (
select u.userId, p.projectId
from (
select distinct projectId from tbl_project_skills
) p
cross join (
select distinct userId from tbl_user_skills
) u
) main
left join tbl_user_skills us on us.userId = main.userId
left join tbl_project_skills ps on ps.skillId = us.skillId and main.projectId = ps.projectId
group by main.userId, main.projectId
Here is a demo in rextester.com.

Another option without having another table. Make an OUTER JOIN and then a GROUP BY on skillId.
SELECT userid,
projectid,
SUM(CASE WHEN p.skillId>1 AND u.skillId>1 THEN 1 ELSE 0 END) AS skills
FROM tbl_user_skills u
FULL OUTER JOIN tbl_project_skills p
ON u.skillId = p.skillId
GROUP BY userid, projectid
ORDER BY skills DESC

Related

Top sales making product for each year

I have two tables with following schema.
CREATE TABLE Product (
id INT AUTO_INCREMENT,
name VARCHAR(64) UNIQUE,
unit_price VARCHAR(6),
PRIMARY KEY (id)
);
CREATE TABLE Sale (
id INT AUTO_INCREMENT,
product_id INT, created_at DATETIME,
units INT, PRIMARY KEY (id),
FOREIGN KEY (product_id) REFERENCES Product(id)
);
INSERT INTO Product(name, unit_price)
VALUES
('Methadone', 30),
('Codeine', 20),
('Morphine', 40),
('Fentanyl', 10);
INSERT INTO Sale(product_id, created_at, units)
VALUES
(1, '2004-01-06 08:27:25', 8),
(4, '2004-03-05 05:21:15', 24),
(2, '2005-01-06 08:26:55', 3),
(4, '2005-03-31 01:55:25', 6),
(1, '2005-11-09 11:33:20', 2),
(4, '2006-02-15 10:46:30', 4),
(3, '2006-04-14 07:40:50', 1),
(4, '2008-01-06 08:27:25', 5),
(4, '2008-01-10 21:31:45', 3),
(1, '2008-01-28 16:07:30', 9),
(3, '2008-03-05 05:21:00', 4),
(2, '2008-03-08 18:25:50', 5),
(4, '2008-05-02 02:15:20', 1),
(4, '2008-05-05 15:19:40', 3),
(4, '2008-06-28 23:08:55', 2),
(4, '2008-07-02 12:14:00', 2),
(3, '2008-08-29 09:07:50', 1),
(2, '2008-10-22 16:56:20', 5),
(3, '2008-10-26 06:01:25', 2),
(4, '2008-12-19 13:49:55', 4),
(2, '2008-12-23 02:55:30', 4),
(1, '2009-02-15 10:43:45', 4),
(2, '2009-04-14 07:37:50', 6),
(3, '2009-06-11 04:31:25', 3),
(4, '2009-08-08 01:25:30', 12),
(1, '2010-11-09 11:33:20', 8),
(1, '2011-01-14 10:22:35', 3),
(3, '2011-03-13 07:16:10', 3),
(4, '2011-05-10 04:10:15', 4),
(4, '2011-07-07 01:04:35', 4),
(4, '2011-09-02 21:58:10', 3),
(2, '2011-10-30 18:51:45', 3),
(1, '2011-12-27 15:45:20', 1),
(4, '2012-02-23 12:43:25', 2),
(4, '2012-04-21 09:37:30', 3),
(4, '2012-06-18 06:31:20', 1),
(2, '2012-08-15 03:25:40', 4),
(2, '2013-01-14 10:23:20', 4),
(4, '2013-02-01 05:01:35', 1),
(4, '2013-03-13 07:16:55', 1),
(4, '2013-03-31 01:55:10', 2),
(4, '2013-05-10 04:11:15', 2),
(3, '2013-05-27 22:48:45', 3),
(1, '2013-07-07 01:05:20', 3),
(2, '2013-07-24 19:42:20', 4),
(4, '2013-09-02 21:59:40', 4),
(3, '2013-09-20 16:36:10', 1),
(4, '2013-10-30 18:54:00', 2),
(4, '2013-11-17 13:29:45', 1),
(4, '2013-12-27 15:48:20', 3);
I want to find top sales making product for each year sorted by year in descending order along-with the total sale made for the top selling product.
I have written the following query.
SELECT EXTRACT(YEAR FROM S.created_at) as year,
GROUP_CONCAT(DISTINCT P1.name
ORDER BY P1.name
SEPARATOR ',')as top,
MAX(S.units*P1.unit_price) as sale
FROM Sale S
INNER JOIN Product P1 ON S.product_id=P1.id
GROUP BY year
ORDER BY year desc
Expected Output:
2013 Codeine,Fentanyl,Morphine 160
2012 Codeine 80
2011 Methadone,Morphine 120
2010 Methadone 240
2009 Codeine,Fentanyl,Methadone,Morphine 120
2008 Codeine,Morphine 280
2006 Fentanyl,Morphine 40
2005 Codeine,Fentanyl,Methadone 60
2004 Fentanyl,Methadone 240
Original Output:
2013 Codeine,Fentanyl,Methadone,Morphine 120
2012 Codeine,Fentanyl 80
2011 Codeine,Fentanyl,Methadone,Morphine 120
2010 Methadone 240
2009 Codeine,Fentanyl,Methadone,Morphine 120
2008 Codeine,Fentanyl,Methadone,Morphine 270
2006 Fentanyl,Morphine 40
2005 Codeine,Fentanyl,Methadone 60
2004 Fentanyl,Methadone 240
The query was supposed to return only the top selling products, bu it's returning all the products. Could anyone provide any help?
Here is a solution for MySQL < 8.0. First, aggregate sales by year and product and filter on the top selling products with an aggregate, correlated subquery; then, aggregate by year:
select
year_at,
group_concat(name order by name) products,
total_sales
from (
select
year(s.created_at) year_at,
p.name,
sum(s.units * p.unit_price) total_sales
from Product p
inner join Sale s on s.product_id = p.id
group by year_at, p.id, p.name
having total_sales = (
select sum(s1.units * p1.unit_price) total_sales1
from Product p1
inner join Sale s1 on s1.product_id = p1.id
where year(s1.created_at) = year_at
group by s1.product_id
order by total_sales1 desc
limit 1
)
) t
group by year_at, total_sales
order by year_at desc
Demo on DB Fiddlde:
year_at | products | total_sales
------: | :---------------------------------- | ----------:
2013 | Codeine,Fentanyl,Morphine | 160
2012 | Codeine | 80
2011 | Methadone,Morphine | 120
2010 | Methadone | 240
2009 | Codeine,Fentanyl,Methadone,Morphine | 120
2008 | Codeine,Morphine | 280
2006 | Fentanyl,Morphine | 40
2005 | Codeine,Fentanyl,Methadone | 60
2004 | Fentanyl,Methadone | 240
You seem to be after this...
SELECT GROUP_CONCAT(a.name) names,a.total, a.year
FROM
( SELECT p.name
, SUM(p.unit_price*s.units) total
, YEAR(created_at) year
FROM product p
JOIN sale s
ON s.product_id = p.id
GROUP
BY year
, name
) a
JOIN
( SELECT MAX(total) total
, year
FROM
( SELECT p.name
, SUM(p.unit_price*s.units) total
, YEAR(created_at) year
FROM product p
JOIN sale s
ON s.product_id = p.id
GROUP
BY year
, name
) n
GROUP
BY year) b
ON b.year = a.year AND b.total = a.total
GROUP BY a.total, a.year
ORDER BY year DESC;
+-------------------------------------+-------+------+
| names | total | year |
+-------------------------------------+-------+------+
| Fentanyl,Codeine,Morphine | 160 | 2013 |
| Codeine | 80 | 2012 |
| Morphine,Methadone | 120 | 2011 |
| Methadone | 240 | 2010 |
| Morphine,Methadone,Fentanyl,Codeine | 120 | 2009 |
| Morphine,Codeine | 280 | 2008 |
| Morphine,Fentanyl | 40 | 2006 |
| Codeine,Methadone,Fentanyl | 60 | 2005 |
| Methadone,Fentanyl | 240 | 2004 |
+-------------------------------------+-------+------+
Here's a variant of my Scalar-Aggregate Comparison query technique that will achieve what you want in a vastly better-performing query than self-joining the aggregate results. As you can see below, this only ever performs a single join, then 3 cascaded layers of aggregation to achieve the final output.
SELECT
yr,
SUBSTRING(MAX(CONCAT(LPAD(total_sales, 16, '0'), products)), 17) AS best_seller,
MAX(total_sales) AS sales_amount
FROM (
SELECT
yr,
total_sales,
GROUP_CONCAT(name order by name) AS products
FROM (
SELECT
YEAR(s.created_at) AS yr,
p.id,
p.name,
SUM(s.units * p.unit_price) AS total_sales
FROM Product AS p
INNER JOIN Sale AS s on s.product_id = p.id
GROUP BY YEAR(s.created_at), p.id
) AS pys
GROUP BY yr, total_sales
) AS py
GROUP BY yr
ORDER BY yr DESC;
Here's a DB Fiddle demo: https://dbfiddle.uk/?rdbms=mysql_5.6&fiddle=e14ed7f396738ce064f1998bdb4310ac
try this:
Select name, year(created_at), sum(unit_price*units )
from Product p inner join sale s on p.id = s.product_id
group by name, year(created_at)
order by 2, 3 desc
If you want the top selling product for each year, use rank() and an aggregation query:
select ps.*
from (select p.name, year(s.created_at) as year, sum(p.unit_price*s.units) as sales
rank() over (partition by year(s.created_at) order by sum(p.unit_price*s.units) desc as seqnum
from Product p inner join
sale s
on p.id = s.product_id
group by name, year
) ps
where seqnum <= 1; -- You can change "1" for more rows

Group and rank top N rows by category

I have a table with the columns category and votes. I've tried multiple solutions before with very little success; usually what would happen is that instead of returning the top 3 items in each category, it returns all of the items available.
SELECT `id`, `user_id`, `full_name`, `category`, `year`, `month`, `thumbnail_photo`, `photo_title`, `votes`
FROM
(
SELECT `id`, `user_id`, `full_name`, `category`, `year`, `month`, `thumbnail_photo`, `photo_title`, `votes`,
#category_rank := IF(#current_category = category, #category_rank + 1, 1) AS category_rank,
#current_category := category
FROM `photo_contest`
ORDER BY
`category`,
`votes` DESC
) ranked
WHERE
category_rank <= 3
AND `year` = '2017'
AND `month` = 'April'
AND `votes` > 0
This particular solution was adapted from SQLines. What I ultimately want to do is to turn a table like this:
Name | Category | Votes
--------- | -------- | -----
Name Foo | CatFoo | 0
Name Bar | CatFoo | 1
Name Baz | CatFoo | 10
Name Quux | CatFoo | 200
Name ooF | CatBar | 50
Name raB | CatBar | 300
Name zaB | CatBar | 10
Name xuuQ | CatBar | 200
...to:
Name | Category | Votes
--------- | -------- | -----
Name Quux | CatFoo | 200
Name Baz | CatFoo | 10
Name Bar | CatFoo | 1
Name raB | CatBar | 300
Name xuuQ | CatBar | 200
Name ooF | CatBar | 50
...with the other WHERE statements included. Year, month, and minimum votes.
Your subquery tries to calculate ranking over the entire table. If you only want to rank for the selected year-month with votes > 0, you should copy those conditions into the subquery as its own WHERE conditions.
UPDATE:
Looks like it's the missing ORDER BY in the outer-query that causes the said problem. I've created the following DDL/SQL at sqlfiddle.
CREATE TABLE IF NOT EXISTS `votes` (
`id` INT NOT NULL,
`category` VARCHAR(10) NULL,
`year` VARCHAR(4) NULL,
`month` VARCHAR(2) NULL,
`votes` INT
)
ENGINE = InnoDB;
INSERT INTO `votes` VALUES
(10, 'cat1', '2016', '05', 300),
(10, 'cat1', '2016', '06', 200),
(10, 'cat2', '2016', '05', 500),
(11, 'cat1', '2016', '05', 200),
(11, 'cat2', '2016', '05', 0),
(11, 'cat2', '2016', '06', 100),
(12, 'cat1', '2016', '05', 400),
(12, 'cat2', '2016', '05', 150),
(13, 'cat1', '2016', '05', 350),
(13, 'cat2', '2016', '05', 100),
(13, 'cat2', '2016', '06', 150),
(14, 'cat1', '2016', '05', 0),
(14, 'cat2', '2016', '05', 450);
SELECT `id`, `category`, `year`, `month`, `votes`
FROM (
SELECT `id`, `category`, `year`, `month`, `votes`,
#category_rank := IF(#current_category = category, #category_rank + 1, 1) AS category_rank,
#current_category := category
FROM `votes`
WHERE
`year` = '2016'
AND `month` = '05'
AND `votes` > 0
ORDER BY
`category`,
`votes` DESC
) ranked
WHERE
category_rank <= 3
ORDER BY
`category`,
`votes` DESC;
I'm not an expert with MySQL, so I propose you a different (standard SQL) approach: you can join the table with itself on Category and on Votes being less or equal to the votes of the current row.
select t1.Name, t1.Category, t1.Votes, count(distinct t2.Name) as rank
from photo_contest t1
join photo_contest t2
on t1.Category = t2.Category and
t1.Votes <= t2.Votes
/*where whatever you want*/
group by t1.Name, t1.Category, t1.Votes
having count(distinct t2.Name) <= 3
order by t1.Category, rank
I tested it here and it seems to do what you asked for
It sounds like your PHPMyAdmin needs an upgrade or a replacement. Meanwhile you might want to try #Stefano Zanini's non-MySQL specific SQL:
SELECT
t1.`id`, t1.`category`, t1.`year`, t1.`month`,
t1.`votes`, count(distinct t2.`id`) as rank
FROM photo_contest t1
INNER JOIN photo_contest t2 ON
t1.`category` = t2.`category` AND
t1.`votes` <= t2.`votes`
WHERE t1.`votes` > 0
GROUP BY t1.`id`, t1.`category`, t1.`votes`
HAVING count(distinct t2.`id`) <= 3
ORDER BY t1.`category`, rank;
It's available on sqlfiddle. If you think this solution suits you better please credit #Stefano Zanini's answer instead of this one.

Sql update where left join count equal to 3

Hi I'm trying to build a sql query where I update the value of a table where the left join of another table is equal to 3.
Example when a vehicle has 3 photos.
The query I've written thus far, it seems to fail with group by though.
UPDATE domain.vehicle_listing AS t0 LEFT OUTER JOIN photo AS t1 ON t0.id = t1.vehicle_listing_id
SET t0.active = 0
WHERE `create_date` >= '2015-5-2'
AND user_profile_id is not null
AND t0.active = 1
GROUP BY t1.vehicle_listing_id
HAVING COUNT(DISTINCT t1.id) = 3
ORDER BY create_date desc;
Vehicle_Listing
id
Photo
id, vehicle_listing_id, photo_url
OneToMany relationship with photo.
You can also use exists
UPDATE vehicle_listing AS t0
SET t0.active = 0
WHERE t0.`create_date` >= '2015-05-02'
AND t0.user_profile_id is not null
AND t0.active = 1
AND EXISTS (
SELECT 1
FROM photo
WHERE vehicle_listing_id=t0.id
GROUP BY vehicle_listing_id
HAVING COUNT(DISTINCT id) = 3
)
Sample data for vehicle_listing
INSERT INTO vehicle_listing
(`id`, `title`, `create_date`, `active`,user_profile_id)
VALUES
(1, 'test', '2015-05-02 00:00:00', 1,1),
(2, 'test1', '2015-05-02 00:00:00', 1,1)
;
Sample data for photo
INSERT INTO photo
(`id`, `vehicle_listing_id`, `photo_url`)
VALUES
(1, 1, 'image.jpg'),
(2, 1, 'image.jpg'),
(3, 1, 'image.jpg'),
(4, 2, 'image.jpg'),
(5, 2, 'image.jpg')
;
Sample Output
id title create_date active user_profile_id
1 test May, 02 2015 00:00:00 0 1
2 test1 May, 02 2015 00:00:00 1 1
DEMO
It is silly to use a left join for this. You want inner join:
UPDATE cardaddy.vehicle_listing vl INNER JOIN
(SELECT p.vehicle_listing_id, count(*) as cnt
FROM photo p
GROUP BY p.vehicle_listing_id
) p
ON vl.id = p.vehicle_listing_id AND
p.cnt = 3
SET vl.active = 0
WHERE vl.create_date >= '2015-05-02' AND
vl.user_profile_id IS NOT NULL AND
vl.active = 1;
(Assuming that user_profile_id is in vehicle_listing.)
UPDATE cardaddy.vehicle_listing AS t0
LEFT OUTER JOIN (
SELECT vehicle_listing_id, count(1) AS counter
FROM photo
GROUP BY vehicle_listing_id
) AS t1
ON t0.id = t1.vehicle_listing_id
AND t1.counter = 3
SET t0.active = 0
WHERE `create_date` >= '2015-5-2'
AND user_profile_id IS NOT NULL
AND t0.active = 1
AND t1.vehicle_list_is IS NOT NULL

Efficient way of writing subquery join statement in mysql

I have a reviews table like the one below:
A user can up vote or down vote these reviews. For which, I am maintaining another table named review_counts. It looks like the one below:
Here, 1 means up vote and -1 is down vote.
Now, I am joining these two tables such that I will get reviews with total number of up vote counts and down vote counts all together. To achieve this, I have written the below query which is working fine.
SELECT * FROM `reviews` as x
LEFT JOIN
(SELECT count(votes) as vote_up, review_id FROM `review_counts` WHERE votes = 1) as y ON x.review_id = y.review_id
LEFT JOIN
(SELECT count(votes) as vote_down, review_id FROM `review_counts` WHERE votes = -1) as z ON x.review_id = z.review_id
For which, I get the result like this:
Now, the question is that I am using two JOINS on same table to get the vote up and vote down, Is there any other way through which I can achieve similar results using single join statement?
You could do this with a single LEFT JOIN and SUM(CASE WHEN...END):
CREATE TABLE reviews(
id INT,
review_id VARCHAR(10),
review VARCHAR(10)
)
CREATE TABLE review_counts(
id INT,
review_id VARCHAR(10),
votes INT
)
INSERT INTO reviews VALUES
(1, 'review1', 'Review 1'),
(2, 'review2', 'Review 2');
INSERT INTO review_counts VALUES
(1, 'Review1', 1),
(2, 'Review1', 1),
(3, 'Review1', 1),
(4, 'Review1', 1),
(5, 'Review1', 1),
(6, 'Review2', -1),
(7, 'Review2', -1),
(8, 'Review2', -1),
(9, 'Review2', -1),
(10, 'Review2', -1);
SELECT
r.*,
SUM(CASE WHEN c.votes = 1 THEN 1 ELSE 0 END) AS Vote_Up,
SUM(CASE WHEN c.votes = -1 THEN 1 ELSE 0 END) AS Vote_Down
FROM reviews r
LEFT JOIN review_counts c
ON c.review_id = r.review_id
GROUP BY r.id, r.review_id, r.review
DROP TABLE reviews
DROP TABLE review_counts
RESULT
id review_id review Vote_Up Vote_Down
----------- ---------- ---------- ----------- -----------
1 review1 Review 1 5 0
2 review2 Review 2 0 5

Multiple INNER JOIN subqueries sql

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