Home >Database >Mysql Tutorial >How to Correctly Sum Aggregate Values from Joined Tables in MySQL to Avoid Cross Product Issues?
Using SUM aggregation to join tables in MySQL: solving cross-product problems
Combining two queries using the SUM aggregate function often presents challenges in MySQL. Cross products between tables can cause incorrect SUM values. To solve this problem, the SUM function needs to be encapsulated into a subquery.
Consider the following example where two queries retrieve SUM(drive_time) and SUM(tm_hours) respectively for a specific date and a teacher ID of 5:
Query 1:
<code class="language-sql">SELECT last_name, first_name, DATE_FORMAT(mil_date, '%m/%d/%y') AS dates, SUM(drive_time) MINUTES FROM bhds_mileage LEFT JOIN bhds_teachers i ON i.ds_id = bhds_mileage.ds_id WHERE mil_date BETWEEN '2016-04-11' AND '2016-04-30' AND bhds_mileage.ds_id = 5 GROUP BY CONCAT(YEAR(mil_date), '/', WEEK(mil_date)), bhds_mileage.ds_id ORDER BY last_name ASC, dates ASC</code>
Query 2:
<code class="language-sql">SELECT last_name, first_name, DATE_FORMAT(tm_date, '%m/%d/%y') AS dates, SUM(tm_hours) total FROM bhds_timecard LEFT JOIN bhds_teachers i ON i.ds_id = bhds_timecard.ds_id WHERE tm_date BETWEEN '2016-04-11' AND '2016-04-30' AND bhds_timecard.ds_id = 5 GROUP BY CONCAT(YEAR(tm_date), '/', WEEK(tm_date)), bhds_timecard.ds_id ORDER BY last_name ASC, dates ASC</code>
Simple connection attempt:
To combine these queries, a simple way is to concatenate them as follows:
<code class="language-sql">SELECT last_name, first_name, DATE_FORMAT(tm_date, '%m/%d/%y') AS dates, SUM(tm_hours) total, SUM(drive_time) MINUTES FROM bhds_timecard LEFT JOIN bhds_teachers i ON i.ds_id = bhds_timecard.ds_id LEFT JOIN bhds_mileage ON DATE_FORMAT(bhds_timecard.tm_date, '%m/%d/%y') = DATE_FORMAT(bhds_mileage.mil_date, '%m/%d/%y') AND bhds_timecard.ds_id = bhds_mileage.ds_id WHERE tm_date BETWEEN '2016-04-11' AND '2016-04-30' AND bhds_timecard.ds_id = 5 GROUP BY CONCAT(YEAR(tm_date), '/', WEEK(tm_date)), bhds_timecard.ds_id </code>
However, this approach creates cross products between tables, resulting in incorrect SUM values.
Solution:
To get correct SUM values, move the SUM aggregate function into a subquery. This prevents cross-product problems and ensures that SUM values are only calculated in relevant rows:
<code class="language-sql">SELECT last_name, first_name, DATE_FORMAT(LEAST(mil_date, tm_date), '%m/%d/%y') AS dates, total, minutes FROM bhds_teachers AS i LEFT JOIN ( SELECT ds_id, YEARWEEK(mil_date) AS week, MIN(mil_date) AS mil_date, SUM(drive_time) AS minutes FROM bhds_mileage WHERE mil_date BETWEEN '2016-04-11' AND '2016-04-30' AND bhds_mileage.ds_id = 5 GROUP BY ds_id, week ) AS m ON m.ds_id = i.ds_id LEFT JOIN ( SELECT ds_id, YEARWEEK(tm_date) AS week, MIN(tm_date) AS tm_date, SUM(tm_hours) AS total FROM bhds_timecard WHERE tm_date BETWEEN '2016-04-11' AND '2016-04-30' AND bhds_timecard.ds_id = 5 GROUP BY ds_id, week ) AS t ON t.ds_id = i.ds_id AND t.week = m.week </code>
The above is the detailed content of How to Correctly Sum Aggregate Values from Joined Tables in MySQL to Avoid Cross Product Issues?. For more information, please follow other related articles on the PHP Chinese website!