2012-08-08 72 views
3

一个看似简单的查询,我不能让工作...Postsgresql OUTER JOIN并不如预期运行

两个表室和可用性

Room   [id, title, beds] 
Availability [room_id, date, bed_delta] 

的房间都有一个固定的,最大床位数可用。可用性说明Room.beds被修改(向上或向下)的时间段。

我想构建一个查询,对于给定的房间和给定的开始日期和持续时间将总和床位可用。

例如:

  • 室1通常具有2个床
  • 1小时,1床取
  • 预期的结果是1

如果可用性另一个周期是补充说,重叠这个日期时间范围,并进一步减少1,预期结果为0.

感觉就像是一个相对简单的查询:

  • LEFT OUTER JOIN客房供应情况而对ROOM_ID
  • 约束日期
  • 选择Room.beds - 总和(Availability.bed_delta)

SELECT r.beds - coalesce(sum(a.bed_delta), 0) as beds_free 
FROM room r 
LEFT OUTER JOIN availability a ON (r.id = a.room_id) 
WHERE date = '2012-01-01 09:00:00+01:00' 
AND r.id = 2 
GROUP BY r.id; 

如果在availability中有匹配的行,此查询才会返回。我的预期是对的房间ID ==单排2

回答

7

的问题是你WHERE条款的这一部分:

WHERE date = '2012-01-01 09:00:00+01:00' 

外部联接只有“允许”行为其加盟条件(这里,r.id = a.room_id)失败;在WHERE子句中施加的右侧表上的其他限制仍然可以从结果中排除整行。是的,这很棘手。

解决方案:移动date约束到连接条件:

LEFT OUTER JOIN availability a ON (r.id = a.room_id AND date = '2012-01-01 09:00:00+01:00') 

(是的,令人惊讶的是你能坚持在那里任意条件,和你现在发现,(对于外部联接)这样可以!是必要的)

或者,你可以使用相关子查询找到的所有Availability记录匹配的兴趣Room的总和:

SELECT r.beds - coalesce((
    SELECT sum(a.bed_delta) 
    FROM availability a 
    WHERE a.room_id = r.id 
    AND date = '2012-01-01 09:00:00+01:00' 
), 0) as beds_free 
FROM room r 
WHERE r.id = 2; 

我发现子查询比GROUP ed外连接更容易理解,但是YMMV。

+0

干杯,非常有用。恐怕我被多年的MySQL使用所污染。对相关子查询的性能影响有何评论? – 2012-08-08 17:15:02

+0

不客气。对于我已经知道的性能再也没有硬性规定。查询规划人员会考虑许多不同的可能计划,并知道将查询转换为其他等效查询的许多方法,因此它可能会为两者生成相同的查询计划。使用'EXPLAIN'或'EXPLAIN ANALYSE'来看看它认为最好的。 – 2012-08-08 18:01:35

+1

@RobCowie如果这个答案是正确的,就像你期望的那样意味着请接受这个答案,这对他人也有帮助。 – gvgvgvijayan 2014-09-25 11:21:37