WITH SORTERS AS ( SELECT DATE_FORMAT(MIN(s04_timestamp), "%Y-%m-%d %H:00") AS start_timestamp, CONCAT("H", CAST( TIMESTAMPDIFF( HOUR, DATE_FORMAT(MIN(s04_timestamp), "%Y-%m-%d %H:00:00"), DATE_FORMAT(LEAST(CURRENT_TIMESTAMP(), :endDate), "%Y-%m-%d %H:00:00") ) AS CHAR ) ) AS hour, COUNT(*) AS total, SUM(sort_code="Success") AS success, SUM(sort_code="Unknown") AS unknown, SUM(sort_code="Unexpected") AS unexpected, SUM(sort_code="Track Err") AS track_err, SUM(sort_code="Gap Err") AS gap_err, SUM(sort_code="Dest Full") AS dest_full, SUM(sort_code="Dest Fault") AS dest_fault, SUM(sort_code="Dest Invalid") AS dest_invalid, SUM(sort_code="No Read") AS no_read, SUM(sort_code="No Code") AS no_code, SUM(sort_code="Multi Label") AS multi_label, SUM(sort_code="Dest Disabled") AS dest_disabled, SUM(sort_code="Rate High") AS rate_high, SUM(sort_code="Div Fail") AS div_fail, SUM(sort_code="Dest None") AS dest_none, SUM(sort_code="Lost") AS lost, SUM(sort_code="Dim Err") AS dim_err, SUM(sort_code="Weight Err") AS weight_err, SUM(sort_code="Underutilized") AS underutilized, SUM(sort_code="Unsafe") AS unsafe FROM package_history WHERE s04_timestamp BETWEEN :startDate AND :endDate AND sorter=:sorter GROUP BY DATE_FORMAT(s04_timestamp, "%Y-%m-%d %H:00") ) SELECT start_timestamp, hour, /* Counts: */ total AS total_count, success AS success_count, unknown AS unknown_count, unexpected AS unexpected_count, track_err AS track_err_count, gap_err AS gap_err_count, dest_full AS dest_full_count, dest_fault AS dest_fault_count, dest_invalid AS dest_invalid_count, no_read AS no_read_count, no_code AS no_code_count, multi_label AS multi_label_count, dest_disabled AS dest_disabled_count, rate_high AS rate_high_count, div_fail AS div_fail_count, dest_none AS dest_none_count, lost AS lost_count, dim_err AS dim_err_count, weight_err AS weight_err_count, underutilized AS underutilized_count, unsafe AS unsafe_count, /* PPH: */ total AS total_pph, success AS success_pph, unknown AS unknown_pph, unexpected AS unexpected_pph, track_err AS track_err_pph, gap_err AS gap_err_pph, dest_full AS dest_full_pph, dest_fault AS dest_fault_pph, dest_invalid AS dest_invalid_pph, no_read AS no_read_pph, no_code AS no_code_pph, multi_label AS multi_label_pph, dest_disabled AS dest_disabled_pph, rate_high AS rate_high_pph, div_fail AS div_fail_pph, dest_none AS dest_none_pph, lost AS lost_pph, dim_err AS dim_err_pph, weight_err AS weight_err_pph, underutilized AS underutilized_pph, unsafe AS unsafe_pph, /* Percents: */ ROUND(total/total, 4) AS total_percent, ROUND(success/total, 4) AS success_percent, ROUND(unknown/total, 4) AS unknown_percent, ROUND(unexpected/total, 4) AS unexpected_percent, ROUND(track_err/total, 4) AS track_err_percent, ROUND(gap_err/total, 4) AS gap_err_percent, ROUND(dest_full/total, 4) AS dest_full_percent, ROUND(dest_fault/total, 4) AS dest_fault_percent, ROUND(dest_invalid/total, 4) AS dest_invalid_percent, ROUND(no_read/total, 4) AS no_read_percent, ROUND(no_code/total, 4) AS no_code_percent, ROUND(multi_label/total, 4) AS multi_label_percent, ROUND(dest_disabled/total, 4) AS dest_disabled_percent, ROUND(rate_high/total, 4) AS rate_high_percent, ROUND(div_fail/total, 4) AS div_fail_percent, ROUND(dest_none/total, 4) AS dest_none_percent, ROUND(lost/total, 4) AS lost_percent, ROUND(dim_err/total, 4) AS dim_err_percent, ROUND(weight_err/total, 4) AS weight_err_percent, ROUND(underutilized/total, 4) AS underutilized_percent, ROUND(unsafe/total, 4) AS unsafe_percent FROM SORTERS ORDER BY start_timestamp;