在处理资产调度的应用程序中考虑下表:
date group_id free_spots
2011-01-01 1 0
2011-01-01 2 0
2011-01-08 1 1
2011-01-08 2 0
2011-01-15 1 1
2011-01-15 2 1
2011-01-22 1 2
2011-01-22 2 2
2011-01-29 1 1
2011-01-29 2 0
2011-02-05 1 0
2011-02-05 2 1
2011-02-12 1 0
2011-02-12 2 1
2011-02-19 1 0
2011-02-19 2 0
使用不同的表格使用相当昂贵的查询(~100ms)将此信息放在一起。结果可以放入临时表,也可以直接内联使用。
我想要的是找到提供点的第一个日期(free_spots> 0)。然后在同一个记录中,我想要最后一个日期,即一个点。所有这些都按group_id分组。
为了说明,在给定的示例表中,我希望得到以下输出:
group_id start_date end_date
1 2011-01-08 2011-01-29
2 2011-01-15 2011-01-22
2 2011-02-05 2011-02-12
现在,我已经想出了一个粗略的解决方案。使用给定的表我会:
然而,这似乎是不可能的,因为我不能再次使用相同的子查询来查找后继或前面的记录。与临时表相同的处理。这些我不能打开并重复使用不止一次。
(*前置或成功取决于日期。对于每个组,日期相等,连续且均匀(但任意)间隔。通常为7或14天)
答案 0 :(得分:1)
它可能效率不高,但它适用于您的数据。 (请注意,如果需要,我为日期范围添加了WHERE约束):
SELECT group_id,MIN(`date`) AS start_date,
(SELECT `date` FROM Slots s3
WHERE s3.group_id=t.group_id
AND s3.`date`<t.next_stop_date
AND s3.free_spots > 0
ORDER BY s3.`date`DESC
LIMIT 1) as end_date
FROM
(SELECT s1.*, MIN(s2.`date`) AS next_stop_date
FROM
Slots s1 LEFT JOIN Slots s2
ON s2.`date` > s1.date AND s1.group_id=s2.group_ID AND s2.free_spots = 0
WHERE s1.free_spots > 0
GROUP BY s1.group_id, s1.`date`
ORDER BY s1.group_id ASC, s1.`date` ASC
) AS t
GROUP BY group_id, next_stop_date
答案 1 :(得分:1)
我可以在SQL Server中编写它,并且知道它可以转换为MySQL。首先,我将为您提供SQL Server版本,然后对下面的翻译进行推动。我本来可以跳过这个问题,但一开始并没有意识到它是针对MySQL的。
这可以容忍日期之间任意长度的可变间隙。
WITH IDs AS (
SELECT *, Row_Number() OVER (PARTITION BY GroupID ORDER BY AvailableDate) ID
FROM Availability
), Data AS (
SELECT
GroupID,
AvailableDate,
ID - Dense_Rank() OVER (PARTITION BY GroupID ORDER BY ID) G
FROM IDs
WHERE FreeSpots > 0
)
SELECT
GroupID,
Min(AvailableDate) FromDate,
Max(AvailableDate) ToDate
FROM Data
GROUP BY GroupID, G;
这是设置脚本:
CREATE TABLE Availability (
AvailableDate datetime,
GroupID tinyint,
FreeSpots tinyint
)
INSERT Availability
SELECT '20110101', 1, 0
UNION ALL SELECT '20110101', 2, 0
UNION ALL SELECT '20110108', 1, 1
UNION ALL SELECT '20110108', 2, 0
UNION ALL SELECT '20110115', 1, 1
UNION ALL SELECT '20110115', 2, 1
UNION ALL SELECT '20110122', 1, 2
UNION ALL SELECT '20110122', 2, 2
UNION ALL SELECT '20110129', 1, 1
UNION ALL SELECT '20110129', 2, 0
UNION ALL SELECT '20110205', 1, 0
UNION ALL SELECT '20110205', 2, 1
UNION ALL SELECT '20110212', 1, 0
UNION ALL SELECT '20110212', 2, 1
UNION ALL SELECT '20110219', 1, 0
UNION ALL SELECT '20110219', 2, 0
MySQL翻译
以下应该等同于我的第一个CTE(公用表表达式),模拟Row_Number()函数。稍微调整一下,你可以使用它作为派生表来进行第二次CTE来模拟Dense_Rank(),并且你有一个有效的查询!
SELECT
GroupID,
AvailableDate,
FreeSpots,
@rownum=CASE WHEN @grpset <> GroupID THEN 0 ELSE @rownum + 1 END AS rownum,
@grpset=GroupID AS grpset
FROM
(SELECT @grpset= -1) g,
(SELECT @rownum:= -1) r,
(SELECT *
FROM Availability
ORDER BY GroupID, AvailableDate
) a
我知道MySQL不是一点点,所以我在网上举个例子。这个应该工作,但我可以让语法错误在那里。如果此查询有效并且您需要更多帮助,请告诉我,我将尝试将其用于MySQL的完整查询。虽然@Quassnoi出现了,但你很快就会拥有它!
答案 2 :(得分:0)
查询不是很好但似乎有效:
SELECT *
FROM (
SELECT a.group_id, a.`date` AS
start_date , max( b.`date` ) AS
end_date
FROM test AS a
LEFT JOIN test AS b ON a.group_id = b.group_id
AND b.free_spots >0
AND a.date < b.date
WHERE a.free_spots >0
AND (
SELECT count( * )
FROM test AS c
WHERE c.group_id = a.group_id
AND c.date > a.date
AND c.date < b.date
AND free_spots =0
) =0
GROUP BY group_id,
start_date
) AS d
WHERE end_date IS NOT NULL
GROUP BY d.end_date
ORDER BY `d`.`group_id` ASC
答案 3 :(得分:-1)
我想不出一个超级简单的方法来做到这一点。这是我能想到的一种方式的草图(使用多个查询)
create temporary table temp1 select group_id,min(date) as start_date from table1 where free_spots>0 group by group_id
alter table temp1 add column end_date datetime default null
create temporary table
{TEMP2 {1}}
然后在while循环中(使用一些编程语言),我会执行以下操作,直到temp2为空。你应该在循环的每一步将日期增加到第二天(称之为$ curDate):
select * from table1 where free_spots>0
您可以在每次查询后检查是否有0行更新。如果他们是,你已经完成了,你可以打破while循环。
答案 4 :(得分:-1)
这适用于sql server(如果我正确理解你的问题)。它也适用于mysql:
从(选择a.group_id,a.min_date,b.max_date) 选择s1.group_id,min(s1.date)min_date 从斑点s1 分组由s1.group_id)a 内连接 (通过group_id从spot group中选择group_id,max(date)max_date)b 在a.group_id = b.group_id
上