如何使用php mysql创建一个Crosstab表

时间:2014-03-05 14:13:56

标签: php mysql crosstab

我有以下记录集:

Date     |Role      |Name
=============================
01/02/14 |Musician  |Bob
01/02/14 |Leader    |Jerry
01/02/14 |Singer    |Carol
08/02/14 |Musician  |Charles
08/02/14 |Leader    |Baz
08/02/14 |Singer    |Norman

我希望数据显示为rota / roster格式。 EG。

Role     |01/02/14  |08/02/14
===============================
Musician |Bob       |Charles
Leader   |Jerry     |Baz
Singer   |Carol     |Norman

理想情况下,我希望在不更改MySQL查询的情况下在php中完成。

这是我到目前为止所得到的,但它并不是很有用。

$temprole='norole';   

$newkey=0;


echo "<table><tr>";
foreach ($result as $key => $val) {
echo "<td>" . $val['date'] . "</td>";

if ($temprole==$val['role_name']){ //is the same role?

 } else {
   //If the role name is different, print the role column
 echo $val['role_name'] . "</br>";
  }
        $temprole = $val['role_name'];
  }

 echo "</tr></table>";



echo "<hr>";

3 个答案:

答案 0 :(得分:2)

善良,这很有趣......: - /

这是经过测试的代码,可以根据需要执行。有很多评论。随意删除它们以更清楚地看到代码。无论...

您应该能够更改$ allRoles数组以使角色以不同的顺序打印。我试过了,效果很好。

它在Windows XP(XAMPP)上运行PHP 5.3.18。

添加了一些css以使表格更清晰。

更改了代码以从'mysqli'查询而不是数组

中读取数据

查看标有'!important'的行,以确保其正常工作。

示例输出:

Roles       01/02/14        05/02/14        08/02/14
musician    Bob             Donald          Charles
leader      Jerry           --              Baz
singer      Carol           Freddy          Norman

代码:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Q2220229 - Pivot table</title>
    <style>
      td {
        border-bottom: 1px solid grey;
        width: 10em;
      }
    </style>
  </head>

  <body>
<?php

/*
 * Some test data base on:
 * Date     |Role      |Name
   =============================
   01/02/14 |Musician  |Bob
   01/02/14 |Leader    |Jerry
   01/02/14 |Singer    |Carol
   08/02/14 |Musician  |Charles
   08/02/14 |Leader    |Baz
   08/02/14 |Singer    |Norman
 *
 */

 /* sample output:
  *
  * Role     |01/02/14  |08/02/14
    ===============================
    Musician |Bob       |Charles
    Leader   |Jerry     |Baz
    Singer   |Carol     |Norman
  */

$db = mysqli_connect('localhost', 'test', 'test', 'testmysql');

// 1) Must return three columns only.
// 2) Can return any number of 'roles' - one per row
// 3) Any date range but beware you may need a wide page!
// 4) Must sort by date!  
$query = mysqli_query($db, "SELECT service_date, role, member FROM role_by_date ORDER BY service_date ASC, role ASC");

// i prefer to used named subscripts to make the code easier to read.
// These MUST match up with column alias from the above query!
define('THE_DATE', 'service_date'); // !important
define('ROLE',     'role');         // !imortant
define('MEMBER',   'member');       // !important

/*
 * Now, we need a complete array of Roles in the order that they are to be displayed.
 *
 * These names must match with the names of the roles in the input data.
 * They will be printed out in the order that they appear in the array.
 *
 * These are the only roles that will appear in the $outputDates array.
 * Add more and in any order to control which 'roles' are shown.  
 *
 */
$allRoles = array('musician', 'leader', 'singer'); // !important

/*
 * At some point we will need an output array that we can easily traverse and
 * print out as a row of dates. i.e. a 'page' of data.
 *
 * We will build it up as we go along...
 */
$outputDates = array(); // !important -- this is the 'pivoted' output array

/*
 * Start to process the input data.
 *
 * To make my life easier, i will use the 'read ahead' technique to simplify the code.
 */

$currentInputRow = mysqli_fetch_array($query);

while (isset($currentInputRow[THE_DATE])) { // process all the input array...

  // must be a new day...
  $currentDay = $currentInputRow[THE_DATE];

  // create an array to hold ALL the possible roles for this day...
  $theDayRoles = array();

  // initialise the array with default values for all the requested roles.
  foreach ($allRoles as $role) {
    $theDayRoles[$role] = '--';
  }

  // now we need to fill theDayRoles with what we actually have for the current day...
  while ($currentInputRow[THE_DATE] == $currentDay) { // loop around all records for the current day

    // set the appropiate DayRole to the current MEMBER
    $theDayRoles[$currentInputRow[ROLE]] = $currentInputRow[MEMBER];

    // read the next input row - may be current day, new day or no more
    $currentInputRow = mysqli_fetch_array($query);
  }
  // end of day on the input for whatever reason...

  /* we now have:
   *   1) Current Date
   *
   *   2) an array of members for ALL the roles on that day.
   *
   *   We need to output it to another array ($outputDates) where we can print it out
   *   by scanning the array line by line later.
   *
   *   I will 'pivot' the array and produce an output array we can scan sequentially later.
   */

   // to ensure that we are updating the correct $outputDates row i will use a subscript
   $currentOutputRowIdx = 0;

   // first add the current date to the output...
   $outputDates[$currentOutputRowIdx][] = $currentDay;
   $currentOutputRowIdx++; // next output row

   // we need to drive off the '$allRoles' array to add the role data in the correct order
   foreach ($allRoles as $outRole) {
     $outputDates[$currentOutputRowIdx][] = $theDayRoles[$outRole];
     $currentOutputRowIdx++; // next output row
   }

} // end of all the input data


