2009-04-25 2 views
1

Вот реальная проблема с запросом MySQL для noobish, который у меня есть.MySQL Выбор неправильного значения столбца в группе По запросу

У меня есть таблица с высокими оценками в игре, которую я пишу. В баре с высоким балом записывается имя, уровень и оценка. В db много дубликатов. Например:

Name | Level | Score | Timestamp (key) 
Bob 2  41 | 1234567.890 
Bob 3  15 | 1234568.890 
Bob 3  20 | 1234569.890 
Joe 2  40 | 1234561.890 
Bob 3  21 | 1234562.890 
Bob 3  21 | 1234563.890 

Я хочу вернуть «высокий уровень, достигнутый» список высокого балла, с выходом аналогичен:

Name | Level | Score 
Bob 3  21 
Joe 2  40 

SQL-запросы В настоящее время я использую:

SELECT *, MAX(level) as level 
FROM highscores 
GROUP BY name 
ORDER BY level DESC, score DESC 
LIMIT 5 

Однако это не совсем сработает. Вывод поля «Счет» всегда кажется случайным образом вытащен из группы, вместо того, чтобы принимать соответствующий балл для достижения самого высокого уровня. Например:

Name | Level | Score 
Bob 3  41 
Joe 2  40 

Боб никогда не получал 41 очко на уровне 3! Как я могу это исправить?

ответ

2

Вам нужно будет использовать подзапрос, чтобы вытащить счет.

select distinct 
    name, 
    max(level) as level, 
    (select max(score) from highscores h2 
     where h2.name = h1.name and h2.level = h1.level) as score 
from highscores h1 
group by name 
order by level desc, score desc 

Cheers,

Эрик

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

Когда вы отбрасываете все (*), а затем максимальный уровень, то, что вы получите, - это каждая запись последовательно, плюс столбец с максимальным уровнем на нем. Обратите внимание, что вы не группируете по партитуре (что дало бы вам Bob 2 41, а Bob 3 21 - две записи для нашего друга Боба).

Итак, как мы это исправим? Вам нужно сделать подзапрос, чтобы дополнительно фильтровать ваши результаты, что и есть (выберите max (score) ...). Теперь, для каждой строки, которая читает Боба, вы получите свой максимальный уровень (3) и его максимальный результат на этом уровне (21). Но это все равно дает нам, однако, много строк, которые Боб имеет (например, если у него 5 строк, вы получите 5 рядов Боба 3 21). Чтобы ограничить это только верхним счетом, нам нужно использовать предложение DISTINCT в инструкции select только для возврата уникальных строк.

UPDATE: Правильно SQL (не могу комментировать ле dorfier переживайте):

SELECT h1.Name, h1.Level, MAX(h1.Score) 
    FROM highscores h1 
    LEFT OUTER JOIN highscores h2 ON h1.name = h2.name AND h1.level < h2.level 
    LEFT OUTER JOIN highscores h3 ON h1.name = h3.name AND h2.level = h3.level AND h1.score < h3.score 
    WHERE h2.Name IS NULL AND h3.Name IS NULL 
    GROUP BY h1.Name, h1.Level 
+0

Спасибо за объяснение Эрика - это общий смысл для меня, но код не совсем работает, и я не могу определить, где проблема. Код теперь последовательно возвращает счет Боба за его низкий уровень, а не его максимальный! Прежде, чем это показалось первым балом в базе данных, что было плохо. Так что это намного лучше - просто еще не совершенно :) –

+0

Я думаю, вам нужно еще одну строку в конце: GROUP BY h1.Name, h1.Level – bernie

+0

Вы правы - не копировали все это, когда я вставлял он в. Спасибо! – Eric

1

Это эффективно.

ВЫБОР h1.Name, h1.Level, h1.Score
ИЗ h1
рекордов LEFT JOIN Рекорды h2 = О h1.name h2.name И h1.level < h2.level
LEFT JOIN Рекорды h3 ПО h1.name = h3.name И h1.level = h3.level И h1.score < h3.score
WHERE h2.id IS NULL AND h3.id IS NULL

Вы ищете уровень/оценка для которых нет более высокого уровня для этого пользователя, и более высокий балл этого уровня.

+0

У меня нет столбца «id», но мой ключ - микротипы. Я просто поменял местами имя столбца «timestamp», и по какой-то причине этот запрос возвращает нулевые результаты. :/ –

+0

Попробуйте «WHERE h2.name IS NULL AND h3.name IS NULL – dkretz

+0

Или, если это новая версия MySQL, вы можете использовать WHERE NOT EXISTS (SELECT 1 FROM highscores WHERE h1.name = name AND h1.level dkretz

0

Интересная проблема.Вот еще одно решение:

SELECT hs.name, hs.level, MAX(score) AS score 
FROM highscores hs 
INNER JOIN (
    SELECT name, MAX(level) AS level FROM highscores GROUP BY name 
) hl ON hl.name = hs.name AND hl.level = hs.level 
GROUP BY hs.name, hs.level; 

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

Мне нравится этот запрос лучше всего, но только для пинков ... Я нахожу следующее развлечение в стиле kludgey. Предполагая, что оценка не может превышать 99999 ...

SELECT name, level, score 
FROM highscores hs 
INNER JOIN (
    SELECT name, MAX(level * 100000 + score) AS hfactor 
    FROM highscores GROUP BY name 
) hf ON hf.hfactor = hs.level * 100000 + hs.score AND hf.name = hs.name; 
+0

Хм, спасибо mmarrk, это намного яснее - хотя, возможно, менее эффективно, чем в других примерах. Я не вижу различий в тестах моей базовой базы данных. –

+0

Он не должен быть менее эффективным (чем первый, который я назвал по крайней мере, второй была шутка). Запрос не обязательно запускается буквально, как вы его видите. Независимо от выбранного метода, механизму запросов, вероятно, придется делать несколько вложенных циклов. В зависимости от выбранного вами метода вы можете торговать некоторыми дополнительными n (например, добавьте больше данных для получения реального теста) и/или запустите запросы через EXPLAIN, чтобы сравнить эффективность. В конце концов, при прочих равных условиях, выберите самый простой. –

 Смежные вопросы

  • Нет связанных вопросов^_^