2015-12-09 69 views
0

我知道有几十个“选择第一行”的问题,但我有一个有趣的情况,我认为我不能用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 
+0

我宁愿只是一些数据和期望的结果 - 至少这表明你正在尝试 – Strawberry

+0

@Strawberry我得到你。对不起,我已经花了好几天的时间研究这个问题,并且我认为这将是一个更简单的解读。我会把小提琴放在一起并更新。 – Squeegy

+0

只是一个珍闻。 'MIN(datestamp)'和'MAX(datestamp)'作为聚合函数正常工作,返回第一个和最后一个日期戳记。另外,你可能会滥用令人讨厌的非标准MySQL扩展到'GROUP BY',这可能会让你疯狂。 https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html –

回答

0

您好!
该问题的解决方案是一个名为MIN一个SQL函数和它的工作原理是这样的:

SELECT MIN(primary_key_column),the_other_columns FROM table_name; 

正如你所看到的,在查询的开始(字选择后)你刚才调用函数MIN并给出也是表的主键的列。
因为这个函数找到表格的最小寄存器。我的意思是如果选择的列是INTEGER,它将会找到该列中最小数字的行

如果您也使用PHP还有就是让那个叫mysql_fetch_array,对于这个功能,你必须首先做一个归仁一个名为的mysql_query另一PHP功能和使用它像这样

$myQuery = mysql_query("SELECT * FROM tablename",$conectionVariable)or die(mysql_error); 

然后我们调用函数mysql_fetch_功能阵列并将其存储在一个变量是这样的:

$myQueryArray = mysql_getch_array($myQuery)or die(mysql_error()); 

现在使用它,你必须把它像一个正常的数组:

$myQueryArray['COLUMN_NAME'] 并返回你列的值是第一行

那么,这一切。

谢谢!