问题
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.
回答1:
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.
回答2:
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
回答3:
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
回答4:
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 |
回答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
来源:https://stackoverflow.com/questions/14192205/count-number-of-items-in-a-row-in-mysql