Sql запрос имеет нежелательные результаты - где проблема?

0

У меня есть таблица, в которой записаны оценки пользователей в игре (пользователь может отправить 5,10,20, столько же баллов, сколько он хочет). Мне нужно показать 20 лучших результатов в игре, но для каждого пользователя. (так как пользователь мог представить, например, 4 счета, которые являются вершинами в соответствии с другими оценками пользователей) Я написал:

SELECT DISTINCT
    `table_highscores`.`userkey`,
    max(`table_highscores`.`score`),
    `table_users`.`username`,
    `table_highscores`.`dateachieved` 
FROM
    `table_highscores`, `table_users` 
WHERE
    `table_highscores`.`userkey` = `table_users`.`userkey`
AND
    `table_highscores`.`gamekey` = $gamekey  
GROUP BY
    `userkey` 
ORDER BY
    max(`table_highscores`.`score`) DESC,
LIMIT 0, 20;

Результат вывода в порядке, но есть проблема. Когда я вычисляю разницу дней (сегодня - это dateachieved), результат неверен. (например, вместо того, чтобы говорить "оценка была отправлена ​​22 дня назад, она говорит 43 дня назад) Итак, я должен сделать второй запрос для каждого балла, чтобы найти истинную дату (что означает +20 запросов). Есть ли более короткий путь найти правильную дату?  Спасибо.

  • 0
    Вы абсолютно правы, что запрос имеет «нежелательные» результаты, или ваш расчет отключен? Как выглядит dateachieved (DATETIME?) И как вы рассчитываете разницу? Мне кажется, что проблема не имеет ничего общего с самим запросом.
  • 1
    Может ли пользователь отправлять один и тот же рекорд более одного раза в разные даты? Если есть несколько дат с одним и тем же максимальным баллом, хотите ли вы дату для самой старой или самой последней?
Теги:

4 ответа

2
Лучший ответ

В вашем запросе вы должны использовать явный JOIN, и вам не нужно ключевое слово DISTINCT.

Этот запрос должен решить вашу проблему. Я предполагаю, что пользователь может отправлять один и тот же рекордер более одного раза в разные даты, и если это произойдет, вам нужна самая старая дата:

SELECT T1.userkey, T1.score, username, dateachieved FROM (
    (SELECT userkey, max(score) AS score
    FROM table_highscores
    WHERE gamekey = $gamekey
    GROUP BY userkey) AS T1
    JOIN
    (SELECT userkey, score, min(dateachieved) as dateachieved
    FROM table_highscores
    WHERE gamekey = $gamekey
    GROUP BY userkey, score) AS T2
    ON T1.userkey = T2.userkey AND T1.score = T2.score
) JOIN table_users ON T1.userkey = table_users.userkey
LIMIT 20
  • 0
    +1: MIN сбросил меня
  • 0
    Большое спасибо! Я нашел OMG Ponies слишком сложным, чтобы понять, поэтому я использовал ваш. :) Чтобы ответить на другие вопросы (в случае, если у кого-то еще есть такая же проблема), дата достижения сохраняется как гггг-мм-дд. Подсчет дней выполняется следующим образом: ceil (abs ((strtotime ("$ dateachtained_from_the_query") - strtotime (date ("Ymd"))) / (60 * 60 * 24))); Спасибо всем за ваши ответы!
Показать ещё 1 комментарий
3

возникает проблема. Когда я вычисляю разницу дней (сегодня - это результат даты), результат неверен.

Есть две проблемы

  • dateachieved вряд ли будет значением, связанным с высокой оценкой
  • вы можете использовать MySQL DATEDIFF, чтобы вернуть количество дней между текущей датой и значением dateachieved.

Использование:

SELECT u.username,
       hs.userkey,
       hs.score,
       DATEDIFF(NOW(), hs.dateachieved) 
  FROM TABLE_HIGHSCORES hs
  JOIN TABLE_USERNAME u ON u.userkey = hs.userkey
  JOIN (SELECT ths.userkey,
               ths.gamekey,
               ths.max_score,
               MAX(ths.date_achieved) 'max_date'
          FROM TABLE_HIGHSCORES ths
          JOIN (SELECT t.userkey,
                       t.gamekey,
                       MAX(t.score) 'max_score'
                  FROM TABLE_HIGHSCORES t
              GROUP BY t.userkey, t.gamekey) ms ON ms.userkey = ths.userkey
                                               AND ms.gamekey = ths.gamekey
                                               AND ms.max_score = ths.score
       ) x ON x.userkey = hs.userkey
          AND x.gamekey = hs.gamekey
          AND x.max_score = hs.score
          AND x.max_date = hs.dateachieved
 WHERE hs.gamekey = $gamekey
 ORDER BY hs.score DESC
 LIMIT 20

Я также изменил ваш запрос на использование синтаксиса ANSI-92 JOIN, используя синтаксис ANSI-89. Это эквивалентная производительность, но ее легче читать, синтаксис поддерживается в Oracle/SQL Server/Postgres/etc и обеспечивает согласованную поддержку LEFT JOIN.

Другое дело - вам нужно использовать backticks, когда имена таблиц и/или столбцов являются ключевыми словами MySQL.

  • 0
    После max_score у вас слишком много запятых, таблица имен пользователей называется table_users, а в группе по userkey и gamekey псевдоним t, а не hs.
  • 0
    @Mark: исправлено, спасибо. Я также обновил, чтобы покрыть получение самой последней максимальной оценки, основанной на дате.
Показать ещё 1 комментарий
1

Вы не сказали, на каком языке вы используете, чтобы рассчитать разницу, но я предполагаю, что это PHP из-за используемого там $gamekey (который должен быть экранирован должным образом, btw).

Если ваше поле с датой указано в формате DATETIME, вы можете рассчитать разницу, подобную этому:

$diff = round((time() - strtotime($row['dateachieved'])) / 86400);
0

Думаю, вам нужно немного уточнить свой вопрос. Можете ли вы предоставить некоторые данные и ожидаемые результаты, а затем я буду в состоянии помочь вам?

  • 2
    Попросите разъяснения в комментариях, а не в реальном ответе, поскольку вы еще ни на что не отвечаете.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню