简体   繁体   中英

MySQL joins involving aggregate data

What I require is a SQL query which can report on data from aggregate and singular tables. The current database I have is as follows.

CREATE TABLE IF NOT EXISTS `faults_days` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `employee_id` int(11) NOT NULL,
    `day_date` date NOT NULL,
    `actioned_calls_out` int(11) NOT NULL,
    `actioned_calls_in` int(11) NOT NULL,
    `actioned_tickets` int(11) NOT NULL,
)

CREATE TABLE IF NOT EXISTS `faults_departments` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(40) NOT NULL,
)

CREATE TABLE IF NOT EXISTS `faults_employees` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `team_id` int(11) NOT NULL,
    `name` varchar(127) NOT NULL,
)

CREATE TABLE IF NOT EXISTS `faults_qos` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `qos_date` datetime NOT NULL,
    `employee_id` int(11) NOT NULL,
    `score` double NOT NULL,
)

CREATE TABLE IF NOT EXISTS `faults_teams` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `department_id` int(11) NOT NULL,
    `name` varchar(40) NOT NULL,
)

A row in Day tracks a single employee's performance for a single day (number of calls taken, number of tickets actioned). A Qos is a measure of an employee's quality on a day (there can be more than one Qos per day - what I need to obtain is the average score). Also, a Qos can be performed on a day where the employee has no performance entry in the database, and this will still need to be shown on the report.

The required end result is 4 reports, which show the employee performance grouped by different columns. A breakdown of a single employee's performance per day, an employee's total performance over a period of time, a team's performance over a period of time, and a whole department's performance over a period of time.

My problem, is that my current queries are a little convoluted, and require two separate queries for the Day data, and the Qos data. My PHP application then combines the data before outputting the report. What I would like, is a single query which returns both total performance, and average quality scores.

The current queries I have to show employee performances are:

SELECT  
    `Employee`.`name` ,  
    `Team`.`name` , 
    `Department`.`name` , 
    SUM(  `Day`.`actioned_calls_in` ) + SUM(  `Day`.`actioned_calls_out` ) , 
    SUM(  `Day`.`actioned_tickets` ) 
FROM  
    `faults_days` AS  `Day` 
JOIN  
    `faults_employees` AS  `Employee` ON  `Day`.`employee_id` =  `Employee`.`id` 
JOIN  
    `faults_teams` AS  `Team` ON  `Employee`.`team_id` =  `Team`.`id` 
JOIN  
    `faults_departments` AS  `Department` ON  `Team`.`department_id` =  `Department`.`id` 
WHERE  
    `Day`.`day_date` >=  '2011-06-01'
    AND  `Day`.`day_date` <=  '2011-06-07'
GROUP BY  `Employee`.`id` 
WITH ROLLUP

and

SELECT  
    `Employee`.`name` ,  
    `Team`.`name` ,  
    `Department`.`name` , 
    COUNT(  `Qos`.`score` ) , 
    AVG(  `Qos`.`score` ) 
FROM  
    `faults_qos` AS  `Qos` 
JOIN  
    `faults_employees` AS  `Employee` ON  `Qos`.`employee_id` =  `Employee`.`id` 
JOIN  
    `faults_teams` AS  `Team` ON  `Employee`.`team_id` =  `Team`.`id` 
JOIN  
    `faults_departments` AS  `Department` ON  `Team`.`department_id` =  `Department`.`id` 
WHERE  
    `Qos`.`qos_date` >=  '2011-06-01'
    AND  `Qos`.`qos_date` <=  '2011-06-07'
GROUP BY  `Employee`.`id` 
WITH ROLLUP

I have also tried simply joining the Qos table, but because it returns multiple rows it messes up the SUM() totals, and also has problems due to the missing FULL OUTER JOIN functionality.

EDIT: I've made some small progress with this. It looks like using subqueries is the way to go, but everything I'm doing is pure guesswork. Here's what I've got so far, its only showing a row if there's an entry in both the Day and Qos tables, which is not what I want, and I've no idea how to expand it to include the various groupings described above.

SELECT  
    `Employee`.`name` ,  
    `Team`.`name` , 
    `Department`.`name`,
    `Day`.`Calls`,
    `Day`.`Tickets`,
    `Qos`.`NumQos`,
    `Qos`.`Score`
FROM `faults_employees` AS  `Employee` 
JOIN  
    `faults_teams` AS  `Team` ON  `Employee`.`team_id` =  `Team`.`id` 
JOIN  
    `faults_departments` AS  `Department` ON  `Team`.`department_id` =  `Department`.`id` 
JOIN
    (SELECT
        `Day`.`employee_id` AS `eid`,
        SUM(`Day`.`actioned_calls_in`) + SUM(`Day`.`actioned_calls_out`) AS `Calls`, 
        SUM(`Day`.`actioned_tickets`) AS `Tickets`
    FROM `faults_days` AS `Day`
    WHERE
        `Day`.`day_date` = '2011-03-02'
    GROUP BY `Day`.`employee_id`
    ) AS `Day`
    ON `Day`.`eid` = `Employee`.`id`
JOIN
    (SELECT
        `Qos`.`employee_id` AS qid,
        COUNT(`Qos`.`id`) AS `NumQos`,
        AVG(`Qos`.`score`) AS `Score`
    FROM `faults_qos` AS `Qos`
    WHERE
        `Qos`.`qos_date` = '2011-03-02'
    GROUP BY `Qos`.`employee_id`
    ) AS `Qos`
    ON `Qos`.`qid` = `Employee`.`id`
GROUP BY `Employee`.`id`

You do want the left join s on the fault_qos and fault_days subqueries. That's what will give you a result even if there isn't a corresponding row in one or both. A left join says that the value is necessary in the table(s) to the left of the join that are involved in the join but not the one on the right. I haven't tested this, and it's late, so I might not be thinking clearly, but if you change your query to this it should work:

SELECT  
    `Employee`.`name` ,  
    `Team`.`name` , 
    `Department`.`name`,
    `Day`.`Calls`,
    `Day`.`Tickets`,
    `Qos`.`NumQos`,
    `Qos`.`Score`
FROM `faults_employees` AS  `Employee` 
JOIN  
    `faults_teams` AS  `Team` ON  `Employee`.`team_id` =  `Team`.`id` 
JOIN  
    `faults_departments` AS  `Department` ON  `Team`.`department_id` =  `Department`.`id` 
LEFT JOIN
    (SELECT
        `Day`.`employee_id` AS `eid`,
        SUM(`Day`.`actioned_calls_in`) + SUM(`Day`.`actioned_calls_out`) AS `Calls`, 
        SUM(`Day`.`actioned_tickets`) AS `Tickets`
    FROM `faults_days` AS `Day`
    WHERE
        `Day`.`day_date` = '2011-03-02'
    GROUP BY `Day`.`employee_id`
    ) AS `Day`
    ON `Day`.`eid` = `Employee`.`id`
LEFT JOIN
    (SELECT
        `Qos`.`employee_id` AS qid,
        COUNT(`Qos`.`id`) AS `NumQos`,
        AVG(`Qos`.`score`) AS `Score`
    FROM `faults_qos` AS `Qos`
    WHERE
        `Qos`.`qos_date` = '2011-03-02'
    GROUP BY `Qos`.`employee_id`
    ) AS `Qos`
    ON `Qos`.`qid` = `Employee`.`id`
GROUP BY `Employee`.`id`

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM