【问题标题】:How do I create a Crosstab table with php mysql如何使用 php mysql 创建交叉表
【发布时间】:2014-04-07 15:46:16
【问题描述】:

我有以下记录集:

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

并且我希望数据以列表/名册格式显示。例如。

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

【问题讨论】:

  • 你应该可以用循环来做到这一点。即使您不想更改 SQL,您也应该让结果集按角色(行)然后按日期(列)排序。然后循环:每次更改 Role 时创建一个新角色,并为每个日期放入相应的 HTML 表格列。
  • MySQL pivot table的可能重复
  • “我希望它完成......”好主意。只需使用一个简单的循环。
  • 有人可以给我一个更清楚的例子来说明如何使用 php 中的循环来做到这一点吗?谢谢。
  • 完成您需要的通用解决方案并不像最初看起来那么简单。例如,如果某天缺少某些角色怎么办。另外,如果您希望以不同的顺序打印角色怎么办。我已经考虑过了,将尝试创建一些代码来处理上述情况,并且仍然很容易更改。

标签: php mysql crosstab


【解决方案1】:

天哪,这很有趣... :-/

这是按要求执行的经过测试的代码。有很多cmets。随意删除它们以更清楚地查看代码。随便...

您应该能够更改 $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>

【讨论】:

  • 谢谢你,这是一个很大的帮助。我尝试修改您的代码以从我的数据库中获取数据集,但我收到“致命错误:第 125 行 /var/www/vhosts/cfmworship.org.uk/gem2.php 中的最大执行时间超过 30 秒”。 125 对应于 ' // 读取下一个输入行 - 可能是新的一天或不再有 $currentRoleRow = next($testData);'任何想法为什么会发生这种情况?
  • 您可以在这里查看我的完整代码:cfmworship.org.uk/rota_code.txt 任何建议都会非常有帮助。谢谢。
  • 您需要用 testData 查询中的“获取下一行”替换“testData”数组中的所有读取。然后它应该按预期工作。所以用 fetches 替换 'current(testData) 和 next(testData)。
  • 用 'mysqli' 查询替换了 testData 数组。
  • 感谢您提供的代码,它看起来更像是我的目标。但是,结果似乎无法在网格中正确显示。以cfmworship.org.uk/gam2.php 为例。不知道这里发生了什么。任何帮助将非常感激。非常感谢!
【解决方案2】:
    <?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>';
        
 ?>

【讨论】:

    【解决方案3】:

    ...这很有趣。我决定将所需的输出复制为原始文本而不是 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        
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-06
      • 1970-01-01
      • 2018-10-02
      • 2017-05-15
      • 2017-01-10
      相关资源
      最近更新 更多