OR operator slowing down MySQL query - mysql

I have four tables each relating to the others by some fields: games, teams, altnames, countries.
Wrote this query with an OR operator in an ON clause (see line 6) which slows down the query very much:
select ROUND(AVG(attendance)), competition, team1, countries.* from games
LEFT JOIN (teams
INNER JOIN countries ON ( countries.iso3 = teams.country )
LEFT JOIN altnames ON ( altnames.entityType = "team" AND altnames.season = "1011" AND altnames.entityId = teams.longName )
)
ON ( altnames.altValue = games.team1 OR teams.longName = games.team1 )
where games.season="1011" group by games.competition, games.team1 having AVG(attendance)>= 500 order by AVG(attendance) desc
Query is fast enough when not using the OR, only one of the two conditions at a time:
1.
select ROUND(AVG(attendance)), competition, team1, countries.* from games
LEFT JOIN (teams
INNER JOIN countries ON ( countries.iso3 = teams.country )
LEFT JOIN altnames ON ( altnames.entityType = "team" AND altnames.season = "1011" AND altnames.entityId = teams.longName )
)
ON ( altnames.altValue = games.team1 )
where games.season="1011" group by games.competition, games.team1 having AVG(attendance)>= 500 order by AVG(attendance) desc
2.
select ROUND(AVG(attendance)), competition, team1, countries.* from games
LEFT JOIN (teams
INNER JOIN countries ON ( countries.iso3 = teams.country )
LEFT JOIN altnames ON ( altnames.entityType = "team" AND altnames.season = "1011" AND altnames.entityId = teams.longName )
)
ON ( teams.longName = games.team1 )
where games.season="1011" group by games.competition, games.team1 having AVG(attendance)>= 500 order by AVG(attendance) desc
Any idea why is this happening, and how the first query can be speed up?
EDIT:
Here are the tables:
altnames:
CREATE TABLE IF NOT EXISTS `altnames` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`entityType` enum('team','comp','logo') NOT NULL,
`entityId` varchar(50) NOT NULL DEFAULT '0',
`season` varchar(4) NOT NULL DEFAULT '0',
`altValue` varchar(50) NOT NULL DEFAULT '0',
KEY `id` (`id`),
KEY `entityType_season_altValue` (`entityType`,`season`,`altValue`)
)
countries:
CREATE TABLE IF NOT EXISTS `countries` (
`name` varchar(50) NOT NULL,
`iso2` varchar(2) NOT NULL,
`iso3` varchar(3) NOT NULL,
`color` varchar(7) NOT NULL,
KEY `iso3` (`iso3`)
)
games:
CREATE TABLE IF NOT EXISTS `games` (
`id` int(7) NOT NULL AUTO_INCREMENT,
`competition` varchar(10) NOT NULL,
`season` varchar(4) NOT NULL,
`stage` varchar(10) DEFAULT NULL,
`gamedate` date DEFAULT NULL,
`team1` varchar(50) NOT NULL,
`team2` varchar(50) NOT NULL,
`score1` tinyint(2) DEFAULT NULL,
`score2` tinyint(2) DEFAULT NULL,
`attendance` int(11) DEFAULT NULL,
`location` varchar(100) DEFAULT NULL,
`source` varchar(400) DEFAULT NULL,
`altsource` varchar(400) DEFAULT NULL,
`note` varchar(400) DEFAULT NULL,
KEY `id` (`id`),
KEY `competition_season` (`competition`,`season`),
KEY `team_comp_season` (`team1`,`competition`,`season`)
)
teams:
CREATE TABLE IF NOT EXISTS `teams` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`shortName` varchar(20) DEFAULT NULL,
`longName` varchar(50) NOT NULL,
`urlSlug` varchar(50) DEFAULT NULL,
`country` varchar(3) NOT NULL DEFAULT 'NSP',
`competitions` varchar(50) DEFAULT NULL,
`latitude` float(10,6) DEFAULT '0.000000',
`longitude` float(10,6) DEFAULT '0.000000',
`inactive` tinyint(1) DEFAULT '0',
KEY `id` (`id`),
KEY `long` (`longName`),
KEY `FK_teams_countries` (`country`),
CONSTRAINT `teams_ibfk_1` FOREIGN KEY (`country`) REFERENCES `countries` (`iso3`)
)

I just had a funny idea in mind While waiting for an expert opinion,
Try to switch OR operator with AND.
conditionA OR conditionB
is equivalent to
NOT(NOT(conditionA) and NOT(conditionB))
Good Luck,

Related

How to optimize this heavy MySQL query?

