2012-08-08 75 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