0

我有一个系统,允许用户将特定文件分配给过去或现在的日期。限制是每个用户每天只能上传一个文件。当用户上传文件时,日期字段必须默认为当前日期,当该日期不可用时,它将以 DESC 顺序显示过去的第一个可用日期。以下是相关的字段名称。

file_id (INT - INDEX - AUTO INCREMENT)
user_id (INT - may index this)
upload_date (INT - stores date as a unix timestamp)

我真正找到的唯一解决方案是将它们全部按日期按 DESC 顺序构建到一个数组中,然后循环直到找到一个空插槽。但是,我觉得如果用户已经填满了过去的一千天,这真的会导致速度问题。我觉得我忽略了一个简单的解决方案。

请注意:出于某种原因,他们的日期被存储为一个 Unix 时间戳,我理解它的缺点,我现在不关心更正。

4

2 回答 2

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

此查询首先使用相关子查询获取任何给定日期的前一个日期。然后它将时间值转换为日期并选择前一个日期有间隔的任何行。最大的日期减一是您要查找的日期。

此 SQL 未经测试,因此可能存在语法错误。

于 2013-02-07T00:16:43.417 回答
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 日期...

 SELECT DATE(FROM_UNIXTIME(s.upload_date)) - INTERVAL n.d DAY AS available_date
于 2013-02-07T00:16:52.790 回答