I need to optimize a MySQL query which takes a lot of time to load.
Here it is :
SELECT
p.id,
UNIX_TIMESTAMP(p.last_answer_date) AS last_answer_date_timestamp,
p.sender_id,
p.recipient_id,
p.is_read_sender,
p.last_answer_user_id,
p.is_read_recipient,
(SELECT m.read FROM pm_message m WHERE m.conv_id = p.id AND m.user_id != $user_id ORDER BY m.date DESC LIMIT 1) AS read_status,
(SELECT m.content FROM pm_message m WHERE m.conv_id = p.id ORDER BY m.date DESC LIMIT 1) AS last_message,
(SELECT u.username FROM user u WHERE (u.id = p.sender_id OR u.id = p.recipient_id) AND u.id != $user_id LIMIT 1) AS from_username,
(SELECT u.id FROM user u WHERE (u.id = p.sender_id OR u.id = p.recipient_id) AND u.id != $user_id LIMIT 1) AS from_userid,
(SELECT ui.gender FROM user_info ui WHERE (ui.user_id = p.sender_id OR ui.user_id = p.recipient_id) AND ui.user_id != $user_id LIMIT 1) AS from_gender,
(SELECT ph.thumb_url FROM photo ph, user_info ui WHERE ui.main_photo = ph.id AND (ph.user_id = p.sender_id OR ph.user_id = p.recipient_id) AND ph.user_id != $user_id LIMIT 1) AS from_thumb_url
FROM pm_conv p
WHERE p.sender_id = $user_id OR p.recipient_id = $user_id
ORDER BY p.last_answer_date DESC LIMIT 25;
This query gets me the result I want but it's really slow... And I think that the nested selects is the reason why this query is so slow.
Here are the tables structures for this query :
CREATE TABLE IF NOT EXISTS `photo` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`url` varchar(255) DEFAULT NULL,
`thumb_url` varchar(255) DEFAULT NULL,
`user_id` int(11) NOT NULL,
`date` datetime NOT NULL,
`status` int(11) NOT NULL,
`votes` int(11) DEFAULT '0',
`comments` int(11) DEFAULT '0',
`views` int(11) DEFAULT '0',
`text` text,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `pm_conv` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date` datetime NOT NULL,
`sender_id` int(11) NOT NULL,
`recipient_id` int(11) NOT NULL,
`last_answer_date` datetime NOT NULL,
`nb_messages` int(11) NOT NULL,
`is_read_sender` int(11) NOT NULL,
`is_read_recipient` int(11) NOT NULL DEFAULT '0',
`last_answer_user_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `recipient_id` (`recipient_id`),
KEY `sender_id` (`sender_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `pm_message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date` datetime NOT NULL,
`content` text NOT NULL,
`user_id` int(11) NOT NULL,
`conv_id` int(11) NOT NULL,
`read` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `conv_id` (`conv_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`encrypt_id` varchar(255) DEFAULT NULL,
`register_date` datetime DEFAULT NULL,
`last_login_date` datetime DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`banned` int(11) DEFAULT NULL,
`banned_reason` text,
`first_step_form` int(11) DEFAULT '0',
`status` int(11) DEFAULT NULL,
`valid_snapchat` int(11) DEFAULT '0',
`introduced_forum` int(11) DEFAULT '0',
`referer` varchar(255) DEFAULT NULL,
`allow_social_featuring` int(11) DEFAULT NULL,
`rank` int(11) DEFAULT NULL,
`fb_id` bigint(20) DEFAULT NULL,
`rate_app_status` int(11) DEFAULT NULL,
`last_activity_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `user_info` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`gender` int(11) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`about` text,
`main_photo` int(11) DEFAULT NULL,
`country` varchar(100) DEFAULT NULL,
`city` varchar(100) DEFAULT NULL,
`relation_type` varchar(30) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`fb_link` varchar(255) DEFAULT NULL,
`twitter_link` varchar(255) DEFAULT NULL,
`youtube_link` varchar(255) DEFAULT NULL,
`instagram_link` varchar(255) DEFAULT NULL,
`app_pref_forum` int(11) DEFAULT NULL,
`app_pref_pm` int(11) DEFAULT NULL,
`app_pref_snapchat_request` int(11) DEFAULT NULL,
`browse_invisibly` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `main_photo` (`main_photo`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Can someone help me to optimize this heavy query?
Thanks!
you can see in the explain plan some tables are being accessed by inefficient indexes. try to calculate statistics on all the tables to see if it changes something (using analyze table).
You can join user table in order to get username and id at once instead of having two subqueries, probably you can do the same with pm_message, but it's a little trickier since subqueries have different conditions.
I would also combine user and user_info tables into, as I can see they have one-to-one relation, so it doesn't makes sense to store this data in different tables. This would allow you to get rid off 4th subquery and simplify the 5th one.
In some cases it is better to perform several queries instead of one with subqueries.

sql can't figure out the query

I have three tables:
CREATE TABLE IF NOT EXISTS `contacts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`providerId` int(10) unsigned NOT NULL DEFAULT '0',
`requestId` int(10) unsigned NOT NULL DEFAULT '0',
`status` binary(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
CREATE TABLE IF NOT EXISTS `messages` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`fromuid` int(255) NOT NULL,
`touid` int(255) NOT NULL,
`sentdt` datetime NOT NULL,
`read` tinyint(1) NOT NULL DEFAULT '0',
`readdt` datetime DEFAULT NULL,
`messagetext` longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
KEY `id` (`id`)
)
CREATE TABLE IF NOT EXISTS `users` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`email` varchar(255) DEFAULT NULL,
`mobile` varchar(15) NOT NULL,
`password` varchar(255) NOT NULL,
`city` varchar(255) NOT NULL,
`zip` varchar(15) DEFAULT NULL,
`device` varchar(50) DEFAULT NULL,
`version` varchar(10) DEFAULT NULL,
`photo` varchar(255) DEFAULT NULL,
`created` datetime NOT NULL,
`live` enum('0','1') NOT NULL DEFAULT '1',
`authenticationTime` datetime NOT NULL,
`userKey` varchar(255) DEFAULT NULL,
`IP` varchar(50) DEFAULT NULL,
`port` int(10) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `firstname` (`mobile`,`city`,`zip`)
)
And this SQL query that finds out friends/contacts for specified user (user id 1 in this case):
SELECT u.id
,u.mobile
,u.name
,(NOW() - u.authenticationTime) AS authenticateTimeDifference
,u.IP
,f.providerid
,f.requestid
,f.status
,u.port
FROM contacts f
LEFT JOIN users u ON u.id =
IF (
f.providerid = 1
,f.requestid
,f.providerid
) WHERE (
f.providerid = 1
AND f.status = 1
)
OR f.requestid = 1
That works fine but I want to be able to also join messages table and show user's friends/contacts who have talked latest (meaning latest conversations first) with order by messages.sentdt desc option but I am unable to figure out how to do that, I tried all joins but none worked :(
Your help will be greatly appreciated. Thanks
Update
Here is sample data above query returns:
In that same resultset, I want to be able to sort based on order by messages.sentdt desc but I am not sure how to pull that in and sort resultset by latest message first
Try this:
select u.id
, u.mobile
, u.name
, (NOW() - u.authenticationTime) as authenticateTimeDifference
, u.IP
, f.providerid
, f.requestid
, f.status
, u.port
from contacts f
left join users u
on u.id = if (f.providerid = 1, f.requestid, f.providerid)
left join (select fromuid, max(sentdt) as sentdt from messages group by fromuid) m
on m.fromuid = if (f.providerid = 1, f.providerid, f.requestid)
where (f.providerid = 1 and f.status = 1)
or f.requestid = 1
order by m.sentdt

