Query with three join incredibly slow - mysql

I'm trying to return all the country that have football matches which play in a specific date. The data are defined in the following tables:
competition
id | country_id | name
50 1 Premier League
competition_seasons
id | competition_id | name
70 50 2019
competition_rounds
id | season_id | name
58 70 Regular Season
match
id | round_id | home | away | result | datetime
44 58 22 87 1 - 0 2019-03-16:00:00
There are different competitions stored in the competition table, and then each competition can have multiple season which are stored in the competition_seasons. A season can also have different competition rounds, these are stored in competition_rounds.
All the matches are stored in the match table and are grouped for the round_id.
I wrote this method for the API:
$app->get('/country/get_countries/{date}', function (Request $request, Response $response, array $args)
{
$start_date = $args["date"] . " 00:00";
$end_date = $args["date"] . " 23:59";
$sql = $this->db->query("SELECT n.* FROM country n
LEFT JOIN competition c ON c.country_id = n.id
LEFT JOIN competition_seasons s ON s.competition_id = c.id
LEFT JOIN competition_rounds r ON r.season_id = s.id
LEFT JOIN `match` m ON m.round_id = r.id
WHERE m.datetime BETWEEN '" . $start_date . "' AND '" . $end_date . "'
GROUP BY n.id");
$sql->execute();
$countries = $sql->fetchAll();
return $response->withJson($countries);
});
there are thousands of records organized by id, but the query took about 6, 7 seconds to return all the countries that play in the specified date.
How can I optimize this process?
Performance
UPDATE
I noticed an interesting thing, if I do:
SELECT round_id, DATE("2019-03-18") FROM `match`
the query is really fast, so I guess the datetime field is slow down the join part, any idea about that?
Table Structure
CREATE TABLE IF NOT EXISTS `swp`.`competition` (
`id` INT NOT NULL,
`country_id` INT NULL,
`name` VARCHAR(255) NULL,
`category` INT NULL,
PRIMARY KEY (`id`),
INDEX `id_idx` (`country_id` ASC),
INDEX `FK_competition_types_competition_type_id_idx` (`category` ASC),
CONSTRAINT `FK_country_competition_country_id`
FOREIGN KEY (`country_id`)
REFERENCES `swp`.`country` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_competition_categories_competition_category_id`
FOREIGN KEY (`category`)
REFERENCES `swp`.`competition_categories` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `swp`.`competition_seasons` (
`id` INT NOT NULL AUTO_INCREMENT,
`competition_id` INT NOT NULL,
`season_id` INT NULL,
`name` VARCHAR(45) NOT NULL,
`update_at` DATETIME NULL,
PRIMARY KEY (`id`),
INDEX `FK_competition_competition_seasons_competition_id_idx` (`competition_id` ASC),
CONSTRAINT `FK_competition_competition_seasons_competition_id`
FOREIGN KEY (`competition_id`)
REFERENCES `swp`.`competition` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `swp`.`competition_rounds` (
`id` INT NOT NULL AUTO_INCREMENT,
`round_id` INT NULL,
`season_id` INT NOT NULL,
`name` VARCHAR(255) NULL,
PRIMARY KEY (`id`),
INDEX `FK_competition_seasons_competition_rounds_season_id_idx` (`season_id` ASC),
CONSTRAINT `FK_competition_seasons_competition_rounds_season_id`
FOREIGN KEY (`season_id`)
REFERENCES `swp`.`competition_seasons` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `swp`.`match`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `swp`.`match` (
`id` INT NOT NULL,
`round_id` INT NOT NULL,
`group_id` INT NULL,
`datetime` DATETIME NULL,
`status` INT NULL,
`gameweek` INT NULL,
`home_team_id` INT NULL,
`home_team_half_time_score` INT NULL,
`home_team_score` INT NULL,
`home_extra_time` INT NULL,
`home_penalties` INT NULL,
`away_team_id` INT NULL,
`away_team_half_time_score` INT NULL,
`away_team_score` INT NULL,
`away_extra_time` INT NULL,
`away_penalties` INT NULL,
`venue_id` INT NULL,
`venue_attendance` INT NULL,
`aggregate_match_id` INT NULL,
PRIMARY KEY (`id`),
INDEX `home_team_id_idx` (`home_team_id` ASC),
INDEX `away_team_id_idx` (`away_team_id` ASC),
INDEX `venue_id_idx` (`venue_id` ASC),
INDEX `match_status_id_idx` (`status` ASC),
INDEX `FK_competition_rounds_match_round_id_idx` (`round_id` ASC),
INDEX `FK_match_match_aggregate_match_id_idx` (`aggregate_match_id` ASC),
INDEX `FK_competition_groups_match_group_id_idx` (`group_id` ASC),
CONSTRAINT `FK_team_match_home_team_id`
FOREIGN KEY (`home_team_id`)
REFERENCES `swp`.`team` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_team_match_away_team_id`
FOREIGN KEY (`away_team_id`)
REFERENCES `swp`.`team` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_venue_match_venue_id`
FOREIGN KEY (`venue_id`)
REFERENCES `swp`.`venue` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_match_status_match_status_id`
FOREIGN KEY (`status`)
REFERENCES `swp`.`match_status` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_competition_rounds_match_round_id`
FOREIGN KEY (`round_id`)
REFERENCES `swp`.`competition_rounds` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_match_match_aggregate_match_id`
FOREIGN KEY (`aggregate_match_id`)
REFERENCES `swp`.`match` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_competition_groups_match_group_id`
FOREIGN KEY (`group_id`)
REFERENCES `swp`.`competition_groups` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;

First, write the query as:
SELECT n.*
FROM country n JOIN
competition c
ON c.country_id = n.id JOIN
competition_seasons s
ON s.competition_id = c.id JOIN
competition_rounds r
ON r.season_id = s.id JOIN
`match` m
ON m.round_id = r.id
WHERE m.datetime >= ? AND
m.datetime < ?
GROUP BY n.id;
The changes here are relatively minor and will not affect performance. But they are important:
JOIN instead of LEFT JOIN, because you require that the conditions match.
Parameters for the date rather than munging the query string, because this is a good idea.
>= and < for the comparison, because this works with both dates and date times. You will need to add 1 day to the end date -- but leave off the time component.
Then, for performance, you want indexes:
match(datetime, round_id)
competition_rounds(id, season_id)
competition_seasons(id, competition_id)
competition(id, country_id)
country(id)
Actually, the first is the most important. The last four are not needed if the respective id columns are declared as primary keys.

With LEFT JOIN, the query can only be executed top-bottom, meaning the last table is scanned for every product of entries in the before tables. Also, using LEFT JOIN and GROUP BY without any aggregate makes no sense, because it will always return all country ids. This having said, I would rewrite it like this:
SELECT DISTINCT
c.country_id
FROM
competition c,
WHERE
EXISTS (
SELECT
*
FROM
competition_seasons s,
competition_rounds r,
`match` m
WHERE
s.competition_id = c.id
AND r.season_id = s.id
AND m.round_id = r.id
AND m.datetime BETWEEN ...
)
This will be correctly optimized by all RDB's I know of.
Note, an 2-column index on (match.datetime, match.round_id) - in this order, will make a huge performance impact. Or is write speed is a concern, at least a single column index on (match.datetime) would be recommended.
Important note about indexes on strings: String comparison is always quirky in RDBs. Make sure you use a binary collation for the datetime column or use native DATETIME format. Various RDBs may fail to use indexes on case-insensitive columns.
Note I removed the join on n - that just add another PK lookup to check that the country still exists in the countries table. You can add it back in if you don't have any ON DELETE CASCADE or other kind of constraint that ensures data consistency, like this:
SELECT DISTINCT
n.id
FROM
country n
WHERE
EXISTS (
SELECT
*
FROM
competition c,
competition_seasons s,
competition_rounds r,
`match` m
WHERE
c.country_id=n.id
AND s.competition_id = c.id
AND r.season_id = s.id
AND m.round_id = r.id
AND m.datetime BETWEEN ...
)

Related

How to properly solve MySql complaint about "GROUP BY clause and contains nonaggregated column" when ONLY_FULL_GROUP_BY is ON

MySQL Version 8.0 Schema SQL
CREATE TABLE IF NOT EXISTS `servers` (
`id` INT NOT NULL,
`name` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `companies` (
`id` INT NOT NULL,
`name` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `print` (
`id` INT NOT NULL,
`page` INT NOT NULL,
`copy` INT NOT NULL,
`date` DATE NOT NULL,
`server` INT NOT NULL,
PRIMARY KEY (`id`),
INDEX `fk_print_servers1_idx` (`server` ASC) VISIBLE,
CONSTRAINT `fk_print_servers1`
FOREIGN KEY (`server`)
REFERENCES `servers` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `company_server` (
`server` INT NOT NULL,
`company` INT NOT NULL,
PRIMARY KEY (`server`, `company`),
INDEX `fk_servers_has_company_company1_idx` (`company` ASC) VISIBLE,
INDEX `fk_servers_has_company_servers_idx` (`server` ASC) VISIBLE,
CONSTRAINT `fk_servers_has_company_servers`
FOREIGN KEY (`server`)
REFERENCES `servers` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_servers_has_company_company1`
FOREIGN KEY (`company`)
REFERENCES `companies` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
insert into servers (id,name)
values
(1, 'server1'),
(2, 'server2'),
(3, 'server3'),
(4, 'server4');
insert into companies (id,name)
values
(1, 'company1'),
(2, 'company2'),
(3, 'company3');
insert into company_server (company,server)
values
(1,1),
(2,1),
(3,2),
(3,3);
insert into print (id,page,copy,date,server)
values
(1,2,3,'2020-1-11',1),
(2,1,6,'2020-1-12',3),
(3,4,5,'2020-1-13',4),
(4,5,3,'2020-1-15',2),
(5,3,4,'2020-1-15',4),
(6,1,2,'2020-1-16',3),
(7,2,2,'2020-1-16',4);
My query:
select
group_concat(c.name separator ',') as name_company,
ss.name,
sum_print as sum,
(sum_print/total) *100 as percentage
from companies c
inner join company_server cs on c.id = cs.company
right join servers ss on ss.id = cs.server
left join (
select
server,
sum(page*copy) as sum_print
from print
where date between CAST('2020-1-12' AS DATE) AND CAST('2020-1-15' AS DATE)
group by server
) tmp on tmp.server = ss.id
cross join (
select sum(page*copy) as total
from print
where date between CAST('2020-1-12' AS DATE) AND CAST('2020-1-15' AS DATE)
) tmp2
group by ss.id;
Mysql error output
Query Error: Error: ER_WRONG_FIELD_WITH_GROUP: Expression #4 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'tmp2.total' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
What I need:
That someone can point me, in the query above, which adjustment is missing so that the error is not thrown
DB Feedle
What I dont want:
Turn off ONLY_FULL_GROUP_BY
Extra notes:
The query works fine if ONLY_FULL_GROUP_BY is turned off
No embarrassment, I don't have good query fluency. I've already tried to read some stackoverflows answers about the same problem, but I ended up not understanding due to my limitation.
I think that by being pointed out where the problem is, maybe I can better understand what the other topics were pointing out.

Select data using if statement to show Not Available for missing values

I have a database table in mysql that store types like "temperature" , "oxygen_saturation" , "heart_rate" , "systolic_pressure" , "diastolic_pressure".
However, for a given checkup_id, some types may not stored. Those I want to show them as N/A.
My table is the following:
CREATE TABLE `exam` (
`id` int NOT NULL AUTO_INCREMENT,
`checkup_id` int NOT NULL,
`type` varchar(100) NOT NULL,
`result` longtext NOT NULL,
`combined_exam_id` int NOT NULL DEFAULT '-1',
`status` tinyint NOT NULL DEFAULT '1',
`version_id` int NOT NULL,
PRIMARY KEY (`id`,`version_id`),
UNIQUE KEY `exam_id` (`id`,`version_id`) /*!80000 INVISIBLE */,
KEY `checkup_idx` (`checkup_id`) /*!80000 INVISIBLE */,
KEY `version_id_fk_exam_idx` (`version_id`),
CONSTRAINT `exam_ibfk_1` FOREIGN KEY (`checkup_id`) REFERENCES `endorse_checkup` (`id`),
CONSTRAINT `version_id_fk_exam` FOREIGN KEY (`version_id`) REFERENCES `ps_versioning` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8;
I have the following select statement:
SELECT end_up.id as checkup_id,
end_e.type,
end_e.result,
FROM endorse_exam as end_e
left JOIN endorse_checkup as end_up on end_up.id = end_e.checkup_id
left JOIN ps_versioning as v ON v.id = end_e.version_id
left JOIN u_users as u ON u.user_id = v.user_id
WHERE end_up.patient_id = 50 AND v.id <= (SELECT IF(164 > -1, 164, max(v.id))) and checkup_id = 25;
Note: I put 164 value by hand, but actually it comes through an API.
Using the SQL code above, I get a result like
However, I want to create a query to get info like:

How to select specific value on join condition?

I have a table that contains the details of a game, this table is called match, each match can have n goals, these goals are stored inside the table goal.
Suppose I have the following situation on match:
id = 2564824
home_team_half_time_score = 2
home_team_score = 4
away_team_half_time_score = 0
away_team_score = 0
so this record means that the match is ended 4 - 0 for the home team, what I want to know is: how can I get only the goals scored in the first time? in this case there are 2 goals scored in the first time, and have this id:
id | match_id | result | minute
16092 2564824 1 - 0 15
16093 2564824 2 - 0 43
16094 2564824 3 - 0 63
16095 2564824 4 - 0 78
I just need to get the minute field of each goal, how can I setup the query?
SCHEMA
CREATE TABLE IF NOT EXISTS `swp`.`match` (
`id` INT NOT NULL,
`round_id` INT NULL,
`datetime` DATETIME NULL,
`status` INT NULL,
`gameweek` INT NULL,
`home_team_id` INT NULL,
`home_team_half_time_score` INT NULL,
`home_team_score` INT NULL,
`home_extra_time` INT NULL,
`home_penalties` INT NULL,
`away_team_id` INT NULL,
`away_team_half_time_score` INT NULL,
`away_team_score` INT NULL,
`away_extra_time` INT NULL,
`away_penalties` INT NULL,
`venue_id` INT NULL,
`venue_attendance` INT NULL,
`aggregate_match_id` INT NULL,
PRIMARY KEY (`id`),
INDEX `home_team_id_idx` (`home_team_id` ASC),
INDEX `away_team_id_idx` (`away_team_id` ASC),
INDEX `venue_id_idx` (`venue_id` ASC),
INDEX `match_status_id_idx` (`status` ASC),
INDEX `FK_competition_rounds_match_round_id_idx` (`round_id` ASC),
INDEX `FK_match_match_aggregate_match_id_idx` (`aggregate_match_id` ASC),
CONSTRAINT `FK_team_match_home_team_id`
FOREIGN KEY (`home_team_id`)
REFERENCES `swp`.`team` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_team_match_away_team_id`
FOREIGN KEY (`away_team_id`)
REFERENCES `swp`.`team` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_venue_match_venue_id`
FOREIGN KEY (`venue_id`)
REFERENCES `swp`.`venue` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_match_status_match_status_id`
FOREIGN KEY (`status`)
REFERENCES `swp`.`match_status` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_competition_rounds_match_round_id`
FOREIGN KEY (`round_id`)
REFERENCES `swp`.`competition_rounds` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_match_match_aggregate_match_id`
FOREIGN KEY (`aggregate_match_id`)
REFERENCES `swp`.`match` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Thanks for any help.
CREATE TABLE IF NOT EXISTS `swp`.`goal` (
`id` INT NOT NULL AUTO_INCREMENT,
`team_id` INT NOT NULL COMMENT 'Team that has scored.',
`player_marker_id` INT NOT NULL,
`player_assist_id` INT NULL,
`match_id` INT NOT NULL,
`minute` VARCHAR(45) NOT NULL,
`result` VARCHAR(45) NULL,
`type` INT NOT NULL COMMENT 'All the status that a goal can assume.',
INDEX `player_marker_id_idx` (`player_marker_id` ASC),
INDEX `player_assist_id_idx` (`player_assist_id` ASC),
INDEX `match_id_idx` (`match_id` ASC),
PRIMARY KEY (`id`),
INDEX `FK_goal_type_goal_goal_type_id_idx` (`type` ASC),
INDEX `FK_team_goal_team_id_idx` (`team_id` ASC),
CONSTRAINT `FK_player_goal_player_marker_id`
FOREIGN KEY (`player_marker_id`)
REFERENCES `swp`.`player` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_player_goal_player_assist_id`
FOREIGN KEY (`player_assist_id`)
REFERENCES `swp`.`player` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_match_goal_match_id`
FOREIGN KEY (`match_id`)
REFERENCES `swp`.`match` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_goal_type_goal_goal_type_id`
FOREIGN KEY (`type`)
REFERENCES `swp`.`goal_type` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_team_goal_team_id`
FOREIGN KEY (`team_id`)
REFERENCES `swp`.`team` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
UPDATE
actual query:
$query = "SELECT m.*,
t.name AS team_name,
t.id AS team_id,
l.position AS team_rank,
COUNT(CASE WHEN g.type = 5 THEN 1 END) AS failed_to_score,
FROM `match` m
LEFT JOIN goal g ON m.id = g.match_id
LEFT JOIN team t ON t.id = :team_id
LEFT JOIN league_ranking l ON l.team_id = :team_id AND l.round_id = :round_id
WHERE m.round_id = :round_id
AND m.status = 5 ";
SELECT minute, result FROM goal WHERE (CAST(minute AS UNSIGNED)<=45 OR minute like '45%') AND match_id='2564824'
if this is your starting point:
id | match_id | result | minute
16092 2564824 1 - 0 15
16093 2564824 2 - 0 43
16094 2564824 3 - 0 63
16095 2564824 4 - 0 78
If this is indeed your expected results then you have to show the input data to get this result
UNTESTED
SELECT m.*,
t.name AS team_name,
t.id AS team_id,
l.position AS team_rank,
COUNT(CASE WHEN g.type = 5 THEN 1 END) AS failed_to_score,
g.minute
FROM `match` m
LEFT JOIN goal g ON m.id = g.match_id
LEFT JOIN team t ON t.id = :team_id
LEFT JOIN league_ranking l ON l.team_id = :team_id AND l.round_id = :round_id
WHERE m.round_id = :round_id
AND m.status = 5 "
AND (CAST(minute AS UNSIGNED)<=45 OR minute like '45%');
This integrated query will stop the analysis of each match to the first half. I'd keep the two queries as separate

MySQL MAX and MIN

I am trying to execute the following query
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;
The tables used in here are the 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;
I am trying to get the MAX level and MIN level on the players table inside one guild
However I am always getting toplevel and lowlevel the same value and tis always the lowest level
I am not sure what is wrong
First thing I notice is: you are using LIMIT without ORDER BY. So from the guilds table you expect to find more than one entry for name = 'Wideswing Poleaxe', but only look at the first the DBMS happens to find. Is this desired?
Next thing I see is the out-dated join syntax. Where did you get this from? A twenty year old book? No, stop, twenty years ago this syntax was already made redundant, so it must be even older ;-) Use explicit joins instead (JOIN ... ON ...)
As to your subqueries: You are comparing with id without any qualifier, so the DBMS will take this to be guild_membership.id or players_online resp. players.id, where you really want it to be guild.id. This should explain that you get unexpected values.
As to how the query is built: You could join to the aggregated player data instead. And use alias names that match the tables.
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';
You can change the left outer join (left join) to an inner join (join), if you don't need to see guilds without any player.
I think the problem is here: a.guild_id = id
The id being used is from players, not guilds, as it is still part of the sub-query.
You shouldn't need all those subqueries, JOINs are almost always faster and should usually be first technique tried.
Try this...
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 only counts non-null values; similarly MAX, MIN, and most other aggregate functions ignore null values (only returning null if only null values were processed).
You should consider modifying your query like
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;

Order query result by latest updated date

i am working on two tables,
CREATE TABLE IF NOT EXISTS `users` (
`userid` INT NOT NULL AUTO_INCREMENT ,
`fname` VARCHAR(45) NOT NULL ,
`lname` VARCHAR(45) NOT NULL ,
`usernick` VARCHAR(45) NOT NULL ,
PRIMARY KEY (`userid`) ,
UNIQUE INDEX `usernick_UNIQUE` (`usernick` ASC) )
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `messages` (
`messageid` INT NOT NULL AUTO_INCREMENT ,
`sendid` INT NOT NULL ,
`recid` INT NOT NULL ,
`message` VARCHAR(1000) NOT NULL ,
`date` TIMESTAMP NULL ,
PRIMARY KEY (`messageid`) ,
INDEX `sender_fk_idx` (`sendid` ASC) ,
INDEX `reciever_fk_idx` (`recid` ASC) ,
CONSTRAINT `sender_fk`
FOREIGN KEY (`sendid` )
REFERENCES `users` (`userid` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `reciever_fk`
FOREIGN KEY (`recid` )
REFERENCES `users` (`userid` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
and want a list of user on basis if their last sent message. Eg.
select U.fname,U.lname,U.usernick from messages as M natural join users as U where M.sendid = U.userid and M.recid={$_SESSION['user_id']} group by U.usernick ORDER BY M.date DESC
EXAMPLE:
name msg sent to "RON" on
Alpha 17 aug
Beta 18 aug
Alpha 19 aug
Gamma 20 aug
SO i want a list like,
gamma (last msg on 20)
alpha (last msg on 18)
beta (last msg on 19)
SEE LINK http://sqlfiddle.com/#!2/9dca2/2/0
You almost have it. You just need max(m.date) in the order by clause. Otherwise, an arbitrary date is chosen:
select U.fname,U.lname,U.usernick
from messages as M join
users as U
on M.sendid = U.userid
where M.recid={$_SESSION['user_id']}
group by U.usernick
ORDER BY max(M.date) DESC
I also fixed the join syntax. It is best to avoid natural joins -- that is, be explicit about the join condition. You do this anyway, so I switched the condition to an on clause and the natural join to a join.