/*
 * Now we just need to print the outputDates array one row at a time...
 */

// need the roles as the first column...
// so we need an index for which one we are currently printing

$currentRoleIdx = -1; // increment each time but allow for the first row being the title 'Roles'

echo '<table>';
foreach ($outputDates as $oneOutputRow) {

  echo '<tr>';

  // this is the first column...
  if ($currentRoleIdx < 0) {
    echo '<td>'. 'Roles' .'</td>';
  }
  else {
    echo '<td>'. $allRoles[$currentRoleIdx] .'</td>';
  }

  // now output the day info
  foreach($oneOutputRow as $column) {
    echo '<td>'. $column .'</td>';
  }
  echo '</tr>';
  $currentRoleIdx++; // next output Role to show...

}
echo '</table>';

?>
</body>
</html>

答案 1 :(得分:1)

    <?php
        echo '<div align=left>';
        //initialize three arrays to hold the values
        $x_role = array();
        $y_date = array();
        $val_name = array();
        
        //store values from the database into three separate arrays NB theRole, 
        // theDate  and theName are the column names from the database
        foreach($data as $recordset)
        {
           $x_role[] =  $recordset->theRole;
           $y_date[] =  $recordset->theDate;    
           $val_name[$recordset->theRole][$recordset->theDate] = $recordset->theName;   
        }
        
        //get unique values for row and column and sort them if necessary
        $unique_x_role = array_unique($x_role);
        //asort($unique_x_role);    
        
        $unique_y_date = array_unique($y_date);
        //asort($unique_y_date);    
        
        // prints table - OUTPUT
        echo '<p>';
        echo '<hr size=10 color=#6f9171 width=100% align=left>';
        echo '<p>';
        echo '<div align=left>';
        
        echo '<table border=3 >';
        
        echo '<tr>';
        echo '<td>ROLE</td>';//prints 'ROLE" in upper left corner of table  
        foreach($unique_y_date as $theDate)
        {
           echo '<td>'.$theDate.'</td>';//prints column headings    
        }
        echo '</tr>';//closes column headings   
        
        foreach($unique_x_role as $theRole)
        {
            echo '<tr>';
            echo '<td>'.$theRole.'</td>';   //prints row title  
            foreach($unique_y_date as $theDate)
            {
                if(isset($val_name[$theRole][$theDate]))// checks if value exists
                {
                   $v =     $val_name[$theRole][$theDate];
                   echo '<td>'.$v.'</td>';  //prints name because it exists
                }
                else
                {
                    echo '<td> - </td>';// prints a dash if  no name exists
                }
            }//foreach($y_date as $theDate)
            echo '</tr>';
        }//foreach($unique_x_role as $theRole)
        
        echo '</table>';
        echo '</div>';
        
 ?>

答案 2 :(得分:0)

...这很有趣。我决定将所需的输出复制为原始文本,而不是将HTML复制为个人挑战。

从本质上讲,数据形成为参考数组,然后进行内爆或迭代打印所需的交叉表布局。 $columnWidth变量允许轻松调整整个表的大小。 str_pad()用于中心对齐。当各自的值不存在时,空合并运算符(??)用于回退到-

代码:(Demo

//It is assumed that the sql is perfectly capable of sorting by date ASC, role ASC, name ASC
$resultSet = [
    ['date' => '01/02/14', 'role' => 'Leader', 'name' => 'Jerry'],
    ['date' => '01/02/14', 'role' => 'Musician', 'name' => 'Bob'],
    ['date' => '01/02/14', 'role' => 'Singer', 'name' => 'Carol'],
    ['date' => '08/02/14', 'role' => 'Leader', 'name' => 'Baz'],
    ['date' => '08/02/14', 'role' => 'Leader', 'name' => 'Gaz'],
    ['date' => '08/02/14', 'role' => 'Leader', 'name' => 'Haz'],
    ['date' => '08/02/14', 'role' => 'Musician', 'name' => 'Charles'],
    ['date' => '08/02/14', 'role' => 'Singer', 'name' => 'Norman'],
    ['date' => '15/02/14', 'role' => 'Astronaut', 'name' => 'Neil'],
];

$columnWidth = 20;

foreach ($resultSet as ['date' => $date, 'role' => $role, 'name' => $name]) {
    $nested[$date][$role][] = $name;
    $dates[$date] = str_pad($date, $columnWidth, " ", STR_PAD_BOTH);
    $roles[$role] = str_pad($role, $columnWidth, " ", STR_PAD_BOTH);
}
$totalColumns = count($dates) + 1;

// HEADINGS
printf(
    implode("|", array_fill(0, $totalColumns, '%s')) . "\n",
    str_pad('Roles', $columnWidth, " ", STR_PAD_BOTH),
    ...array_values($dates)
);

// SEPARATOR
echo implode("|", array_fill(0, $totalColumns, str_repeat('=', $columnWidth)));

// DATA
foreach ($roles as $role => $paddedRole) {
    echo "\n$paddedRole";
    foreach ($nested as $date => $roleGroup) {
        echo '|' . str_pad(implode(', ', $nested[$date][$role] ?? ['-']), $columnWidth, " ", STR_PAD_BOTH);
    }
}

输出:

       Roles        |      01/02/14      |      08/02/14      |      15/02/14      
====================|====================|====================|====================
       Leader       |       Jerry        |   Baz, Gaz, Haz    |         -          
      Musician      |        Bob         |      Charles       |         -          
       Singer       |       Carol        |       Norman       |         -          
     Astronaut      |         -          |         -          |        Neil