在MYSQL中连接包含SUM问题的表

时间:2016-06-22 21:24:20

标签: mysql sql join

我总是遇到在连接表上获取SUM的问题,总是有问题,我可以通过运行两个查询得到我需要的结果,我想知道这两个查询是否可以组合成一个连接查询,这里是我的查询和我加入查询的尝试

查询1

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 

以分钟为单位的输出是 271, 281, 279

查询2

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

这里的输出是33.00,36.00,26.75

现在我尝试加入查询

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 

括号是预期的

输出1044(271),1086(281),1215(279)

2 个答案:

答案 0 :(得分:4)

在主查询中使用多个连接时,最终会得到所有表的叉积,因此总和会乘以另一个表中匹配的行数。您需要将总和移动到子查询中。

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

答案 1 :(得分:2)

有一些问题...... bhds_mileagebhds_timecard之间的部分笛卡尔积(交叉积),因为来自一个表的每个细节行(在一个组内)将是&# 34;交叉加入"使用另一个表中的详细信息行。这种情况发生在GROUP BY操作折叠行之前,并计算SUM。这就解释了为什么你会看到"膨胀"值。

解决方法是在内联视图中计算至少一个SUM()聚合...像第一个查询一样完成SUM()/ GROUP BY()。为清楚起见,您可以对两个原始查询执行相同的操作,然后从内联视图中加入结果。

MySQL本身不支持FULL外连接。其中一个表需要是驱动表。例如,我们可以使用_timecard作为驱动表,但这意味着我们必须从_timecard返回给定周的行,以便从_mileage返回相应的行。也就是说,如果_timecard中没有一行,我们就无法从_mileage获取一行。

我们注意到bhds_teacher的加入是外连接。如果我们在ds_id_mileage _timecard之间有一个外键约束,引用_teacher,那么这不一定需要是外连接,我们可以使用内部联接,并使用_teacher作为两个外部联接的驱动表。

另一个问题是SELECT列表中的非聚合...例如DATE_FORMAT((tm_date), '%m/%d/%y')

GROUP BY是年份和星期,因此DATE_FORMAT的值是不确定的......它可能来自组内的任何 tm_date。我们无法保证您获得一周的第一天,即一周内的最早日期或其他任何内容。

此外,WEEK函数的第二个参数被省略,因此默认为default_week_format系统变量。就个人而言,我避免使用YEARWEEKCONCAT函数,并使用更简单的DATE_FORMAT,使用明确包含mode参数的日期格式字符串本周。

如果您想加入"周",则加入谓词应该在"周"价值,而不是一周内的一个不确定日期。

(对于我们不知道的数据可能会有一些特定的限制......如果在给定的一周中_mileage中有行,那么在星期一,我们保证有一个_timecard在更一般的情况下,我们不会有这种保证。)

即使我们确实有这种保证,我们也不能保证SELECT列表中的非聚合不会从星期二_timecard和星期四_里程返回日期...(除非有& #39;某种保证,数据只包含"星期一"日期在_timecard和_mileage上的行。由此可见,非聚合表达式不是连接谓词的可靠表达式。

假设ds_id _teacherds_id上是唯一的,并且_mileage_timecard都有外键SELECT i.last_name , i.first_name , tm.dates , tm.total_hours , mm.total_minutes FROM bhds_teacher i LEFT JOIN ( SELECT t.ds_id , DATE_FORMAT( t.tm_date,'%Y/%U') AS week_ , DATE_FORMAT( MIN(t.tm_date) ,'%m/%d/%y') AS dates , SUM(t.tm_hours) AS total_hours FROM bhds_timecard t WHERE t.tm_date BETWEEN '2016-04-11' AND '2016-04-30' -- < AND t.ds_id = 5 -- < GROUP BY t.ds_id , DATE_FORMAT( t.tm_date,'%Y/%U') -- week ) tm ON tm.ds_id = i.ds_id LEFT JOIN ( SELECT m.ds_id , DATE_FORMAT( m.mil_date,'%Y/%U') AS week_ , DATE_FORMAT( MIN(m.mil_date), '%m/%d/%y' ) AS dates , SUM( m.drive_time ) AS total_minutes FROM bhds_mileage m WHERE m.mil_date BETWEEN '2016-04-11' AND '2016-04-30' -- < AND m.ds_id = 5 -- < GROUP BY m.ds_id , DATE_FORMAT( m.mil_date,'%Y/%U') -- week ) mm ON mm.ds_id = i.ds_id AND mm.week_ = tm.week_ WHERE i.ds_id = 5 -- < ORDER BY i.last_name ASC, tm.dates ASC 引用,那么这样的话:

document.querySelector('shop-app /deep/ shop-button')