我有一个系统,允许用户将特定文件分配给过去或现在的日期。限制是他们每个用户每天只能上传一个文件。当用户上传文件时,日期字段必须默认为当前日期,当该日期不可用时,它将以DESC顺序显示过去的第一个可用日期。以下是相关字段名称。
file_id (INT - INDEX - AUTO INCREMENT)
user_id (INT - may index this)
upload_date (INT - stores date as a unix timestamp)
我真正找到的唯一解决方案是按日期将它们全部构建到DESC中的数组并循环直到我找到一个空槽。但是,我觉得如果用户已经填满了过去的一千天,这可能会导致速度问题。我觉得我忽视了一个简单的解决方案。
请注意:出于这样或那样的原因,他们将Date存储为Unix时间戳,我理解其缺点,我不担心此时的更正。
答案 0 :(得分:1)
要获取尚未使用的最新日期:
select user_id, max(date) - 1
from (select ud.*,
(select max(date) from upload_date ud2 where ud2.user_id = ud.user_id and ud2.date < ud.date
) as prevdate
from upload_date ud
) ud
where date(from_unixtime(ud.prevdate)) <> date(from_unixtime(ud.date)) - 1 or
ud.prevdate is null
group by user_id
此查询首先使用相关子查询获取任何给定日期的上一个日期。然后,它将时间值转换为日期,并选择上一个日期有间隙的任何行。日期减去1的最大值是您要查找的日期。
此SQL未经测试,因此可能存在语法错误。
答案 1 :(得分:1)
解决此问题的一种方法是使用经典的“返回缺失行”查询。基本上,要从数据库返回“丢失”行,您需要一种方法来生成“缺失”行。
要构建这样的查询,我们可以从:
开始SELECT MAX(t.upload_date)
FROM mytable t
WHERE t.upload_date <= NOW()
AND t.user = 'someuser'
这是初始日期,我们将从后面开始工作。
对于“每天一个”的要求,您可能希望将该upload_date截断为午夜,至少对于此查询。现在,我们假设SELECT列表中的表达式已被截断,以说明方法,而不会陷入处理unix时间戳的细节中。
生成日期的降序列表,从上一个查询检索到的初始日期开始......
SELECT s.upload_date - INTERVAL n.d DAY AS available_date
FROM ( SELECT MAX(t.upload_date) AS upload_date
FROM mytable t
WHERE t.upload_date <= NOW()
AND t.user = 'someuser'
) s
CROSS
JOIN ( SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) n
ORDER BY n.d DESC
通过该结果,我们可以使用反连接模式来查找尚未使用的日期。这是一个LEFT JOIN和一个抛出匹配行的谓词:
SELECT s.upload_date - INTERVAL n.d DAY AS available_date
FROM ( SELECT MAX(t.upload_date) AS upload_date
FROM mytable t
WHERE t.upload_date <= NOW()
AND t.user = 'someuser'
) s
CROSS
JOIN ( SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) n
LEFT
JOIN mytable u
ON u.upload_date = s.upload_date - INTERVAL n.d DAY
AND u.user = 'someuser'
WHERE u.upload_date IS NULL
ORDER BY n.d DESC
LIMIT 1
只回顾9天,让它回顾更多天,只需将内联视图别名扩展为n以返回更多连续的整数。 (我们可以通过交叉连接玩一些技巧来获得一整堆整数。)
剩下的就是处理“匹配”标准(适用于MySQL DATE数据类型):
ON u.upload_date = s.upload_date - INTERVAL n.d DAY
这样的事情:
ON u.upload_date >= UNIX_TIMESTAMP(FROM_UNIXTIME(s.upload_date)-INTERVAL n.d+1 DAY)
AND u.upload_date < UNIX_TIMESTAMP(FROM_UNIXTIME(s.upload_date)-INTERVAL n.d DAY)
并使用整数时间戳值来获取MySQL DATE ...
SELECT DATE(FROM_UNIXTIME(s.upload_date)) - INTERVAL n.d DAY AS available_date