使用嵌套While循环MySQL / PHP获取记录范围

时间:2013-06-20 05:29:44

标签: php mysql while-loop nested-loops

我有一个表“dtr”,其中包含6列,即Name,ACNo,Date,Time,State和Exception。

   Name      | ACNo. |    Date   |    Time      |   State    | Exception|
-------------+-------+-----------+--------------+------------+----------+
Johnny Starks| 1220  | 5/13/2013 | 11:45:18 PM  |  Check In  |   OK     |
Johnny Starks| 1220  | 5/14/2013 | 12:46:58 AM  |  Out       |   Out    |
Johnny Starks| 1220  | 5/14/2013 | 12:52:41 AM  |  Out Back  |   Out    |
Johnny Starks| 1220  | 5/14/2013 | 02:12:50 AM  |  Out       |   Out    |
Johnny Starks| 1220  | 5/14/2013 | 02:43:11 AM  |  Out Back  |   Out    |
Johnny Starks| 1220  | 5/14/2013 | 05:46:58 AM  |  Out       |   Out    |
Johnny Starks| 1220  | 5/14/2013 | 06:22:41 AM  |  Out Back  |   Out    |
Johnny Starks| 1220  | 5/14/2013 | 06:55:12 AM  |  Check Out |   OK     |
Johnny Starks| 1220  | 5/14/2013 | 11:47:13 PM  |  Check In  |   OK     |
Johnny Starks| 1220  | 5/15/2013 | 12:36:28 AM  |  Out       |   Out    |
Johnny Starks| 1220  | 5/15/2013 | 12:59:11 AM  |  Out Back  |   Out    |
Johnny Starks| 1220  | 5/15/2013 | 03:12:54 AM  |  Out       |   Out    |
Johnny Starks| 1220  | 5/15/2013 | 03:33:31 AM  |  Out Back  |   Out    |
Johnny Starks| 1220  | 5/15/2013 | 06:55:12 AM  |  Check Out |   OK     |

所以,我想使用下面的代码得到的结果是...或者我希望循环执行的序列..

在 第一个循环 - 它将获得1个登记入住 然后移动到 第二个循环 - 将获得结账 在办理入住和退房手续之后.. 它现在会去 第3个/最后一个循环 - 将使用第一个和第二个循环的结果范围获得Breaks(具有Out Exception的那个)。

我这样做,所以我可以计算每一对的Out和Outbreak 一(1)班(从办理登机手续到退房)。 之后,我可以计算一(1)班的休息时间/分钟。

我不能仅仅基于日期的转变范围。 becoz如果我这样做,其他休息将在另一个/下一个班次,它与休息日具有相同的登记日期,我无法正确计算或计算它所具有的每一对休息。

但似乎我无法做对。 我该怎么办才能得到正确的结果?或者让循环正确...

<?php
include 'connection.php';
$checktime='';
$outtime = '';

