2010-01-01 6 views
0

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

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

Возможно ли, чтобы пользователь подавал одинаковый высокий балл более одного раза в разные даты?Если существует несколько дат с одинаковой максимальной оценкой, вы хотите, чтобы дата была самой старой или самой последней? –

ответ

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 слишком сложно понять, поэтому я использовал ваш. :) Чтобы ответить на другие вопросы (в случае, если кто-то другой имеет ту же проблему), dateachieved хранится как yyyy-mm-dd. Расчет дней выполняется следующим образом: ceil (abs ((strtotime («$ dateachieved_from_the_query») - strtotime (дата («Y-m-d»)))/(60 * 60 * 24))); Спасибо всем за ваши ответы! – Manolis

+0

Сохраняете ли вы дату, полученную как строку вместо даты или времени? Было бы лучше выбрать подходящий тип для coumn, тогда вы могли бы просто сделать это, как предложил OMG Ponies: 'DATEDIFF (NOW(), hs.dateachieved)'. Вы попробовали это? Произошла ли ошибка? –

1

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

Если dateachieved поле в формате DATETIME, вы можете вычислить разницу так:

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

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

+2

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

3

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

Там две проблемы

  1. dateachieved, вероятно, будет значение, связанное с высоким значением
  2. вы можете использовать MySQL's 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 ПРИСОЕДИНЯЙСЯ синтаксис, от ANSI-89 синтаксис. Это эквивалентная производительность, но ее легче читать, синтаксис поддерживается в Oracle/SQL Server/Postgres/etc и обеспечивает последовательную поддержку LEFT JOIN.

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

+0

У вас слишком много запятой после max_score, таблица имени пользователя называется table_users, а в группе по ключу пользователя и gamekey псевдоним t, а не hs. –

+0

@Mark: Исправлено, спасибо. Я также обновляюсь, чтобы получить максимальную максимальную ценность балла по дате. –

+0

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