2016-05-25 4 views
0

Я пытаюсь выполнить следующий запросMySQL MAX и MIN

SELECT `id`, 
     `name`, 
     `ownerid`, 
     `creationdata`, 
     `motd`, 
     (SELECT Count(*) 
     FROM guild_membership a, 
       players_online b 
     WHERE a.player_id = b.player_id 
       AND a.guild_id = id) AS `online`, 
     (SELECT Max(b.level) 
     FROM guild_membership a, 
       players b 
     WHERE a.player_id = b.id 
       AND a.guild_id = id) AS `toplevel`, 
     (SELECT Min(a.level) 
     FROM players a, 
       guild_membership b 
     WHERE a.id = b.player_id 
       AND b.guild_id = id) AS `lowlevel` 
FROM `guilds` 
WHERE `name` = 'Wideswing Poleaxe' 
LIMIT 1; 

Таблицы, используемые здесь, являются followin

CREATE TABLE IF NOT EXISTS `players` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) NOT NULL, 
    `group_id` int(11) NOT NULL DEFAULT '1', 
    `account_id` int(11) NOT NULL DEFAULT '0', 
    `level` int(11) NOT NULL DEFAULT '1', 
    ... 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `name` (`name`), 
    FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE, 
    KEY `vocation` (`vocation`) 
) ENGINE=InnoDB; 

CREATE TABLE IF NOT EXISTS `guilds` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) NOT NULL, 
    `ownerid` int(11) NOT NULL, 
    `creationdata` int(11) NOT NULL, 
    `motd` varchar(255) NOT NULL DEFAULT '', 
    PRIMARY KEY (`id`), 
    UNIQUE KEY (`name`), 
    UNIQUE KEY (`ownerid`), 
    FOREIGN KEY (`ownerid`) REFERENCES `players`(`id`) ON DELETE CASCADE 
) ENGINE=InnoDB; 

CREATE TABLE IF NOT EXISTS `guild_membership` (
    `player_id` int(11) NOT NULL, 
    `guild_id` int(11) NOT NULL, 
    `rank_id` int(11) NOT NULL, 
    `nick` varchar(15) NOT NULL DEFAULT '', 
    PRIMARY KEY (`player_id`), 
    FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, 
    FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, 
    FOREIGN KEY (`rank_id`) REFERENCES `guild_ranks` (`id`) ON DELETE CASCADE ON UPDATE CASCADE 
) ENGINE=InnoDB; 

Я пытаюсь получить уровень MAX и уровень мин, от игрок стол внутри одной гильдии

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

Я не уверен, что это неправильно

ответ

1

Первое, что я замечаю: вы используете LIMIT без ORDER BY. Поэтому из таблицы гильдий вы ожидаете найти более одной записи для name = 'Wideswing Poleaxe', но посмотрите только на то, что DBMS обнаруживает. Желательно?

Следующее, что я вижу, это устаревший синтаксис соединения. Откуда вы это взяли? Двадцатилетняя книга? Нет, стоп, двадцать лет назад этот синтаксис уже были сделаны излишними, поэтому он должен быть даже старше ;-) Использования явно присоединяется вместо (JOIN ... ON ...)

Как ваших подзапросы: Вы сравниваете с id без классификатора, поэтому СУБД будет считать это guild_membership.id или players_online соответственно. players.id, где вы действительно хотите быть guild.id. Это должно объяснить, что вы получаете неожиданные значения.

Что касается построения запроса: вы можете присоединиться к данным агрегированного игрока. И используйте псевдонимы, соответствующие таблицам.

select 
    guilds.id, 
    guilds.name, 
    guilds.ownerid, 
    guilds.creationdata, 
    guilds.motd, 
    players.online, 
    players.toplevel, 
    players.lowlevel 
from guilds 
left join 
(
    select 
    gms.guild_id, 
    max(p.level) as toplevel, 
    min(p.level) as lowlevel, 
    sum((select count(*) from players_online po where po.player_id = p.id)) as online 
    from guild_membership gms 
    join players p on p.id = gms.player_id 
    group by gms.guild_id 
) players on players.guild_id = guilds.id 
where guilds.name = 'Wideswing Poleaxe'; 

Вы можете изменить левое внешнее соединение (left join), чтобы внутреннее соединение (join), если вам не нужно видеть гильдию без какого-либо игрока.

+0

Ваше поле онлайн, вероятно, только когда-либо будет на один или на нуль; и вам гарантируется только одно из этих значений (в том же наборе значений данных), если все игроки одновременно находятся в сети или в автономном режиме. (т. е. вам не нужен подзапрос). – Uueerdo

+0

@Uueerdo: Если это так, таблица 'player' должна либо просто иметь флаг онлайн yes/no, либо, для получения дополнительных данных, иметь' id_player_online'.Но это наоборот: в таблице 'players_online' есть' id_player', что позволяет одному игроку быть несколько раз в этой таблице. Таким образом, мой запрос совпадает с datamodel, но, конечно, это может быть сбой в модели, и тогда все может быть так же просто, как вы описываете. –

+0

Я судил, как оригинальный вопрос использовал таблицу 'players_online'; если это так, как вы его интерпретировали, их первоначальный запрос получил бы общее количество раз, когда игроки были в сети. В любом случае, ваше значение 'online' будет изменчивым, поскольку оно не является агрегированным или сгруппированным по запросу, содержащему подзапрос, в который он рассчитывается; вместо этого он будет получать количество раз, когда эффективный случайный игрок этой гильдии был в сети. – Uueerdo

0

Вы должны рассмотреть вопрос об изменении вашего запроса, как

SELECT g.`id`, 
     g.`name`, 
     g.`ownerid`, 
     g.`creationdata`, 
     g.`motd`, 
     (SELECT Count(*) 
     FROM guild_membership a, 
       players_online b 
     WHERE a.player_id = b.player_id 
       AND a.guild_id = id) AS `online`, 
     (SELECT Max(b.level) 
     FROM players b join guild_membership a on a.player_id = b.id 
       AND a.guild_id = g.id) AS `toplevel`, 
     (SELECT Min(a.level) 
     FROM players a join 
       guild_membership b on a.id = b.player_id 
       AND b.guild_id = g.id) AS `lowlevel` 
FROM `guilds` g 
WHERE g.`name` = 'Wideswing Poleaxe' 
LIMIT 1; 
1

Я думаю, что проблема здесь: a.guild_id = id

Идентификатор используется от игроков, а не гильдий, поскольку он по-прежнему является частью подзапроса.

1

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

Попробуйте это ...

SELECT `id`, `name`, `ownerid`, `creationdata`, `motd` 
     , COUNT(po.player_id) AS online 
     , MAX(p.level) AS toplevel 
     , MIN(p.level) AS lowlevel 
FROM `guilds` AS g 
LEFT JOIN guild_membership AS gm ON g.id = gm.guild_id 
LEFT JOIN players AS p ON gm.player_id = p.player_id 
LEFT JOIN players_online AS po ON gm.player_id = po.player_id 
WHERE g.`name` = 'Wideswing Poleaxe' 
; 

COUNT подсчитывает только ненулевые значения; аналогично MAX, MIN и большинство других агрегатных функций игнорируют нулевые значения (только возврат null, если обрабатываются только нулевые значения).