Whenever I am running this query, it takes about 25-30 seconds for it to run. As you can see, the most advanced thing here is to calculate two coalesces within subqueries.
SELECT
g.name,
g.id,
(
SELECT
COALESCE (
SUM(result2 / result1) * (
SUM(IF(result2 != 0, 1, 0)) * 0.1
),
0
) AS res
FROM
gump.war gwr
WHERE
started = 1
AND (UNIX_TIMESTAMP(time) + 7 * 24 * 60 * 60) > UNIX_TIMESTAMP()
AND gwr.guild1 = g.id
AND gwr.winner = g.id
) + (
SELECT
COALESCE (
SUM(result1 / result2) * (
SUM(IF(result1 != 0, 1, 0)) * 0.1
),
0
) AS res1
FROM
gumb.war gwr
WHERE
started = 1
AND (UNIX_TIMESTAMP(time) + 7 * 24 * 60 * 60) > UNIX_TIMESTAMP()
AND gwr.guild2 = g.id
AND gwr.winner = g.id
) AS avg
FROM
gumb.guild g
ORDER BY
avg DESC,
g.point DESC,
g.experience DESC LIMIT 10;
Table structures/schemas:
CREATE TABLE `guild` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(12) NOT NULL DEFAULT '',
`owner` int(10) unsigned NOT NULL DEFAULT '0',
`level` tinyint(2) DEFAULT NULL,
`experience` int(11) DEFAULT NULL,
`win` int(11) NOT NULL DEFAULT '0',
`draw` int(11) NOT NULL DEFAULT '0',
`loss` int(11) NOT NULL DEFAULT '0',
`point` int(11) NOT NULL DEFAULT '0',
`account` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
CREATE TABLE `war` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`guild1` int(10) unsigned NOT NULL DEFAULT '0',
`guild2` int(10) unsigned NOT NULL DEFAULT '0',
`time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`type` tinyint(2) unsigned NOT NULL DEFAULT '0',
`price` int(10) unsigned NOT NULL DEFAULT '0',
`score` int(10) unsigned NOT NULL DEFAULT '0',
`started` tinyint(1) NOT NULL DEFAULT '0',
`winner` int(11) NOT NULL DEFAULT '-1',
`result1` int(11) NOT NULL DEFAULT '0',
`result2` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
Indexes will definitely help, indexing fields used in JOIN criteria and in WHERE clauses carries the most impact.
Generic syntax example:
CREATE INDEX idx_col1col2 ON tbl_Test (Col1, Col2)
You don't likely want to just cram every field used into one index, and you likely shouldn't create an index for each field either.
There are many resources for helping you understand how to build your indexes, here are a couple items:
MySQL CREATE INDEX Syntax
MySQL Index Optimization
Related
I'm having an issue with a particular SQL query running on one of my databases:
select
`map`,
`tier`,
(select count(*) from mapzones a where a.map = b.map
and `track` = 0 and `type` > 0) as `stages`,
(select count(*) from mapzones a where a.map = b.map
and `track` > 0 and `type` > 0) as `bonuses`
from
maptiers b
order by `map` asc;
The response takes ~28-30 seconds to execute, which is much too slow for what I need.
Strangely, when I execute a similar query on another database for a separate gamemode, it takes <100ms, usually <50ms:
select
`mapname`,
`tier`,
(select count(*) from ck_zones a where a.mapname = b.mapname
and `zonegroup` = 0 and (zonetype = 2
or zonetype = 3)) as `stages`,
(select count(*) from ck_zones a where a.mapname = b.mapname
and `zonegroup` > 0 and `zonetype` = 0) as `bonuses`
from
ck_maptier b
order by `mapname` asc;
Server Info
Server version: 8.0.29 - MySQL Community Server - GPL
Collation/Type
First Query's Database (BhopTimer)
Second Query's Database (SurfTimer)
(from Comment)
SELECT a.map, a.tier, b.stage_count
FROM maptiers a
INNER JOIN
(
SELECT DISTINCT map,
COUNT(CASE WHEN (type > 0 AND track = 0) THEN 1 END)
OVER (PARTITION BY map) AS `stage_count`
FROM mapzones
) AS b ON a.map = b.map
ORDER BY a.map;
Create Tables
CREATE TABLE `maptiers` (
`map` varchar(255) NOT NULL,
`tier` int NOT NULL DEFAULT '1',
PRIMARY KEY (`map`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `mapzones` (
`id` int NOT NULL AUTO_INCREMENT,
`map` varchar(255) NOT NULL,
`type` int DEFAULT NULL,
`corner1_x` float DEFAULT NULL,
`corner1_y` float DEFAULT NULL,
`corner1_z` float DEFAULT NULL,
`corner2_x` float DEFAULT NULL,
`corner2_y` float DEFAULT NULL,
`corner2_z` float DEFAULT NULL,
`destination_x` float NOT NULL DEFAULT '0',
`destination_y` float NOT NULL DEFAULT '0',
`destination_z` float NOT NULL DEFAULT '0',
`track` int NOT NULL DEFAULT '0',
`flags` int DEFAULT '0',
`data` int DEFAULT '0',
`form` tinyint DEFAULT NULL,
`target` varchar(63) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14612 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `ck_maptier` (
`mapname` varchar(54) NOT NULL,
`tier` int NOT NULL,
`maxvelocity` float NOT NULL DEFAULT '3500',
`announcerecord` int NOT NULL DEFAULT '0',
`gravityfix` int NOT NULL DEFAULT '1',
`ranked` int NOT NULL DEFAULT '1',
`stages` int DEFAULT NULL,
`bonuses` int DEFAULT NULL,
PRIMARY KEY (`mapname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `ck_zones` (
`mapname` varchar(54) NOT NULL,
`zoneid` int NOT NULL DEFAULT '-1',
`zonetype` int DEFAULT '-1',
`zonetypeid` int DEFAULT '-1',
`pointa_x` float DEFAULT '-1',
`pointa_y` float DEFAULT '-1',
`pointa_z` float DEFAULT '-1',
`pointb_x` float DEFAULT '-1',
`pointb_y` float DEFAULT '-1',
`pointb_z` float DEFAULT '-1',
`vis` int DEFAULT '0',
`team` int DEFAULT '0',
`zonegroup` int NOT NULL DEFAULT '0',
`zonename` varchar(128) DEFAULT NULL,
`hookname` varchar(128) DEFAULT 'None',
`targetname` varchar(128) DEFAULT 'player',
`onejumplimit` int NOT NULL DEFAULT '1',
`prespeed` int NOT NULL DEFAULT '350',
PRIMARY KEY (`mapname`,`zoneid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Samples
First Query (BhopTimer)
maptiers.sql
mapzones.sql
Second Query (SurfTimer)
ck_maptier.sql
ck_zones.sql
See if they run faster when turned inside out:
SELECT b.map,
b.tier,
x.stages,
x.bonuses
FROM ( SELECT map,
SUM(track = 0 AND type = 0) AS stages,
SUM(track > 0 AND type > 0) AS bonuses
FROM mapzones
GROUP BY map ) AS x
JOIN maptiers AS b ON b.map = x.map
ORDER BY map ASC;
INDEXes:
mapzones: INDEX(map, track, type)
maptiers: INDEX(map, tier)
(For further discussion, please provide SHOW CREATE TABLE for each table. Also: EXPLAIN SELECT ...)
I have a query like that:
SELECT * , (
( 1584392725 ) - ( suprayts.time )
) AS timeDiff
FROM (
`suprayts`
)
WHERE `suprayts`.`is_deleted` = '0'
AND `suprayts`.`is_approved` =1
AND `suprayts`.`username` != 'rayben1'
AND `suprayts`.`time` >1584306325
ORDER BY `suprayts`.`is_boosted_by_user` DESC , `suprayts`.`id` ASC
LIMIT 10
This query runs very slow (avg 0.2 seconds), if i delete the following line:
AND `suprayts`.`username` != 'rayben1'
It runs 10x faster. (avg 0.02 secs) How can i speed up this query?
My indexes:
Explain:
My table:
CREATE TABLE IF NOT EXISTS `suprayts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(15) CHARACTER SET utf8 NOT NULL,
`question` varchar(70) COLLATE utf8mb4_unicode_ci NOT NULL,
`suprayt_photo` varchar(50) CHARACTER SET utf8 NOT NULL,
`time` int(11) NOT NULL,
`endTime` int(11) NOT NULL,
`datetext` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`like_count` int(10) unsigned NOT NULL DEFAULT '0',
`dislike_count` int(10) unsigned NOT NULL DEFAULT '0',
`is_approved` bit(1) NOT NULL DEFAULT b'0',
`is_deleted` enum('1','0') CHARACTER SET utf8 NOT NULL DEFAULT '0',
`is_end_notification_sent` bit(1) NOT NULL DEFAULT b'0',
`open_vote` enum('0','1') CHARACTER SET utf8 NOT NULL DEFAULT '1',
`boost` int(11) NOT NULL DEFAULT '0',
`is_boosted_by_user` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `username_3` (`username`,`suprayt_photo`),
KEY `id` (`id`,`time`,`is_approved`,`is_deleted`),
KEY `username` (`username`,`is_deleted`),
KEY `username_2` (`username`,`datetext`),
KEY `id_2` (`id`,`username`,`time`,`is_approved`,`is_deleted`),
KEY `username_4` (`username`,`time`,`is_approved`,`is_deleted`),
KEY `ix1` (`id`,`time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=130789 ;
Depending on the SQL pre-compiler if one is being used, or the SQL itself, possible it is sensitive to the order of the query "where" clause not being in DB key order and it is not using the index and instead doing a sequential scan? Just to eliminate the possibility, try putting the WHERE items in the DB index key order.
USE NOT EXISTS
SELECT * , (
( 1584392725 ) - ( suprayts.time )
) AS timeDiff
FROM (
`suprayts`
)
WHERE `suprayts`.`is_deleted` = '0'
AND `suprayts`.`is_approved` =1
AND NOT EXISTS (
SELECT x.no FROM (SELECT 1 AS no) x WHERE `suprayts`.`username` = 'rayben1'
)
AND `suprayts`.`time` >1584306325
ORDER BY `suprayts`.`is_boosted_by_user` DESC , `suprayts`.`id` ASC
LIMIT 10
I have following mysql query which is taking long time(40s) to load the results.
SELECT SQL_CALC_FOUND_ROWS blog_posts.ID FROM blog_posts
LEFT JOIN blog_term_relationships AS tt0 ON (blog_posts.ID = tt0.object_id)
LEFT JOIN blog_term_relationships AS tt1 ON (blog_posts.ID = tt1.object_id)
LEFT JOIN blog_term_relationships AS tt2 ON (blog_posts.ID = tt2.object_id)
LEFT JOIN blog_term_relationships AS tt3 ON (blog_posts.ID = tt3.object_id)
WHERE 1=1
AND ( ( tt0.term_taxonomy_id IN (141,177) AND tt1.term_taxonomy_id IN (2389,2390) )
OR ( tt2.term_taxonomy_id IN (167,1169,1715) AND tt3.term_taxonomy_id IN (2519,2520) ) )
AND blog_posts.post_type = 'post' AND (blog_posts.post_status = 'publish')
GROUP BY blog_posts.ID ORDER BY blog_posts.post_date ASC LIMIT 0, 20
Is there any way to optimize this query.
Edit:
This is related to wordpress and this query was automatically create from the wp_query.
Table structures as bellow,
blog_posts table:
CREATE TABLE `blog_posts` (
`ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`post_author` bigint(20) unsigned NOT NULL DEFAULT '0',
`post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`post_date_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`post_content` longtext NOT NULL,
`post_title` text NOT NULL,
`post_excerpt` text NOT NULL,
`post_status` varchar(20) NOT NULL DEFAULT 'publish',
`comment_status` varchar(20) NOT NULL DEFAULT 'open',
`ping_status` varchar(20) NOT NULL DEFAULT 'open',
`post_password` varchar(255) NOT NULL DEFAULT '',
`post_name` varchar(200) NOT NULL DEFAULT '',
`to_ping` text NOT NULL,
`pinged` text NOT NULL,
`post_modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`post_modified_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`post_content_filtered` longtext NOT NULL,
`post_parent` bigint(20) unsigned NOT NULL DEFAULT '0',
`guid` varchar(255) NOT NULL DEFAULT '',
`menu_order` int(11) NOT NULL DEFAULT '0',
`post_type` varchar(20) NOT NULL DEFAULT 'post',
`post_mime_type` varchar(100) NOT NULL DEFAULT '',
`comment_count` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`),
KEY `type_status_date` (`post_type`,`post_status`,`post_date`,`ID`),
KEY `post_parent` (`post_parent`),
KEY `post_author` (`post_author`),
KEY `post_name` (`post_name`(191))
) ENGINE=MyISAM AUTO_INCREMENT=125636 DEFAULT CHARSET=utf8
blog_term_relationships table:
CREATE TABLE `blog_term_relationships` (
`object_id` bigint(20) unsigned NOT NULL DEFAULT '0',
`term_taxonomy_id` bigint(20) unsigned NOT NULL DEFAULT '0',
`term_order` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`object_id`,`term_taxonomy_id`),
KEY `term_taxonomy_id` (`term_taxonomy_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
EXPLAIN QUERY:
enter image description here
Reformulate
SELECT SQL_CALC_FOUND_ROWS blog_posts.ID
FROM (
(
SELECT object_id
FROM blog_term_relationships AS tt0
JOIN blog_term_relationships AS tt1 USING(object_id)
WHERE tt0.term_taxonomy_id IN (141,177)
AND tt1.term_taxonomy_id IN (2389,2390)
)
UNION DISTINCT
(
SELECT object_id
FROM blog_term_relationships AS tt2
JOIN blog_term_relationships AS tt3 USING(object_id)
WHERE tt2.term_taxonomy_id IN (167,1169,1715)
AND tt3.term_taxonomy_id IN (2519,2520) )
) AS tt
JOIN blog_posts ON blog_posts.ID = tt.object_id
WHERE blog_posts.post_type = 'post'
AND blog_posts.post_status = 'publish'
ORDER BY blog_posts.post_date ASC
LIMIT 0, 20
This gets rid of the GROUP BY and does several other things to speed up the query.
Prefix index
`post_name` varchar(200) NOT NULL DEFAULT '',
KEY `post_name` (`post_name`(191))
) ENGINE=MyISAM AUTO_INCREMENT=125636 DEFAULT CHARSET=utf8
Make up your mind -- 191 is for version 5.6 with utf8mb4 (which you have not specified); 191 is so close to 200, you may as well make it VARCHAR(191). Getting rid of the prefix index is likely to speed up some of your queries.
InnoDB
Don't use MyISAM, move to InnoDB. That is for performance, robustness, etc. That will coincidentally fix an inefficiency in KEY term_taxonomy_id).
SQL_CALC_FOUND_ROWS
SQL_CALC_FOUND_ROWS is costly. It's purpose is passe.
I have been left with a report which returns rows at an awfully slow rate. I feel I need to redo without so many or any sub-queries. But I am having a complete brain freeze to how to attempt this.
I have looked at indexes and the keys are not unique enough, which forces a full table scan time. Is there any way I can pull certain information from the other tables using a separate query, adding that as a variable and use it in the main query. As really the result of this query is only a few lines.
Are there any tips or tricks, that I could use to optimise or correct this SQL statement to speed it up.
(EDIT) I have added some create code for the tables.
SELECT
case when (select count(ag.`PKEY`) - count(ag.`ANSWERTIME`) from acdcallinformation ag
where (ag.`COMPLETED`) = 1 and answertime is null and time(ag.INSTIME) and DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()) and ag.skillid = acdcallinformation.skillid) is null
then 0
else
(select count(ag.`PKEY`) - count(ag.`ANSWERTIME`) from acdcallinformation ag where (ag.`COMPLETED`) = 1
and answertime is null and DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()) and ag.skillid = acdcallinformation.skillid)
end as LostCalls,
case when count(acdcallinformation.idleonqueue) is null then 0 else count(acdcallinformation.idleonqueue) end as CountCallsACD,
case when count(acdcallinformation.`ANSWERTIME`) is null then 0 else count(acdcallinformation.`ANSWERTIME`) end AS acdcallinformation_ANSWERED,
(select skillinfo.skillname from skillinfo where skillinfo.pkey = acdcallinformation.skillid) AS acdcallinformation_SKILLIDTEXT,
(select count(pkey) from acdcallinformation age
where DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()) and age.skillid = acdcallinformation.skillid and (age.`COMPLETED`) = 0 and answertime is null
and SKILLID in (select SKILLID
from
callcenterinformation
where time > (now() - INTERVAL 5 SECOND) and callswaiting > 0)) as Waiting,
-- count(acdcallinformation.`PKEY`) as CallsWaiting,
acdcallinformation.`DATEOFCALL` AS acdcallinformation_DATEOFCALL,
acdcallinformation.`FIRSTRINGONQUEUE` AS acdcallinformation_FIRSTRINGONQUEUE,
case when acdcallinformation.`CONNECTTIME` is null then time('00:00:00') else acdcallinformation.`CONNECTTIME` end AS acdcallinformation_CONNECTTIME,
acdcallinformation.`CALLSTATEBEFOREIDLE` AS acdcallinformation_CALLSTATEBEFOREIDLE,
case when acdcallinformation.`AGENTRINGTIME` is null then time('00:00:00') else acdcallinformation.`AGENTRINGTIME` end AS acdcallinformation_AGENTRINGTIME,
acdcallinformation.`IDLEONQUEUE` AS acdcallinformation_IDLEONQUEUE,
acdcallinformation.`DDI` AS acdcallinformation_DDI,
acdcallinformation.`CLIP` AS acdcallinformation_CLIP,
acdcallinformation.`SKILLID` AS acdcallinformation_SKILLID,
acdcallinformation.`ACTIONTYPE` AS acdcallinformation_ACTIONTYPE,
acdcallinformation.`ACTIONDESTINATION` AS acdcallinformation_ACTIONDESTINATION,
acdcallinformation.`COMPLETED` AS acdcallinformation_COMPLETED,
acdcallinformation.`HANDLED` AS acdcallinformation_HANDLED,
acdcallinformation.`CONFIRMED` AS acdcallinformation_CONFIRMED,
(
SELECT
cal.`AGENTSREADY` AS callcenterinformation_AGENTSREADY
FROM
`callcenterinformation` cal
WHERE cal.skillid <> 1 and acdcallinformation.skillid = skillid order by pkey desc limit 1,1) as agentsready
FROM
`acdcallinformation` acdcallinformation
where DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()- interval 1 day )
group by (select skillinfo.skillname from skillinfo where skillinfo.pkey = acdcallinformation.skillid);
CREATE TABLE `callcenterinformation` (
`INSTIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`PKEY` INT(11) NOT NULL,
`SKILLID` INT(11) NULL DEFAULT '0',
`DATE` DATE NULL DEFAULT NULL,
`TIME` TIME NULL DEFAULT NULL,
`AGENTSLOGGEDIN` INT(11) NULL DEFAULT '0',
`AGENTSREADY` INT(11) NULL DEFAULT '0',
`AGENTSRINGING` INT(11) NULL DEFAULT '0',
`AGENTSCONNECTED` INT(11) NULL DEFAULT '0',
`AGENTSINPAUSE` INT(11) NULL DEFAULT '0',
`AGENTSINWRAPUP` INT(11) NULL DEFAULT '0',
`CALLSWAITING` INT(11) NULL DEFAULT '0',
`COMPLETED` TINYINT(1) NULL DEFAULT '0',
`HANDLED` TINYINT(1) NULL DEFAULT '0',
`CONFIRMED` TINYINT(1) NULL DEFAULT '0',
PRIMARY KEY (`PKEY`),
INDEX `DATE` (`DATE`),
INDEX `TIME` (`TIME`),
INDEX `SKILLID` (`SKILLID`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;
CREATE TABLE `acdcallinformation` (
`INSTIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`PKEY` INT(11) NOT NULL,
`DATEOFCALL` DATE NULL DEFAULT NULL,
`FIRSTRINGONQUEUE` TIME NULL DEFAULT NULL,
`CONNECTTIME` TIME NULL DEFAULT NULL,
`CALLSTATEBEFOREIDLE` INT(11) NULL DEFAULT '0',
`AGENTRINGTIME` TIME NULL DEFAULT NULL,
`ANSWERTIME` TIME NULL DEFAULT NULL,
`IDLEONQUEUE` TIME NULL DEFAULT NULL,
`DDI` TEXT NULL,
`CLIP` TEXT NULL,
`SKILLID` INT(11) NULL DEFAULT '0',
`ACTIONTYPE` INT(11) NULL DEFAULT '0',
`ACTIONDESTINATION` TEXT NULL,
`COMPLETED` TINYINT(1) NULL DEFAULT '0',
`HANDLED` TINYINT(1) NULL DEFAULT '0',
`CONFIRMED` TINYINT(1) NULL DEFAULT '0',
PRIMARY KEY (`PKEY`),
INDEX `DATEOFCALL` (`DATEOFCALL`),
INDEX `IDLEONQUEUE_HANDLED` (`IDLEONQUEUE`, `HANDLED`),
INDEX `SKILLID` (`SKILLID`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;
CREATE TABLE `skillinfo` (
`INSTIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`PKEY` INT(11) NOT NULL,
`SKILLNAME` TEXT NULL,
`CLIP` TEXT NULL,
`WRAPUPTIMELENGTH` INT(11) NULL DEFAULT '0',
`MAXRINGTIMELENGTH` INT(11) NULL DEFAULT '0',
`FORCEDTICKET` TINYINT(1) NULL DEFAULT '0',
`STATEAFTERWRAPUP` INT(11) NULL DEFAULT '0',
`STATEAFTERUNANSWEREDCALL` INT(11) NULL DEFAULT '0',
`ACTIONTYPE` INT(11) NULL DEFAULT '0',
`ACTIONDESTINATION` TEXT NULL,
`DEFLECTAFTERCOURTESY` TINYINT(1) NULL DEFAULT '0',
`MAXOVERALLRINGTIMELENGTH` INT(11) NULL DEFAULT '0',
`AUTOCLIP` TINYINT(1) NULL DEFAULT '0',
`OUTGOINGSETTINGSACTIVE` TINYINT(1) NULL DEFAULT '0',
`NUMPLANIDENTIFIER` INT(11) NULL DEFAULT '0',
`TYPEOFNUMBER` INT(11) NULL DEFAULT '0',
`CLIR` INT(11) NULL DEFAULT '0',
`OUTGOINGROUTEID` INT(11) NULL DEFAULT '0',
`USELASTAGENT` TINYINT(1) NULL DEFAULT '0',
`CLIPROUTINGACTIVE` TINYINT(1) NULL DEFAULT '0',
`USETHRESHOLD` TINYINT(1) NULL DEFAULT '0',
`NORMALLOADTHRESHOLD` INT(11) NULL DEFAULT '0',
`OVERLOADTHRESHOLD` INT(11) NULL DEFAULT '0',
`STATEAFTERFORWARD` INT(11) NULL DEFAULT '0',
`CALLDISTTYPE` INT(11) NULL DEFAULT '0',
`USERGROUPID` INT(11) NULL DEFAULT '0',
`EXTERNALCONTROL` TINYINT(1) NULL DEFAULT '0',
`LASTAGENTLIMIT` INT(11) NULL DEFAULT '0',
PRIMARY KEY (`PKEY`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;
Too be honest, there is so much 'wrong' with this query it just isn't fun anymore =/
Some thoughts:
IFNULL() is much more readable than CASE WHEN <field> IS NULL THEN constant ELSE <field> END, especially if turns out to be a sub-query.
AFAIK COUNT(*) will always return 0, even if nothing is found. Thus, there is no need to write an IFNULL() around it
COUNT(field) only counts the non-NULL records for this field, but again, if nothing is found it will return 0, so no need for an IFNULL() around it
You should teach yourself how to JOIN tables as it's (much) better practice than using correlated sub-queries all over the place.
I don't know much about mysql, but it seems to me that you're killing your performance by putting casts and functions around the fields that otherwise seem to have a useful index. I'm pretty sure that due to these constructions the engine simply is not able to use said indexes causing performance to go down the drain. eg. I would try to rewrite
AND DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()) into something like AND DATEOFCALL >= CUR_DATE(), after all, both sides are dates (= numbers)
DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()- interval 1 day) into DATEOFCALL >= date(now()- interval 1 day) for the very same reason
I'm also not sure what time(ag.INSTIME) should do ?!?! Is it true whenever the time is different from 00:00:00 ?
I'm VERY surprised this query actually compiles at all as you seem to GROUP BY on just the skillname, but also fetch quite a lot of other fields from the table (e.g. idleonqueue). From an MSSQL background that should not work.. I guess mysql is different although I do wonder what the result will be like.
Anyway, trying to apply some of the above to your query I end up with below. I doubt it will be 'much faster'; it might be just a bit, but I'd consider it a step forward in your mission to clean it up further...
Good luck!
SELECT (SELECT COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`)
FROM acdcallinformation ag
WHERE ag.`COMPLETED` = 1
AND answertime is null
AND time(ag.INSTIME)
AND ag.DATEOFCALL >= CURDATE()
AND ag.skillid = info.skillid) AS LostCalls,
COUNT(info.idleonqueue) AS CountCallsACD,
COUNT(info.`ANSWERTIME`) AS acdcallinformation_ANSWERED,
skillinfo.skillname AS acdcallinformation_SKILLIDTEXT,
(SELECT COUNT(pkey)
FROM acdcallinformation age
WHERE age.DATEOFCALL >= CURDATE()
AND age.skillid = info.skillid
AND age.`COMPLETED` = 0
AND age.answertime is null
AND age.SKILLID IN (SELECT SKILLID
FROM callcenterinformation cci
WHERE cci.time > (now() - INTERVAL 5 SECOND)
AND cci.callswaiting > 0)) AS Waiting,
-- count(info.`PKEY`) AS CallsWaiting,
info.`DATEOFCALL` AS acdcallinformation_DATEOFCALL,
info.`FIRSTRINGONQUEUE` AS acdcallinformation_FIRSTRINGONQUEUE,
IFNULL(info.`CONNECTTIME`, time('00:00:00')) AS acdcallinformation_CONNECTTIME,
info.`CALLSTATEBEFOREIDLE` AS acdcallinformation_CALLSTATEBEFOREIDLE,
IFNULL(info.`AGENTRINGTIME`, time('00:00:00')) AS acdcallinformation_AGENTRINGTIME,
info.`IDLEONQUEUE` AS acdcallinformation_IDLEONQUEUE,
info.`DDI` AS acdcallinformation_DDI,
info.`CLIP` AS acdcallinformation_CLIP,
info.`SKILLID` AS acdcallinformation_SKILLID,
info.`ACTIONTYPE` AS acdcallinformation_ACTIONTYPE,
info.`ACTIONDESTINATION` AS acdcallinformation_ACTIONDESTINATION,
info.`COMPLETED` AS acdcallinformation_COMPLETED,
info.`HANDLED` AS acdcallinformation_HANDLED,
info.`CONFIRMED` AS acdcallinformation_CONFIRMED,
(SELECT cal.`AGENTSREADY` AS callcenterinformation_AGENTSREADY
FROM `callcenterinformation` cal
WHERE cal.skillid <> 1
AND cal.skillid = info.skillid
ORDER BY pkey DESC LIMIT 1,1) AS agentsready
FROM `acdcallinformation` info
JOIN `skillinfo`
ON skillinfo.pkey = info.skillid
WHERE info.DATEOFCALL >= (date(now()- interval 1 day ))
GROUP BY skillinfo.skillname ;
i have these subqueries in a main query used to fetch some events:
SELECT [...],
(SELECT COUNT(*) FROM WEventUser WHERE WEventUser.eID=e.eID AND favorited=1) as numfavorited,
(SELECT COUNT(*) FROM WEventUser WHERE WEventUser.eID=e.eID AND subscribed=1) as numsubscribed,
(SELECT COUNT(*) FROM WEventUser WHERE eID=e.eID AND WEventUser.uID=2 AND favorited=1) as favorited,
(SELECT COUNT(*) FROM WEventUser WHERE eID=e.eID AND WEventUser.uID=2 AND subscribed=1) as subscribed,
[...] WHERE...etc.
structure of WEventUser is quite simple
CREATE TABLE IF NOT EXISTS `WEventUser` (
`eID` int(10) unsigned NOT NULL auto_increment,
`uID` int(10) unsigned NOT NULL,
`favorited` int(1) unsigned default '0',
`subscribed` int(1) unsigned default '0',
PRIMARY KEY (`eID`,`uID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
These subqueries are really expensive. Can you help me finding an alternative (like a single join)?
Thanks in advance!
EDIT:
I'm selecting from a main WEvents table that is:
CREATE TABLE IF NOT EXISTS `wevents` (
`eID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uID` int(10) unsigned DEFAULT NULL,
`ecID` int(10) unsigned NOT NULL,
`eName` varchar(64) NOT NULL,
`eDescription` longtext,
`eIsActive` varchar(1) NOT NULL DEFAULT '0',
`eIsValidated` tinyint(4) NOT NULL DEFAULT '-1',
`eDateAdded` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`eDateModified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`eID`,`ecID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
You shouldn't use a subqueries, it is enough to count values in the COUNT function, e.g. -
SELECT [...],
COUNT(IF(wu.favorited = 1, 1, NULL)) as numfavorited,
COUNT(IF(wu.subscribed = 1, 1, NULL)) as numsubscribed,
COUNT(IF(wu.uID=2 AND wu.favorited=1, 1, NULL)) as favorited,
COUNT(IF(wu.uID=2 AND wu.favorited = 1, 1, NULL)) as subscribed,
[...]
FROM
WEventUser wu
WHERE...etc.
You can easily use this one if you want to join WEventUser with another table.