我的PHP页面查询文件表,用户可以单击页面上的列,按标题,日期,大小,状态和上传用户名称进行排序。然后,他们可以单击每个文件,在单独的播放器页面中查看它。
我想要做的是在播放器中创建上一个/下一个按钮,以转到文件页面上订购的上一个或下一个文件。这意味着可以通过上面列出的任何参数对结果进行排序。
对于像日期这样的东西,它很简单:
SELECT * FROM Files WHERE date > curdate ORDER BY date LIMIT 1
但是,其他一些参数导致了我的问题:
如何处理字符串,例如上传用户的姓名?
如何处理已排序列中的下一项具有相同值的情况?例如,status是介于0和3之间的int,大多数文件的状态为0.如果我按“状态”页面上的状态排序,则会先列出状态为0的所有文件,然后是状态1,等等。当前文件位于0状态文件的中间,如何找出下一个状态为0的状态?
(P.S。我知道这个主题有很多主题,但我没有看到解决上述具体情况的问题。)
答案 0 :(得分:3)
问:我如何处理类似上传用户名称的字符串?
A:与处理日期和数字的方式相同。字符串也是“可订购的”。
问:如何处理排序列中下一项具有相同值的情况?
答:与处理非唯一日期的重复值的方式相同。除了“主要”排序列之外,您还需要另一个独特的“次要”排序列,或者“主要”和“次要”组合在一起是唯一的。
理想情况下,您可以在非可空列上使用PRIMARY KEY或UNIQUE KEY作为“次要”排序顺序。
“技巧”是保存列表中的当前位置,方法是将“最后看到的”行保存为主要值和次要值,然后在查询中使用该信息以获取“下一页”。
WHERE t.major >= :last_seen_major
AND (t.major > :last_seen_major OR t.minor > :last_seen_minor)
ORDER BY t.major ASC, t.minor ASC
LIMIT 1
从最后一行(在这种情况下,只有一行),您需要保存主列和次列的值,以便可以在同一查询中使用这些值以获取“下一行”。
为了获得最佳查询效果,您需要一个可用前导列为(major, minor)
的索引。
根据您的查询,假设您有id
列,您可以执行以下操作:
SELECT f.*
FROM Files f
WHERE f.date >= :last_seen_date
AND (f.date > :last_seen_date OR f.id > :last_seen_id)
ORDER BY f.date ASC, f.id ASC
LIMIT 1
要按其他列排序,请将WHERE和ORDER BY子句中的f.date
替换为其他内容,例如: f.name
。
性能较差的替代
另一种非常流行的方法是在LIMIT子句中使用“offset”。
乍一看,这似乎是一个优雅的解决方案,但它确实有一些麻烦。
你可以这样做:
ORDER BY major ASC, minor ASC LIMIT 41,1
对于“下一行”,您将偏移量增加1
ORDER BY major ASC, minor ASC LIMIT 42,1
此方法存在一个问题,如果在已经看到的行范围内插入行,则“下一个”查询将返回相同的行。因为第41行现在是第42行。如果有人删除了一行,“下一个”查询将跳过一行。而且我不愿意在我的“获得下一行”功能中遇到这种缺陷。这种方法仍然需要跟踪列表中的位置,但是通过携带额外的偏移量而不是行的一部分。
这种方法的另一个问题是数据库必须检索行,然后对它们进行排序,然后最后应用LIMIT子句,这可能是大型集的性能问题。