You will have to do a couple passes on your data set to generate that output. That is, you'll have lets say 4 rows representing all of the status values and you will have to iterate over it a couple of times to extract out the date column headers and the "Class" row identifiers.
You can perform this in PHP. So on the 1st pass you grab the dates for the header. And also store the "Class" for the first column.
On the 2nd pass you then iterate over the data again but this time its wrapped in a loop so you can pull out the records for that cell.
Here is some psuedo-code:
$records = $db->query("select * from your_query here...");
$dates = [];
$classes = [];
// first pass is to pull out the distinct dates & classes which represent our bounds
foreach($records AS $record) {
$dates[] = $record['execution_date'];
$classes[] = $record['class_name'];
}
// distinct the date set and sort them from lowest to highest
$dates = array_unique($dates);
$dates = sort($dates);
$classes = array_unique($classes);
// display the date row
echo "<tr><td> </td>"
foreach($dates AS $date) {
echo $date;
}
echo "</tr>";
// start displaying each class+date pair
foreach($classes AS $klass) {
echo "<tr>";
echo "<td>" . $klass . "</td>";
// display each date record for this class
foreach($dates AS $date) {
$class_and_date_record = filter($records, $klass, $date);
if($class_and_date_record) {
echo "<td>" . $class_and_date_record['status'] . "</td>";
}
}
echo "</tr>";
}
function filter($records, $klass, $date) {
foreach($records AS $row) {
if($row['class_name'] == $klass && $row['execution_date'] == $date) {
return $row;
}
}
return NULL;
}