从数据库获取下一个可用日期

时间:2013-02-06 23:39:53

标签: php mysql

我有一个系统,允许用户将特定文件分配给过去或现在的日期。限制是他们每个用户每天只能上传一个文件。当用户上传文件时,日期字段必须默认为当前日期,当该日期不可用时,它将以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时间戳,我理解其缺点,我不担心此时的更正。

2 个答案:

答案 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