使用mysql查询计算连续缺席总数和百分比计算

Posted

技术标签:

【中文标题】使用mysql查询计算连续缺席总数和百分比计算【英文标题】:Counting total number of consecutive absent and percentage calculation using mysql query 【发布时间】:2017-10-17 23:54:58 【问题描述】:

我有两张桌子; studentattendance:

学生桌:

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

考勤表:

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

现在我想显示学生在哪一天在场或缺席,还想计算科目 id ms100 的每个学生的总上课次数、连续缺勤率和出勤率。

对于连续失踪/缺席,我只想考虑最后一次连续失踪/缺席。例如,如果 s1 在第 1、6、7 天出现 10 天,那么他的 con_missing 将是 3 而不是 5。如果 s1 在第 9 天出现,那么他的 con_missing 将为 0,因为我只想在学生缺席超过 1 天时才考虑连续失踪。

例如,sid s1,s2,s4 的学生出现在 subject ms100 的所有 2 个班级中,因此他们的 total number of attended class 将是 2,因为出现在所有班级中,所以他们的 total number of consecutive absent 将是 0 和 @ 987654332@ 将是 (total attended class/total class)*100 在这种情况下是 100 %。另一方面,s3 在所有课程中都缺席,因此他的total number of attended classpercentage 将是0total number of consecutive absent 将是2。

对于学生 ID s5,consecutive absent 将是 0,因为他只缺席了一天。

我希望得到类似以下模式的结果,其中主题 ms100 的每个单独上课日期将显示为列,并且在该特定日期单个学生的出勤状态(存在/缺席)将显示为该列的值:

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

我使用Angularjs 作为字体末端和php 作为后端。这是我迄今为止尝试过的

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>

得到这样的结果

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      

那么如何通过mysql查询达到我预期的结果呢?

【问题讨论】:

我猜这应该比您指定的两个日期有效?如果是这样,它应该总是添加更多的列吗?如果您想检查多个星期,那将是大量的列。还有, con_missing 应该如何在更长的时间跨度内工作?如果您检查 10 天并且 s1 在第 1、6、7 天出现。那么前 4 天是连续的,然后是 3 天。应该如何呈现?还是应该一直只有两个日期?我们需要更多信息在这里.. 是的,这里的日期不固定。日期将取决于特定主题有多少独特的“日期”条目,例如,如果主题“ms100”总共完成了 10 个讲座,那么我们有 10 个唯一日期,如果完成更多讲座,那么我们就有更多日期,即完全不知道有多少个日期。我想在讲座完成时选择那些唯一日期,并希望在这些日期显示学生的出勤状态。对于con_missing 我只想考虑最后一次连续丢失。如果 s1 在第 1、6、7 天出现,那么他的 con_missing 将是 3 而不是 5。 【参考方案1】:

好的,这需要一段时间才能聚在一起:)

首先我们在mysql数据库中设置一个函数来获取连续天数:

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

然后我们构建 sql,但硬编码以测试 2 列:

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;

然后我们知道它可以工作,但主要问题是它是动态数量的列。所以你需要做的就是将它分成几部分。

我们需要一个存储过程来构建和执行我们的动态 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

构建动态sql通常是个坏主意,因为存在sql注入的风险,所以我建议你对发送到存储过程的数据做一些检查。

然后你可以调用存储过程来得到你想要的结果。

call getData('ms100')

【讨论】:

非常感谢您的辛勤工作。对不起,我从未使用过这种类型的程序,所以我不知道如何在 php 中使用此查询来实现我预期的结果表。可以你提供了一些关于这个问题的指南? 嗨。只需在服务器上运行创建查询(一个用于存储过程,一个用于函数)(直接在 mysql ide 中,例如工作台)。先运行函数,再运行存储过程。然后运行命令“call getDate('ms100');”对其进行测试。如果一切正常,那么您只需从 php 运行该命令,这里有一个示例:***.com/questions/3966747/… @query,如果您需要更多信息或有什么问题,请告诉我!

以上是关于使用mysql查询计算连续缺席总数和百分比计算的主要内容,如果未能解决你的问题,请参考以下文章

获取列总和并用于计算总数的百分比(mySQL)

获取列总和并用于计算总数的百分比(mySQL)

mysql 将子查询排除在连接之外

MySQL:在进一步计算中使用聚合结果

计算查询中的百分比 - Access SQL

我想从子查询的结果中计算男性和女性的总数。有没有mysql查询