这是获得结果的一种方法:
SELECT t.score
, t.username
, t.id
FROM scores t
WHERE ( t.score < 77 )
OR ( t.score = 77 AND t.username < 'name7' )
OR ( t.score = 77 AND t.username = 'name7' AND t.id < 70 )
ORDER
BY t.score DESC
, t.username DESC
, t.id DESC
(注意:ORDER BY 子句可以帮助 MySQL 决定使用索引来避免“ Using filesort
”操作。您的索引是查询的“覆盖”索引,因此我们希望Using index
在EXPLAIN
输出中看到“”。)
我进行了快速测试,在我的环境中,这确实执行了索引的范围扫描并避免了排序操作。
解释输出
id select_type table type possible_keys key rows Extra
-- ----------- ----- ----- ------------------ ---------- ---- --------------------------
1 SIMPLE t range PRIMARY,scores_UX1 scores_UX1 3 Using where; Using index
LIMIT n
(如果您不需要返回所有满足条件的行,您可能需要向该查询添加一个。)
如果您有一个唯一的行 ID,您可以避免通过连接来指定表中的值。鉴于您问题中的数据:
在这里,我们使用对同一个表的第二次引用来获取行 id=70,然后使用连接来获取所有“较低”的行。
SELECT t.score
, t.username
, t.id
FROM scores k
JOIN scores t
ON ( t.score < k.score )
OR ( t.score = k.score AND t.username < k.username )
OR ( t.score = k.score AND t.username = k.username AND t.id < k.id )
WHERE k.id = 70
ORDER
BY t.score DESC
, t.username DESC
, t.id DESC
LIMIT 1000
该查询的 EXPLAIN 还显示 MySQL 使用覆盖索引并避免排序操作:
id select_type table type possible_keys key rows Extra
-- ----------- ----- ----- ------------------ ---------- ---- ------------------------
1 SIMPLE k const PRIMARY,scores_UX1 PRIMARY 1
1 SIMPLE t range PRIMARY,scores_UX1 scores_UX1 3 Using where; Using index