2011-08-23 89 views
1

我有对象训练,房间和老师。 培训有0个或几个房间。 培训有0或几位教师。sql multiple count

Table training (id,name,town)

Table training_room (id, id_training, id_room, enabled)

Table training_teacher (id, id_training, id_teacher, enabled)

我要链接 和链接使教师的数量谁属于与使房间数目镇培训的列表。

我在哪里可以写入启用字段的条件? 这是我查询的开始。

SELECT training.id, 
     training.town , 
     COUNT (t_r.id_formation), 
     COUNT (t_t.id_formation) 
FROM training 
LEFT JOIN training_room t_r 
LEFT JOIN training_teacher t_t 
WHERE training.town ='X' 
GROUP BY training.id 

非常感谢。

+0

中有什么'formation'表你查询? –

+0

对不起,这是“训练”(以法语形式:)) – Julien

回答

2

假设我已经理解了问题:

SELECT training.id, 
     training.town , 
     COUNT (distinct t_r.id), 
     COUNT (distinct t_t.id) 
FROM training t 
LEFT JOIN training_room t_r ON t.id = t_r.id_training AND t_r.enabled = 'true' 
LEFT JOIN training_teacher t_t ON t.id = t_t.id_training AND t_t.enabled = 'true' 
WHERE training.town ='X' 
GROUP BY training.id 
+0

实际上,在where子句中放置两个'enabled'检查会无意中将'LEFT'连接变为'INNER'连接,因为任何缺失的记录都会返回'这些字段当然不会是'真'的。最好的解决方案是将这些条件放入相应联接语句的“ON”子句中。 –

+0

完美。谢谢StevieG。我不能+1你的答案,因为一个弹出窗口要求我登录,而我已经登录。 – Julien

+0

@Julien:当你有15个以下的代表点时,你无法注册,但你可以[接受](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work)一个答案,如果它确实为你工作。不过,我怀疑目前形式的这个特定答案会返回正确的结果。如果有2个房间和3个教师与培训相匹配,那么两个COUNTs将返回6(2×3)用于该培训。 –

0

这应该有助于

SELECT training.id, 
     training.town, 
     COUNT (SELECT t_r.id_training FROM training_room t_r WHERE t.id = t_r.id_training AND t_r.enabled = 'true') AS training_room_count, 
     COUNT (SELECT t_t.id_training FROM training_teacher t_t WHERE t.id = t_t.id_training AND t_t.enabled = 'true') AS training_teacher_count 
FROM training t 
WHERE training.town ='X' 

,或者你可以做(​​做整体同样的事情):

SELECT training.id, 
     training.town , 
     COUNT (t_r.id_training) AS training_room_count, 
     COUNT (t_t.id_training) AS training_teacher_count 
FROM training t 
LEFT JOIN training_room t_r ON t.id = t_r.id_training 
LEFT JOIN training_teacher t_t ON t.id = t_t.id_training 
WHERE training.town ='X' 
    AND COALESCE(t_r.enabled, 'true') = 'true' 
    AND COALESCE(t_t.enabled, 'true') = 'true' 
GROUP BY training.id, training.town 
+0

我相信你在SQL Server中测试过你的脚本,至少是第二个包含'ISNULL'的脚本。 MySQL的等价物是'IFNULL',但你也可以使用两个服务器产品都支持的'COALESCE'。 –

+0

我错过了MySQL标签,同时在看另一个问题,谢谢 – Seph

2

我会建议你不要像这样加入你的餐桌。正如我在对StieveG的回答的评论中所说的那样,通过以这种方式加入表格,您最终可以得到有时称为mini-Cartesian product的结果。这里有一个说明解释,以防万一你需要它。

例如,你有2行中training_roomtraining匹配行,3行中training_teacher匹配相同training行。首先将会议室桌子连接在一起,结果,我们得到了两排上述训练。它们包含两个不同的房间,但请记住它们也包含相同的训练,重复。

为什么这很重要?因为随后加入的training_teacher完成而非仅针对训练表,但针对加入的结果为trainingtraining_room。它仅仅发生在连接条件仅包含training中的一列,但该列将取自上一次连接,不完全来自该表。

因此,您将与每个重复培训进行匹配,并且每个人都会根据匹配教师的数量获得另外三个培训。

所以,最后一次培训会产生6行,其中2个房间会重复3次,3个老师会重复两次。因此,这两个COUNT将返回6

一个可能的解决方案可能是简单地改变计数的表达式是这样的:

… 
COUNT (DISTINCT t_r.id), 
COUNT (DISTINCT t_t.id) 
… 

这不会阻止产生笛卡尔积的加入,但至少结果是一致的(提供的培训就可以从来没有重复的房间或教师)。

但是,如果我是你,我可能会解决这个问题不同,在子选择分组并加入training表的子查询的结果,就像这样:

SELECT 
    t.id, 
    t.town, 
    IFNULL(tr.cnt) AS room_count, 
    IFNULL(tt.cnt) AS teacher_count 
FROM training AS t 

    LEFT JOIN (
    SELECT 
     id_training, 
     COUNT(*) AS cnt 
    FROM training_room 
    WHERE enabled = 'true' 
    GROUP BY id_training 
) AS tr ON t.id = tr.id_training 

    LEFT JOIN (
    SELECT 
     id_training, 
     COUNT(*) AS cnt 
    FROM training_teacher 
    WHERE enabled = 'true' 
    GROUP BY id_training 
) AS tt ON t.id = tt.id_training 

WHERE t.town = 'X'