【发布时间】:2019-07-30 22:22:31
【问题描述】:
我有一个包含分数的表(最多 20000 条记录)。我想显示一个带有延迟加载功能的记分牌:只显示玩家得分的 20 条记录,如果他向上滚动,则再显示 20 条记录,或者如果他向下滚动,则获取 20 条记录。 这个板子会经常被大量玩家同时调用,所以我必须以最轻松的方式来做。
CREATE TABLE cities (
cityId SMALLINT UNSIGNED NOT NULL,
points SMALLINT UNSIGNED NOT NULL, -- not unique at all
PRIMARY KEY (cityId)
)
ENGINE = INNODB;
ALTER TABLE cities
ADD INDEX points (points);
如何有效地获取指定行 (WHERE cityId=<myCityId>) 的前 10 行和后 10 行,按 points 降序排序
我怎样才能找到下一个 20?因为使用OFFSET 和LIMIT 似乎不是最好的方法
https://www.eversql.com/faster-pagination-in-mysql-why-order-by-with-limit-and-offset-is-slow/
谢谢
编辑:
我尝试了两种@Schwern 解决方案,但都没有按预期工作,因为我可以有相同分数的线条。
select points, cityName from (
(
select *
from cities
where points < (select points from cities where cityName = :cityName)
order by points desc
limit 5
)
union
select * from cities where cityName = :cityName
union
(
select *
from cities
where points >= (select points from cities where cityName = :cityName)
and cityName != :cityName
order by points
limit 5
)
) t
order by points;
limit=5 和 cityName=Viry 的结果:
points cityName
0 Nantes
0 Amiens
2223 Roye
3705 Caps City
4446 Toulouse
5187 Viry
5187 Rampillon
5187 Vdr
5187 Chicago
5187 Le Village
5187 Titoucity
缺少很多相同分数的行(例如:32行,分数=4446,这里只有一个)
MariaDB / MySQL 版本翻译自Oracle solution
WITH RECURSIVE boundaries (prevr, nextr, lvl) as (
select
COALESCE(
(
select max(c.points)
from cities AS c
where c.points < c2.points
),
c2.points
) AS prevr,
COALESCE(
(
select min(c.points)
from cities AS c
where c.points > c2.points
),
c2.points
) AS nextr,
1 lvl
from cities AS c2
where cityName = :cityName
union all
select
COALESCE(
(
select max(points)
from cities AS c
where c.points < prevr
),
prevr
) AS prevr,
COALESCE(
(
select min(points)
from cities AS c
where c.points > nextr
),
nextr
) AS nextr,
lvl+1 lvl
from boundaries
where lvl+1 <= :lvl
)
select c.points, c.cityName
from cities AS c
join boundaries AS b
on c.points between b.prevr and b.nextr
and b.lvl = :lvl
order by c.points;
lvl=1 和 cityName=Viry 的结果
points cityName
4446 Toulouse
4446 Jotown
4446 Guignes
4446 Douns
4446 Colombes
4446 Chambly
4446 Cassandra Gn
4446 Bussyland
4446 Magny Les Hameaux
4446 Palamos
4446 Ville
4446 Loujul
4446 Osny
4446 Sqy
4446 Senlis
4446 Vendres
4446 Amiens
4446 Saint Jean De Luz
4446 Senlis
4446 Abbeville
4446 Ca City
4446 Tolkien
4446 Paiementland
4446 Cash City
4446 Amiens
4446 Beauvais
4446 Kona
4446 St Petaouchnoc'
4446 Amiens
4446 Pick City
4446 Conflans
4446 Versailles ^ +1
5187 Le Village
5187 Compiegne
5187 Titoucity
5187 Vdr
5187 Rampillon
5187 Chicago
5187 Moustache Ville
5187 Viry ^ 0
5928 Trot Ville v -1
5928 Amiens
5928 Cityc
5928 Bakel City
5928 Rouen
5928 Noailles
5928 Caps Town
5928 Atlantis
5928 Camon
5928 Smart City
5928 Maville
5928 Azzana
5928 Strasbourg
5928 Sqy Park
它有效,但我需要决定我得到多少行,有时我可以有 50 个相同的分数,有时只有一两个。
重新编辑
使用第二个订单字段重试第一个解决方案
SET @mypoints := (select points from cities where cityId = :cityId);
select t.points, t.cityId, t.cityName from (
(
select *
from cities AS c1
where c1.points <= @mypoints
AND c1.cityId > :cityId
order by c1.points DESC, c1.cityId ASC
limit 5
)
union
select * from cities AS c2 where c2.cityId = :cityId
union
(
select *
from cities AS c3
where c3.points >= @mypoints
AND c3.cityId < :cityId
order by c3.points ASC, c3.cityId DESC
limit 5
)
) t
order by t.points;
limit=5 和 cityId=36 的结果
points cityId cityName
0 49 Nantes
1482 53 Paris
1482 51 Mattown
2223 56 Haudiville
3705 37 Caps City
5187 36 Viry < ==
6669 29 Prospercity
6669 31 Amiens
8892 22 Meteor
20007 34 Ouagadougou
20007 35 Meaux
和第一个问题一样
【问题讨论】:
-
什么版本的 MySQL 和/或 MariaDB?较新的有window functions。
-
好的,谢谢,我总是在不知道它是否真的有用的情况下留下这个数字,知道我知道:) stackoverflow.com/questions/5634104/… 我的错误来自这里:dev.mysql.com/doc/refman/8.0/en/integer-types.html(我混淆了字节和位)和来自phpmyadmin 在字段创建时询问长度并默认放置值
-
mysql Ver 15.1 Distrib 10.1.40-MariaDB
-
偏移和限制对我来说似乎是一种合理的方式