Оптимизация SQL позиции в таблице лидеров - PullRequest
0 голосов
/ 08 февраля 2019

Я предлагаю список лидеров опыта для бота Discord, который я активно развиваю с помощью таких карт, как профильные карты, показывающие свое звание.SQL-запрос, который я сейчас использую, работает безупречно, однако я заметил, что этот запрос занимает довольно много времени.

    SELECT id, 
           discord_id, 
           discord_tag, 
           xp, 
           level 
    FROM   (SELECT @rank := @rank + 1 AS id, 
                   discord_id, 
                   discord_tag, 
                   xp, 
                   level 
            FROM   profile_xp, 
                   (SELECT @rank := 0) r 
            ORDER  BY xp DESC) t 
    WHERE  discord_id = '12345678901'; 

Таблица не слишком большая (примерно 20 000 уникальных записей), но этот запрос занимает в среднем где-то 300-450 мс, что накапливается относительно быстро при большом количестве одновременных запросов.

Мне было интересно, можно ли оптимизировать этот запрос для повышения производительности.Я выделил это для этого запроса, остальная часть сервера MySQL отзывчива и быстра.

Буду рад любой подсказке и заранее благодарю!:)

Ответы [ 3 ]

0 голосов
/ 08 февраля 2019

Вы сканируете 20000 строк, чтобы назначить «номера строк», а затем выбираете из него ровно одну строку.Вместо этого вы можете использовать агрегацию:

SELECT *, (
    SELECT COUNT(*)
    FROM profile_xp AS x
    WHERE xp > profile_xp.xp
) + 1 AS rnk
FROM profile_xp
WHERE discord_id = '12345678901'

Это даст вам ранг игрока.Для плотного ранга используйте COUNT(DISTINCT xp).При необходимости создайте индекс для столбца xp.

0 голосов
/ 08 февраля 2019

Предполагая, что discord_id является первичным ключом таблицы, и вы просто пытаетесь получить «ранг» одной записи, вы должны иметь возможность использовать другой подход.

SELECT px.discord_id, px.discord_tag, px.xp, px.level
   , 1 + COUNT(leaders.xp) AS rank
   , 1 + COUNT(DISTINCT leaders.xp) AS altRank
FROM profile_xp AS px
LEFT JOIN profile_xp AS leaders ON px.xp < leaders.xp
WHERE px.discord_id = '12345678901'
GROUP BY px.discord_id, px.discord_tag, px.xp, px.level
; 

Примечание.ранг "и" altRank ".rank должно дать вам положение, аналогичное тому, что вы изначально искали;ваши результаты могли колебаться для "связей", это rank всегда ставит привязанных игроков в их наивысшую "связь".Если 3 записи связаны для 2-го места, те (запрашиваемые отдельно с этим) будут показывать 2-е место, следующий xp будет занимать 5-е место (при условии 1 на 1-м, 2,3,4 на 2-м, 5 на 5-м).altRank "закроет пробелы", поместив 5 в "группу" на 3-м месте.

Я бы также порекомендовал индекс на xp, чтобы ускорить это.

0 голосов
/ 08 февраля 2019

Не ответ;слишком долго для комментария:

Я обычно пишу такие вещи точно так же, как вы, потому что это быстро и легко, но на самом деле есть технический недостаток этого метода - хотя это становится очевидным только внекоторые ситуации.

В качестве иллюстрации рассмотрим следующее:

DROP TABLE IF EXISTS ints;

CREATE TABLE ints (i INT NOT NULL PRIMARY KEY);

INSERT INTO ints VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);

Ваш запрос:

 SELECT a.*
      , @i:=@i+1 rank
   FROM ints a
   JOIN (SELECT @i:=0) vars
  ORDER
     BY RAND() DESC;
    +---+------+
    | i | rank |
    +---+------+
    | 3 |    4 |
    | 2 |    3 |
    | 5 |    6 |
    | 1 |    2 |
    | 7 |    8 |
    | 9 |   10 |
    | 4 |    5 |
    | 6 |    7 |
    | 8 |    9 |
    | 0 |    1 |
    +---+------+

Посмотрите, набор результатов не является "случайным"совсем.rank всегда соответствует i

Теперь сравните это со следующим:

SELECT a.*
     , @i:=@i+1 rank 
  FROM 
     ( SELECT * FROM ints ORDER by RAND() DESC) a 
  JOIN (SELECT @i:=0) vars;
+---+------+
| i | rank |
+---+------+
| 5 |    1 |
| 2 |    2 |
| 8 |    3 |
| 7 |    4 |
| 4 |    5 |
| 6 |    6 |
| 0 |    7 |
| 1 |    8 |
| 3 |    9 |
| 9 |   10 |
+---+------+
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...