假设我有这张高分表:
id : primary key
username : string
score : int
用户名和分数本身可以重复,只有id是每个人唯一的。我还有一个快速获得高分的索引:
UNIQUE scores ( score, username, id )
我怎样才能得到给定人下面的行? “下方”是指它们在此索引中的给定行之前。
例如for ( 77, 'name7', 70 ) in format ( score, username, id ) 我想检索:
77, 'name7', 41
77, 'name5', 77
77, 'name5', 21
50, 'name9', 99
但不是
77, 'name8', 88 or
77, 'name7', 82 or
80, 'name2', 34 ...
最佳答案
这是获取结果的一种方法:
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
”操作。您的索引是查询的“覆盖”索引,因此我们会希望在 EXPLAIN
输出中看到“Using index
”。)
我进行了快速测试,在我的环境中,这确实执行了索引范围扫描并避免了排序操作。
解释输出
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
关于Mysql,获取多列索引中特定行之前的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18196971/