MYSQL中存在求和问题的联接表

MYSQL中存在求和问题的联接表,mysql,sql,join,Mysql,Sql,Join,我总是很难在联接表上求和,总是有一个问题,我可以通过运行两个查询来获得所需的结果,我想知道这两个查询是否可以组合成一个联接查询,下面是我的查询和我尝试联接查询的结果 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_teacher

我总是很难在联接表上求和,总是有一个问题,我可以通过运行两个查询来获得所需的结果,我想知道这两个查询是否可以组合成一个联接查询,下面是我的查询和我尝试联接查询的结果

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 
问题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)

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

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

有几个问题。。。
bhds\u里程
bhds\u时间卡
之间的部分笛卡尔积(叉积),因为一个表中的每个细节行(组内)将与另一个表中的细节行“交叉连接”。这发生在GROUPBY操作折叠行并计算总和之前。这就解释了为什么你会看到“膨胀”的价值观

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

MySQL本机不支持完全外部联接。其中一张桌子需要是驾驶台。例如,我们可以使用
\u timecard
作为驾驶表,但这意味着我们必须从
\u timecard
返回给定一周内的一行,以便从\u milies返回相应的行。也就是说,如果没有
\u timecard
中的一行,我们就无法从
\u milies
中获取一行

我们注意到,
bhds\u teacher
的连接是一个外部连接。如果我们在
\u里程
\u时间卡
中的
ds\u id
之间都有一个外键约束,引用
\u教师
,那么这不一定需要是外部联接,我们可以使用内部联接,并使用
\u教师
作为两个外部联接的驱动表

另一个问题是选择列表中的非聚合。。。e、 g.
DATE\u格式((tm\u日期),“%m/%d/%y”)

GROUP BY为“年”和“周”,因此DATE_格式的值不确定。。。它可能来自集团内的任何
tm\u日期
。不能保证你会得到一周的第一天,一周内最早的日期或者其他什么

此外,省略了
WEEK
函数的第二个参数,因此将默认为
default\u WEEK\u格式
系统变量。就我个人而言,我会避免使用
YEAR
WEEK
CONCAT
函数,使用一个更简单的
DATE\u格式
,使用一个明确包含周模式参数的日期格式字符串

如果您想在“week”上加入,那么连接谓词应该在“week”值上,而不是在一周内的一个不确定日期上

(数据上可能存在一些我们不知道的特定约束条件……如果在给定的一周内,在一个星期一,里程数中有行,那么我们保证在同一个星期一有一张里程卡。在更一般的情况下,我们没有这种保证。)

即使我们有这样的保证,我们也不能保证选择列表中的非聚合不会返回星期二时间卡和星期四里程的日期。。。(除非有某种形式的保证,数据将只包括在_timecard和_miliege上带有“星期一”日期的行)。否则,非聚合表达式就不是连接谓词的可靠表达式

假设
ds\u id
\u教师
上是唯一的,并且由
\u里程
\u时间卡
中的外键
ds\u id
引用,则类似如下:

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
选择姓氏
,我的名字
,tm.dates
,tm.总时数
,毫米,总分钟
来自bhdsèU教师i
左边
加入(选择t.ds\U id
,日期格式(t.tm_日期,“%Y/%U”)为周_
,日期格式(MIN(t.tm\u日期),“%m/%d/%y”)作为日期
,总和(总时数)为总时数
来自bhds\U时间卡t
其中t.tm_日期介于“2016-04-11”和“2016-04-30”之间--<
t.ds_id=5--<
团体
由t.D.U.id提供
,日期格式(t.tm_日期,%Y/%U')--周
)商标
在tm.ds\u id=i.ds\u id上
左边
加入(选择m.ds\U id
,日期格式(百万美元日期,%Y/%U')为周_
,日期格式(最小(百万分之日),“%m/%d/%y”)作为日期
,总和(m.行驶时间)为总分钟
从bhds_里程m
其中,m.mil_日期介于“2016-04-11”和“2016-04-30”之间--<
m.ds_id=5--<
团体
m.D.U.id
,日期格式(百万美元日期,%Y/%U')--周
)嗯
在mm.ds\u id=i.ds\u id上
和mm.week=tm.week_
其中i.ds_id=5--<
命令
按i.姓ASC,tm.日期ASC
尝试:
选择。。。from()q1内部连接()q2 on…order by…