Spped up MySQL query

I need help speeding up a MySQL query that's running extremely slowly. It's taking over 35 seconds to return 900 rows.
Does anyone have ideas how I can speed things up on this query?
Many thanks in advance
select products.*,
p.price as lowest_price,
products_images.thumbnail
from products
inner join products_categories on products_categories.product_id = products.id
inner join products_colours on products_colours.product_id = products.id
inner join products_quantity_pricing on products_quantity_pricing.product_id = products.id
left join ( select min(price) as price, product_id from products_quantity_pricing group by products_quantity_pricing.product_id ) as p on p.product_id = products.id
inner join products_images on products_images.product_id = products.id
where products.id > 0 group by products.id
order by products.product_name
Here is the setup of the tables involved:
CREATE TABLE IF NOT EXISTS `products_categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` smallint(5) unsigned zerofill NOT NULL,
`category` int(11) NOT NULL,
`sub_category` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1016 ;
CREATE TABLE IF NOT EXISTS `products` (
`product_prefix` varchar(10) NOT NULL,
`id` smallint(5) unsigned zerofill NOT NULL AUTO_INCREMENT,
`supplier_id` int(11) NOT NULL,
`product_code` varchar(50) NOT NULL,
`supplier_product_code` mediumtext NOT NULL,
`product_name` varchar(200) NOT NULL,
`product_description` text NOT NULL,
`print_area` varchar(100) DEFAULT NULL,
`print_position` varchar(100) DEFAULT NULL,
`dimensions` varchar(100) DEFAULT NULL,
`origination` tinytext,
`unit_cost` decimal(9,2) NOT NULL,
`updated` datetime NOT NULL,
`url` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `product_code` (`product_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=901 ;
CREATE TABLE IF NOT EXISTS `products_images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` smallint(5) unsigned zerofill NOT NULL,
`fullsize` varchar(255) NOT NULL,
`midsize` varchar(255) NOT NULL,
`thumbnail` varchar(255) NOT NULL,
`updated` datetime NOT NULL,
`colour_tag` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2402 ;
CREATE TABLE IF NOT EXISTS `products_colours` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` smallint(5) unsigned zerofill NOT NULL,
`colour` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
KEY `product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2546 ;
CREATE TABLE IF NOT EXISTS `products_quantity_pricing` (
`product_id` smallint(5) unsigned zerofill NOT NULL,
`quantity` smallint(6) NOT NULL,
`price` decimal(9,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Add indexes on the product_id columns in all the tables.
In the products_quantity_pricing, a composite index on (product_id, price) will also speed up finding the minimum price for each product. If you create this composite index, you don't need to create a separate index just on product_id; the prefix of a composite index also serves as an index of its own.

sql statement mysql notcorrect

SELECT SUBSTRING(m.own,3,4) as c , (select amphur.AMPHUR_NAME where c = SUBSTRING(m.own,3,4) ),
COUNT(* ) AS cnt
FROM MEMBER AS m
GROUP BY SUBSTRING(m.own,3,4)
order by cnt desc
sql statement mysql
what wrong with code below when i fill
(select amphur.AMPHUR_NAME where c = SUBSTRING(m.own,3,4) )
it error
CREATE TABLE IF NOT EXISTS `member` (
`idmember` int(11) NOT NULL AUTO_INCREMENT,
`own` varchar(255) DEFAULT NULL,
`Sname` varchar(255) DEFAULT NULL,
`Ssurname` varchar(255) DEFAULT NULL,
`Sex` enum('¿','¿') NOT NULL,
`Hno` varchar(255) DEFAULT NULL,
`Moo` varchar(255) DEFAULT NULL,
`tambol` varchar(200) NOT NULL,
`dateofbirth` date DEFAULT NULL,
`migratedate` date DEFAULT NULL,
`status` enum('5','4','3','2','1') DEFAULT '5',
`Unit` int(4) DEFAULT NULL,
`staff1` int(11) DEFAULT NULL,
`staff2` int(11) DEFAULT NULL,
`fathercode` varchar(30) NOT NULL,
`mathercode` varchar(30) NOT NULL,
PRIMARY KEY (`idmember`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=8994 ;
CREATE TABLE IF NOT EXISTS `amphur` (
`AMPHUR_ID` int(5) NOT NULL AUTO_INCREMENT,
`AMPHUR_CODE` varchar(4) COLLATE utf8_unicode_ci NOT NULL,
`AMPHUR_NAME` varchar(150) COLLATE utf8_unicode_ci NOT NULL,
`GEO_ID` int(5) NOT NULL DEFAULT '0',
`PROVINCE_ID` int(5) NOT NULL DEFAULT '0',
`province_name` varchar(80) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`AMPHUR_ID`),
KEY `province_name` (`province_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=999 ;
Your subquery is missing a From clause:
SELECT SUBSTRING(m.own,3,4) as c
, (select amphur.AMPHUR_NAME
From amphur
Where ??? = SUBSTRING(m.own,3,4) )
, COUNT(* ) AS cnt
FROM MEMBER AS m
However, how does the amphur table relate to the member table?
You cannot use aliases in the same level.
Even if you could, you are filtering on non-correlated columns in your subquery: the subquery would just return the record from amphur if there is one record, or an error if there are more.
Could you please provide some sample data and the desired recordset?
there is no "FROM" clause in your select Amphur.amphur_name

MySQL join problem

I want to join a pages table and menu table.
CREATE TABLE IF NOT EXISTS `pages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
`keywords` varchar(255) NOT NULL DEFAULT '',
`description` varchar(255) NOT NULL DEFAULT '',
`path` varchar(255) NOT NULL DEFAULT '',
`content` text NOT NULL,
`status` enum('active','inactive') NOT NULL DEFAULT 'active',
`category_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=21 ;
CREATE TABLE IF NOT EXISTS `menus` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`shortdesc` varchar(255) NOT NULL,
`page_id` varchar(60) NOT NULL,
`status` enum('active','inactive') NOT NULL,
`parentid` int(11) NOT NULL,
`order` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=79 ;
I have errors with the following SQL.
function generateTree(&$tree, $parentid = 0) {
$res = $this->db->query('SELECT M.*, P.name AS PageName
WHERE M.parentid = $parentid
ORDER BY M.order asc, M.parentid asc
FROM menus AS M
LEFT JOIN pages AS P
ON P.id = M.page_id');
...
...
Can you tell what I am doing wrong?
Thanks in advance.
You've got your SQL syntax mixed up
$res = $this->db->query('
SELECT
M.*, P.name AS PageName
FROM
menus AS M
LEFT JOIN pages AS P ON P.id = M.page_id
WHERE
M.parentid = $parentid
ORDER BY
M.order asc, M.parentid asc
');
BTW, you should bot be using variables in the SQL string. Use parameterized queries instead (mysyqli*).