我知道有幾十個「選擇第一行」的問題,但我有一個有趣的情況,我認爲我不能用LIMIT修復。MySQL選擇表格的第一行
我試圖顯示在開始和結束日期之間可用的連續N天可用的屬性。我很親密。
我打算把這個查詢拆分成多個位,因爲它是一個很大的問題。
阻止日期(第1部分)
(SELECT c.id,c.property_id,c.start_date,c.end_date FROM property_calendar as c WHERE c.start_date <= '.$endQuote.' AND c.end_date >= '.$startQuote)
UNION
(SELECT l.id,l.property_id,l.start_date,l.end_date FROM lease as l WHERE l.start_date <= '.$endQuote.' AND l.end_date >= '.$startQuote.' AND l.status < 6)
ORDER BY start_date ASC
這一聯合搗毀了兩個日曆在一起,便於比較。每個條目都有一個start_date和end_date。落在(包括開始和結束)之間的日期被認爲是不可用的。
計算器(部分2)
SELECT IFNULL(DATEDIFF(IFNULL(ct2.start_date, '2015-12-09'), ct1.end_date) - 1, 0) as open_days,
(DATEDIFF(ct1.start_date, '2015-12-01') - 1) as first_open,
IFNULL(DATEDIFF(LEAST(ct1.end_date, '2015-12-09'), GREATEST(ct1.start_date, '2015-12-01')) + 1, 0) as blocked_days,
ct1.property_id as property_id
FROM ({blocked dates}) as ct1
LEFT JOIN ({blocked dates}) as ct2 ON ct2.property_id = ct1.property_id AND ct2.start_date > ct1.end_date
GROUP BY ct1.property_id,ct1.start_date
這需要在第1部分表,並將其加入到自己讓我們來比較先前阻斷日期下。 open_days
是每個阻止日期之間的負面空間。 只是ct1
被封鎖了多少天。造成問題的部分是first_open
。
此查詢有效,除非第一個阻止日期發生在用戶開始日期之後。在那種情況下,我們錯過了計算那些開放的前幾天的情況。所以,我的解決方案的一部分是添加first_open
,它只是簡單地計算DATEDIFF(ct1.start_date, {the given start date})
。
最後一筆
SELECT cj.property_id,
IFNULL(SUM(cj.blocked_days), 0) as total_blocked,
IFNULL(GREATEST(MAX(cj.open_days), MAX(cj.first_open)), 0) as consec_days_avail
FROM {the calculator} as cj
正如我所說的,這裏是我的問題。選擇MAX(cj.first_open)
顯然不適合我。沒有唯一的ID可以通過,因爲可用性是ID可能發生碰撞的兩個表的組合。而且MySQL沒有使用FIRST()
函數。但基本上我只關心最大的可用連續日期,因爲我們不關心連續幾天發生的情況,只是它們發生在用戶的開始日期和結束日期之間。
那麼有沒有更好的方法來構造這個查詢?我試圖探索方法來獲得計算器中的第一行實際上DATEDIFF()
和其他所有的都是0,但我使用IF(ct1.start_date = MIN(ct1.start_date), DATEDIFF(...), 0)
的問題是,前兩行將有一個非0值,我只想第一個。我認爲這與我如何加入表格本身有關。
任何幫助表示讚賞。謝謝。
編輯
對於好奇各方這裏的查詢都在一塊。這正在按照預期工作(據我所知)。現在我只是想知道是否有更好的方法或者是否有任何提示。
SELECT p.* as id, IFNULL(cd.total_blocked, 0) as total_blocked, IFNULL(cd.consec_days_avail, 0) as consec_days_avail
FROM properties AS p
LEFT JOIN (
SELECT cj.property_id, IFNULL(SUM(cj.blocked_days), 0) as total_blocked, IFNULL(GREATEST(MAX(cj.open_days), MAX(cj.first_open)), 0) as consec_days_avail
FROM (
SELECT IFNULL(DATEDIFF(IFNULL(ct2.start_date2, {user_end_date}), ct1.end_date) - 1, 0) as open_days,IF(MIN(ct1.start_date) = ct1.start_date, DATEDIFF(ct1.start_date, {user_start_date}), 0) as first_open,IFNULL(DATEDIFF(LEAST(ct1.end_date, {user_end_date}), GREATEST(ct1.start_date, {user_start_date})) + 1, 0) as blocked_days,ct1.property_id as property_id
FROM ((SELECT c.id,c.property_id,c.start_date,c.end_date FROM property_calendar as c WHERE c.start_date <= {user_end_date} AND c.end_date >= {user_start_date}) UNION (SELECT l.id,l.property_id,l.start_date,l.end_date FROM lease as l WHERE l.start_date <= {user_end_date} AND l.end_date >= {user_start_date} AND l.status < 6) ORDER BY start_date ASC) as ct1
LEFT JOIN ((SELECT c.id as id2,c.property_id as property_id2,c.start_date as start_date2,c.end_date as end_date2 FROM property_calendar as c WHERE c.start_date <= {user_end_date} AND c.end_date >= {user_start_date}) UNION (SELECT l.id,l.property_id,l.start_date,l.end_date FROM lease as l WHERE l.start_date <= {user_end_date} AND l.end_date >= {user_start_date} AND l.status < 6) ORDER BY start_date2 ASC) as ct2 ON ct2.property_id2 = ct1.property_id AND ct2.start_date2 > ct1.end_date
GROUP BY ct1.property_id,ct1.start_date) as cj
GROUP BY cj.property_id) as cd on cd.property_id = p.id
WHERE p.state = 1
GROUP BY p.id
HAVING total_blocked < DATEDIFF({user_end_date},{user_start_date}) + 1 AND (consec_days_avail >= {user_days_avail} OR total_blocked = 0)
ORDER BY p.id asc
我寧願只是一些數據和期望的結果 - 至少這表明你正在嘗試 – Strawberry
@Strawberry我得到你。對不起,我已經花了好幾天的時間研究這個問題,並且我認爲這將是一個更簡單的解讀。我會把小提琴放在一起並更新。 – Squeegy
只是一個珍聞。 'MIN(datestamp)'和'MAX(datestamp)'作爲聚合函數正常工作,返回第一個和最後一個日期戳記。另外,你可能會濫用令人討厭的非標準MySQL擴展到'GROUP BY',這可能會讓你瘋狂。 https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html –