count number of items in a row in mysql

烂漫一生 提交于 2020-01-01 18:17:36

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!