$isql = mysql_query("Select * from dtr where ACNo = '1220' and 
State = 'Check In'") or die(mysql_error());

while($irow = mysql_fetch_array($isql)){
    $iID = $irow['ACNo'];
    $iDate = $irow['Date'];
    $iTime = $irow['Time'];
    $iState = $irow['State'];

    if($iState == 'Check In'){
        $checktime = $iTime;
        $indate = $iDate;
    }

$osql = mysql_query("Select * from dtr where ACNo = '1220' and 
State = 'Check Out'") or die(mysql_error());

while($orow = mysql_fetch_array($osql)){
    $oID = $orow['ACNo'];
    $oDate = $orow['Date'];
    $oTime = $orow['Time'];
    $oState = $orow['State'];

    if($oState == 'Check Out'){
        $outtime = $oTime;
        $outdate = $oDate;
    }


$bsql = mysql_query("select * from dtr where Exception = 'Out' And Time Between     
'$checktime' and '$outtime'")or die(mysql_error());

while($brow = mysql_fetch_array($bsql)){
    $bID = $brow['ACNo'];
    $bDate = $brow['Date'];
    $bTime = $brow['Time'];
    $bState = $brow['State'];

    echo $bDate.' - ';
    echo $bTime.' ';
    echo $bState.'<br>';
}

}

}
?>

编辑: 按照要求.. 我一直期待或想要的输出是这样的......

 5/13/2013 | 11:45:18 PM  |  Check In  
 5/14/2013 | 12:46:58 AM  |  Out       
 5/14/2013 | 12:52:41 AM  |  Out Back  
 5/14/2013 | 02:12:50 AM  |  Out       
 5/14/2013 | 02:43:11 AM  |  Out Back  
 5/14/2013 | 05:46:58 AM  |  Out       
 5/14/2013 | 06:22:41 AM  |  Out Back  
 5/14/2013 | 06:55:12 AM  |  Check Out
 Break Count : 3
 Total Mins Of Break: [total here]

 5/14/2013 | 11:47:13 PM  |  Check In  
 5/15/2013 | 12:36:28 AM  |  Out       
 5/15/2013 | 12:59:11 AM  |  Out Back  
 5/15/2013 | 03:12:54 AM  |  Out       
 5/15/2013 | 03:33:31 AM  |  Out Back  
 5/15/2013 | 06:55:12 AM  |  Check Out
 Break Count : 2
 Total Mins Of Break: [total here] 

2 个答案:

答案 0 :(得分:1)

您拥有的算法很脆弱,难以理解(可能是由于变量名称和嵌套循环),并且易于优化。我建议采用不同的方法。这将生成您想要的示例输出并修复我提到的问题:

<?php
include 'connection.php';

$q = mysql_query("SELECT * FROM dtr WHERE ACNo = '1220' ORDER BY `Date`, `Time`") or die(mysql_error());

$isIn = false;
$breakDate = NULL;
$breakTime = NULL;
$breakCount = 0;
$breakCumulativeSeconds = 0;
while($dtr = mysql_fetch_object($q))
{
  $logLine = "$dtr->Date | $dtr->Time | $dtr->State\n";

  switch($dtr->State)
  {
    case 'Check In':
      if($isIn)
      {
        throw new LogicException('Two check-ins.');
      }

      $isIn = true;
      echo $logLine;
      break;

    case 'Check Out':
      if(!$isIn)
      {
        throw new LogicException('Check out when not checked in');
      }
      else if(isset($breakDate))
      {
        throw new LogicException('Check out while on break.');
      }

      echo $logLine;
      echo "Break Count: $breakCount\n";
      echo 'Total Mins Of Break: ', number_format($breakCumulativeSeconds / 60, 2), "\n\n";
      $breakCount = $breakCumulativeSeconds = 0;
      $isIn = false;
      break;

    case 'Out':
      if(!$isIn)
      {
        throw new LogicException('Break when not checked in');
      }
      else if(isset($breakDate))
      {
        throw new LogicException('Break start while already on break');
      }

      $breakDate = $dtr->Date;
      $breakTime = $dtr->Time;

      echo $logLine;
      break;

    case 'Out Back':
      if(!isset($breakDate))
      {
        throw new LogicException('Break end while not on break');
      }

      ++$breakCount;
      $breakCumulativeSeconds += (strtotime("$dtr->Date $dtr->Time") - strtotime("$breakDate $breakTime"));
      $breakDate = $breakTime = NULL;

      echo $logLine;
      break;

    default:
      throw new LogicException('Unknown State.');
  }
}
?>

另外,就像有人提到的那样,你应该考虑使用mysqli,PDO或者更好的像Docii或者ActiveRecord这样的完整ORM,就像Yii所拥有的那样。如果你做任何这些,你会很高兴。

答案 1 :(得分:1)

我可能会做的是在DB端进行所有计算和准备数据(使用相当残酷的查询)然后将其呈现在php中

$acno = 1220;

$sql = 
"SELECT s.n, s.details, b.breaks_count, b.breaks_total
  FROM
(
  SELECT n, GROUP_CONCAT(CONCAT_WS('|', DATE_FORMAT(date, '%m/%d/%Y'), time, state)) details
    FROM
  (
    SELECT @n := IF(state = 'Check In', @n + 1, @n) n, d.*
      FROM dtr d, (SELECT @n := 0, @m := 0) n
     WHERE acno = $acno
  ) a
  GROUP BY n
) s JOIN
(
  SELECT n, COUNT(*) breaks_count, ROUND(SUM(o.secs) / 60) breaks_total
    FROM
  (
    SELECT n, m 
           ,TIME_TO_SEC(
            TIMEDIFF(MIN(CASE WHEN state = 'Out Back' THEN ADDTIME(date, time) END),
                     MIN(CASE WHEN state = 'Out'      THEN ADDTIME(date, time) END))) secs
      FROM
    (
      SELECT @n := IF(state = 'Check In', @n + 1, @n) n, 
             @m := IF(state = 'Out', @m + 1, @m) m,
             d.*
        FROM dtr d, (SELECT @n := 0, @m := 0) n
       WHERE acno = $acno
    ) q
     WHERE state IN('Out', 'Out Back')
     GROUP BY n, m
  ) o
   GROUP BY n
) b ON s.n = b.n";

$result = mysql_query($sql);
if($result === FALSE) {
    die(mysql_error()); // TODO: better error handling
}
//We've done everything on db side so presentation is short and clean
while($row = mysql_fetch_assoc($result)) {
    $details = explode(',', $row['details']);
    foreach ($details as $detail) {
        list($date, $time, $state) = explode('|', $detail);
        echo "$date - $time - $state<br>";
    }
    echo "Break Count: " .$row['breaks_count']."<br>";
    echo "Total Mins Of Break: ".$row['breaks_total']."<br>";
}

以下是数据库查询的 SQLFiddle 演示。

根据您的示例数据输出php脚本:

05/13/2013 - 11:45:18 PM - Check In
05/14/2013 - 12:46:58 AM - Out
05/14/2013 - 12:52:41 AM - Out Back
05/14/2013 - 02:12:50 AM - Out
05/14/2013 - 02:43:11 AM - Out Back
05/14/2013 - 05:46:58 AM - Out
05/14/2013 - 06:22:41 AM - Out Back
05/14/2013 - 06:55:12 AM - Check Out
Break Count: 3
Total Mins Of Break: 72
05/14/2013 - 11:47:13 PM - Check In
05/15/2013 - 12:36:28 AM - Out
05/15/2013 - 12:59:11 AM - Out Back
05/15/2013 - 03:12:54 AM - Out
05/15/2013 - 03:33:31 AM - Out Back
05/15/2013 - 06:55:12 AM - Check Out
Break Count: 2
Total Mins Of Break: 43