简体   繁体   中英

Counting total number of consecutive absent and percentage calculation using mysql query

I have two tables; student and attendance :

Student table:

sid            name
-----         --------
s1            nam1      
s2            nam2     
s3            nam3    
s4            nam4      
s5            nam5 

Attendance table:

sid           status       date           sub_id                  
-----         --------   ---------        ------
s1            present    2017-05-16       ms100        
s2            present    2017-05-16       ms100    
s3            absent     2017-05-16       ms100    
s4            present    2017-05-16       ms100    
s5            present    2017-05-16       ms100

s1            present    2017-05-17       ms100        
s2            present    2017-05-17       ms100    
s3            absent     2017-05-17       ms100    
s4            present    2017-05-17       ms100    
s5            absent     2017-05-17       ms100

s1            present    2017-05-16       ms101        
s2            present    2017-05-16       ms101    
s3            absent     2017-05-16       ms101    
s4            present    2017-05-16       ms101    
s5            absent     2017-05-16       ms101

Now I want to show on which date students were present or absent also want to count the total attended class, consecutive absent and percentage of attendance for every student for the subject id ms100.

For consecutive missing/absent i want to to consider only last consecutive missing/absent.For example if among 10 days s1 was present on day 1, 6, 7 then his con_missing will be 3 not 5 . If s1 was present on day 9 then his con_missing will be 0 as i want to consider consecutive missing only when a student is absent for more than 1 days.

For example students with sid s1,s2,s4 were present in all the 2 classes of the subject ms100 so their total number of attended class will be 2, as the were present in all classes so their total number of consecutive absent will be 0 and percentage will be {(total attended class/total class)*100} which is 100 % in this case. On the other hand s3 was absent in all classes so his total number of attended class and percentage will be 0 and total number of consecutive absent will be 2.

For student id s5 the consecutive absent will be 0 as he is absent just for one day.

I am expecting result like following pattern where each individual class date for the subject ms100 will be shown as column and attendance status(present/absent) of an individual student on that particular date will be shown as value of that column:

sid    name   2017-05-16  2017-05-17 consecutive_absnt  total_atn   %                 
-----  -----  ----------  ---------   ----------------  ---------  ----
s1     nam1   present     present      0                  2         100    
s2     nam2   present     present      0                  2         100    
s3     nam3   absent      absent       2                  0          0     
s4     nam4   present     present      0                  2         100    
s5     nam5   present     absent       0                  1         50

I am using Angularjs as font end and php as back end.This is what i have tried so far

php

$query=" 
SELECT atn.sid
     , atn.date
     , atn.status
     , s.name
  FROM attendance atn 
  join student s 
    on atn.sid = s.sid 
 where atn.sub_id = 'ms100'
 ORDER 
    BY atn.date
     , atn.sid
";
    $result = $mysqli->query($query) or die($mysqli->error.__LINE__);

    $arr = array();
    if($result->num_rows > 0) {
        while($row = $result->fetch_assoc()) {
            $arr[] = $row;  
        }
    }
    # JSON-encode the response
    $json_response = json_encode($arr);

    // # Return the response
    echo $json_response;

angularjs

<table class="table table-striped table-bordered">
<thead>
<th>sid</th>
<th>name</th>

<th ng-repeat="data in list | unique: 'date'">{{data.date}}</th>

<th>consecutive missing</th>
<th>total attended </th>
<th>%</th>
</thead>
<tbody>
    <tr ng-repeat="data in filtered = (list | filter:search | orderBy : predicate :reverse) | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit">
       <td>{{data.sid}}</td>
       <td>{{data.name}}</td>
       <td>{{data.status}}</td>
        <td>{{data.consecutive}}</td>
        <td>{{data.total_atn}}</td>
        <td>{{data.percentage}}</td>
  </tbody>
</table>

Getting result like this

sid      name       2017-05-16   2017-05-17  con_missing  totl_atend  %           
-----   --------   ---------   ----------    --------     --------   ---
s1      nam1         present                         
s2      nam2         present                
s3      nam3         absent          
s4      nam4         present        
s5      nam5         present    

s1      nam1         present            
s2      nam2         present         
s3      nam3         absent          
s4      nam4         present        
s5      nam5         absent      

So how can I achieve my expected result through mysql query?

Ok this took a while to get together :)

First off we setup a function in the mysql database to get the consecutive number of days:

CREATE FUNCTION `getConsecutive`( _subid varchar(45), _sid varchar(45) ) RETURNS int(11)
BEGIN

    declare ret int;

    select max(consecutive) into ret from (
            select  q.date, 
                    q.status,
                    @consecutive :=  CASE WHEN @stop = 1 THEN 0 WHEN q.status = 'absent' THEN @consecutive +1 ELSE 0 END as consecutive,
                    @started :=     CASE WHEN @consecutive > 0 OR @started > 0 THEN 1 ELSE 0 END as started,
                    @stop :=        CASE WHEN @consecutive = 0 AND @started > 0 THEN 1 ELSE @stop END as stop
            from (
                select date, status from Attendance where sub_id = _subid and sid = _sid order by date desc
            ) q,
            (select @consecutive := 0) r,
            (select @started := 0) r2,
            (select @stop := 0) r3
        ) as z;

    RETURN CASE WHEN ret = 1 THEN 0 ELSE ret END;

END

Then we build the sql, but hardcoded to test for 2 columns:

SELECT atn.sid, s.name,
    MAX(IF(atn.date = '2017-05-16', atn.date, null)) `2017-05-16`,
    MAX(IF(atn.date = '2017-05-17', atn.date, null)) `2017-05-17`,
    getConsecutive(atn.sub_id, atn.sid) consecutive_absnt,
    SUM(CASE WHEN atn.status = 'present' THEN 1 ELSE 0 END) total_atn,
    ROUND(100*(SUM(CASE WHEN atn.status = 'present' THEN 1 ELSE 0 END)/count(1)), 2) '%'
  FROM Attendance atn 
  join Student s 
    on atn.sid = s.sid 
 where atn.sub_id = 'ms100'
 GROUP BY atn.sid, s.name;

Then we know it works but the main problem is that it is a dynamically number of columns.. so you will need to do is divide this up in parts.

We need a stored procedure that will build and execute our dynamic sql.

CREATE PROCEDURE `getData`(_subId VARCHAR(45))
BEGIN

    select GROUP_CONCAT(CAST(CONCAT('MAX(IF(atn.date = \'', dd, '\', atn.date, null)) `', dd, '`\n') AS CHAR)) INTO @builtSql
    from (
        select distinct str_to_date(date, '%Y-%m-%d') dd from Attendance where sub_id = _subId
        ) q
        ;


  SET @builtSql = CONCAT('SELECT atn.sid, 
        s.name,', @builtSql, ',
        getConsecutive(atn.sub_id, atn.sid) consecutive_absnt, 
        SUM(CASE WHEN atn.status = \'present\' THEN 1 ELSE 0 END) total_atn, 
        ROUND(100*(SUM(CASE WHEN atn.status = \'present\' THEN 1 ELSE 0 END)/count(1)), 2) \'%\' 
        FROM Attendance atn 
        join Student s on atn.sid = s.sid 
        where atn.sub_id = \'', _subId,'\' 
        GROUP BY atn.sid, s.name');

    PREPARE stmt FROM @builtSql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

END

To build a dynamic sql is usually a bad idea because of the risk of sql injection, so i advise you to do some checks on the data sent into the stored procedure.

Then you can just call the Stored Procedure to get the result you wanted above.

call getData('ms100')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM