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

时间:2017-05-18 08:59:28

标签: php mysql angularjs aggregate-functions

我有两张桌子; 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

现在我想显示学生出席或缺席的日期,还要计算每个学生的主要入学分数,连续缺席和出勤率百分比ms100。

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

例如,5的所有2个班级中都有sid s1,s2,s4的学生,因此他们的subject ms100将为2,因为所有班级都有total number of attended class在这种情况下,total number of consecutive absent0将是percentage{(total attended class/total class)*100}。另一方面,所有课程中都缺少100 %,因此他的s3total number of attended class将为percentage0将为2。

对于学生ID s5,total number of consecutive absent将为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 作为字体结尾而php作为后端。这是我到目前为止所尝试的

PHP

Angularjs

angularjs

$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;

获得这样的结果

<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>

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

1 个答案:

答案 0 :(得分: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')