[英]Mysql, get rows before specific row in a multi-column index
Say I have this table for high-scores: 说我有这个表高分:
id : primary key
username : string
score : int
User names and scores themselves can be repeating, only id is unique for each person. 用户名和分数本身可以重复,只有id对每个人都是唯一的。 I also have an index to get high-scores fast: 我还有一个快速获得高分的索引:
UNIQUE scores ( score, username, id )
How can I get rows below the given person? 如何在给定人员下方获得行? By 'below' I mean they go before the given row in this index. “低于”我的意思是它们在此索引中的给定行之前。
Eg for ( 77, 'name7', 70 ) in format ( score, username, id ) I want to retrieve: 例如(77,'name7',70)格式(得分,用户名,id)我要检索:
77, 'name7', 41
77, 'name5', 77
77, 'name5', 21
50, 'name9', 99
but not 但不是
77, 'name8', 88 or
77, 'name7', 82 or
80, 'name2', 34 ...
Here's one way to get the result: 这是获得结果的一种方法:
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
(NOTE: the ORDER BY clause may help MySQL decide to use the index to avoid a " Using filesort
" operation. Your index is a "covering" index for the query, so we'd expect to see " Using index
" in the EXPLAIN
output.) (注意:ORDER BY子句可以帮助MySQL决定使用索引来避免“ Using filesort
”操作。你的索引是查询的“覆盖”索引,所以我们期望在EXPLAIN
看到“ Using index
”输出)。
I ran a quick test, and in my environment, this does perform a range scan of the index and avoids a sort operation. 我运行了一个快速测试,在我的环境中,它确实执行了索引的范围扫描并避免了排序操作。
EXPLAIN OUTPUT EXPLAIN OUTPUT
id select_type table type possible_keys key rows Extra
-- ----------- ----- ----- ------------------ ---------- ---- --------------------------
1 SIMPLE t range PRIMARY,scores_UX1 scores_UX1 3 Using where; Using index
(You may want to add a LIMIT n
to that query, if you don't need to return ALL the rows that satisfy the criteria.) (如果不需要返回满足条件的所有行,则可能需要向该查询添加LIMIT n
。)
If you have an unique id of a row, you could avoid specifying the values in the table by doing a join. 如果您具有行的唯一ID,则可以避免通过执行连接来指定表中的值。 Given the data in your question: 根据您的问题中的数据:
Here we use a second reference to the same table, to get the row id=70, and then a join to get all the rows "lower". 这里我们使用对同一个表的第二个引用来获取行id = 70,然后使用连接来获取所有行“lower”。
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
The EXPLAIN for this query also shows MySQL using the covering index and avoiding a sort operation: 此查询的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
The concept of "below" for repeating scores is quite fuzzy: Think of 11 users having the same score, but you want the "10 below" a special row. 重复分数的“下方”概念非常模糊:想想11个用户具有相同的分数,但您希望“10以下”成为特殊行。 That said, you can do something like (assuming you start with id=70) 也就是说,你可以做一些事情(假设你从id = 70开始)
SELECT score, username, id
FROM scores
WHERE score<=(SELECT score FROM scores WHERE id=77)
ORDER BY if(id=77,0,1), score DESC
-- you might also want e.g. username
LIMIT 5 -- you might want such a thing
;
Which will give you the rows in question inside this fuzzy factor, with the anchor row first. 这将在这个模糊因子中给出有问题的行,首先是锚行。
Edit 编辑
Re-reading your question, you don't want the anchor row, so you need WHERE score<=(...) AND id<>77
and forget the first part of the ORDER BY
重新阅读你的问题,你不需要锚行,所以你需要WHERE score<=(...) AND id<>77
并忘记ORDER BY
的第一部分
Edit 2 编辑2
After your update to the question, I understand you want only those rows, that have one of 在您对问题进行更新后,我了解到您只想要那些拥有其中一行的行
We just have to put that into a query (again assuming your anchor row has id=70): 我们只需将其放入查询中(再次假设您的锚行具有id = 70):
SELECT score, username, id
FROM scores, (
SELECT
@ascore:=score,
@ausername:=username,
@aid:=id
FROM scores
WHERE id=70
) AS seed
WHERE
score<@ascore
OR (score=@ascore AND username<@ausername)
OR (score=@ascore AND username=@ausername AND id<@aid)
ORDER BY
score DESC,
username DESC,
id DESC
-- limit 5 //You might want that
;
I think this is the query you want: 我想这是你想要的查询:
select s.*
from scores s
where s.score <= (select score
from scores
where id = 70
) and
s.id <> 70
order by scores desc
limit 4;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.