Convert not in subquery to join - mysql

I am not sure what I am missing in a query where i am trying to convert a not in subquery to join.
Here is my original query that works perfectly for me:
select
battery_id
from
battery_price
where
clinic_id = 2
and battery_id not in
(
select battery_id
from battery_price
where clinic_id = 4569
)
;
Here is the query that I am trying and it does not work because it does not give me any null field:
select leftq.battery_id as leftf, rightq.battery_id as rightf
from
(
select
battery_id
from
battery_price
where
clinic_id = 2
) as leftq
left join
(
select battery_id
from battery_price
where clinic_id = 4569
) as rightq
on rightq.battery_id = leftq.battery_id
;
Here is the table schema:
CREATE TABLE `battery_price` (
`battery_price_id` int(11) NOT NULL AUTO_INCREMENT,
`clinic_id` int(11) NOT NULL DEFAULT '0',
`battery_id` int(11) NOT NULL DEFAULT '0',
`retail_price` decimal(10,2) NOT NULL DEFAULT '0.00',
`actual_cost` decimal(10,2) NOT NULL DEFAULT '0.00',
`provider_cost` decimal(10,2) NOT NULL DEFAULT '0.00',
`in_use` tinyint(1) NOT NULL DEFAULT '1',
`sales_tax_id1` int(11) NOT NULL DEFAULT '0',
`sales_tax_id2` int(11) NOT NULL DEFAULT '0',
`sales_tax_id3` int(11) NOT NULL DEFAULT '0',
`sales_tax_id4` int(11) NOT NULL DEFAULT '0',
`sales_tax_id5` int(11) NOT NULL DEFAULT '0',
`sales_tax_category_id` int(11) NOT NULL DEFAULT '0',
`price_locked` tinyint(1) NOT NULL DEFAULT '1',
`item_number` varchar(50) NOT NULL DEFAULT '',
`last_update` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_edit_user_id` int(11) DEFAULT '0',
PRIMARY KEY (`battery_price_id`),
KEY `battery_id` (`battery_id`),
KEY `battery_id_2` (`battery_id`),
KEY `battery_id_3` (`battery_id`)
) ENGINE=MyISAM AUTO_INCREMENT=2639 DEFAULT CHARSET=latin1

Try this one:
select
t.battery_id
from
battery_price t
left outer join battery_price u
on t.battery_id = u.battery_id
and u.clinic_id = 4569
where t.clinic_id = 2
and u.battery_id is NULL

Although I'm not quite sure why you want to select a NULL column (if you don't, just omit the second selected column after the comma), I think this query solves your problem:
select battery_price.battery_id as leftf, rightq.battery_id as rightf
from battery_price left join
(
select battery_id
from battery_price
where clinic_id = 4569
) as rightq
on rightq.battery_id = battery_price.battery_id
where battery_price.clinic_id = 2 and rightq.battery_id is null;

Related

optimize the mysql query

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.

MySQL query is extremely slow

I have a complex SQL query that linked 6 tables and I found out that it takes about 2 minutes (which I think it shouldn't) to display the result. The result will return about 5400++ records. Is there any way to redesign the query so that it will be able to execute a bit faster? Thank you for help!
My current query is:
SELECT *,
(SELECT MasterDescription FROM sys_master_code WHERE MasterId = mktg_account.CategoryId) AS CustomerCategory,
(CASE WHEN mktg_account.BusinessSourceId = 97 THEN mktg_account.BusinessSourceOthers ELSE (SELECT MasterDescription FROM sys_master_code WHERE MasterId = mktg_account.BusinessSourceId) END) AS BusinessSource,
(SELECT TreeName FROM sys_tree WHERE TreeId = mktg_account.TreeId) AS PhaseName,
(SELECT SolicitorName FROM sys_solicitor WHERE sys_solicitor.SolicitorId =(SELECT SolicitorId FROM mktg_spa WHERE mktg_spa.AccountId = mktg_account.AccountId)) AS SolicitorName,
(SELECT MasterCode FROM sys_master_code WHERE sys_master_code.MasterId = mktg_account.AgentCompany) AS AgentCompanyText,
(SELECT MasterCode FROM sys_master_code WHERE sys_master_code.MasterId = mktg_account.FinancialTypeId) AS FinanceSource,
(SELECT UserName FROM sys_user WHERE UserId = mktg_account.CreatedBy) AS CreatedByName
FROM mktg_account
WHERE TreeId IN (SELECT TreeId FROM sys_user_access_right WHERE UserId = 4)
AND IsApproved = '1' AND IsDeleted <>'1'
ORDER BY AccountNo
CREATE TABLE IF NOT EXISTS mktg_account (
AccountId int(10) unsigned NOT NULL AUTO_INCREMENT,
AccountNo varchar(30) NOT NULL DEFAULT '',
AccountStatus varchar(1) DEFAULT NULL,
TreeId int(10) unsigned NOT NULL DEFAULT '0',
SalesDate datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PurchasePrice double NOT NULL DEFAULT '0',
SalesPersonId int(10) unsigned DEFAULT NULL,
FinancialTypeId int(10) unsigned DEFAULT NULL,
BillingCustomerId int(10) unsigned DEFAULT NULL,
EventId int(11) NOT NULL DEFAULT '0',
CategoryId int(11) NOT NULL DEFAULT '0',
RealEstateAgentId int(11) NOT NULL DEFAULT '0',
BusinessSourceId int(10) unsigned DEFAULT NULL,
BusinessSourceOthers varchar(300) DEFAULT NULL,
SalesPromotionId int(10) unsigned DEFAULT NULL,
Remarks text,
AgentName varchar(100) NOT NULL DEFAULT '',
AgentCompany varchar(100) NOT NULL DEFAULT '',
AgentContact varchar(100) DEFAULT NULL,
AgentRemarks text,
CreatedDateTime datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
CreatedBy int(10) unsigned NOT NULL DEFAULT '0',
PurgedDateTime datetime DEFAULT NULL,
PurgedBy int(10) unsigned DEFAULT NULL,
PurgedIP varchar(20) DEFAULT NULL,
IsDeleted tinyint(3) unsigned NOT NULL DEFAULT '0',
IsApproved tinyint(3) unsigned NOT NULL DEFAULT '1',
IsPurge tinyint(3) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (AccountId)
)
CREATE TABLE IF NOT EXISTS sys_master_code (
MasterId int(10) unsigned NOT NULL AUTO_INCREMENT,
MasterType varchar(20) NOT NULL DEFAULT '',
MasterCode varchar(100) NOT NULL DEFAULT '',
MasterDescription varchar(500) NOT NULL DEFAULT '',
CreatedDateTime datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
CreatedBy int(10) unsigned NOT NULL DEFAULT '0',
CreatedIP varchar(20) NOT NULL DEFAULT '',
IsDeleted tinyint(3) unsigned NOT NULL DEFAULT '0',
IsApproved tinyint(3) unsigned NOT NULL DEFAULT '1',
IsActive varchar(1) NOT NULL DEFAULT 'Y',
PRIMARY KEY (MasterId)
)
CREATE TABLE IF NOT EXISTS sys_tree (
TreeId int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Tree Running Id',
TreeName varchar(150) NOT NULL DEFAULT '',
IsDeleted tinyint(3) unsigned DEFAULT NULL COMMENT '1 = deleted',
PRIMARY KEY (TreeId)
)
CREATE TABLE IF NOT EXISTS sys_solicitor (
SolicitorId int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Running id number',
SolicitorCode varchar(50) NOT NULL DEFAULT '',
SolicitorName varchar(150) NOT NULL DEFAULT '',
SolicitorType varchar(50) NOT NULL DEFAULT '',
ContactPerson varchar(50) NOT NULL DEFAULT '',
PhoneNo varchar(50) NOT NULL DEFAULT '' COMMENT 'phone number',
Email varchar(40) NOT NULL DEFAULT '' COMMENT 'email address',
CreatedDateTime datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
CreatedBy int(10) unsigned NOT NULL DEFAULT '0',
IsDeleted tinyint(3) unsigned DEFAULT NULL COMMENT '1 = deleted',
IsApproved tinyint(3) unsigned DEFAULT NULL COMMENT '0 = waiting approval',
PRIMARY KEY (SolicitorId)
)
CREATE TABLE IF NOT EXISTS sys_user (
UserId int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'User Running Id',
UserLoginId varchar(25) NOT NULL DEFAULT '' COMMENT 'User Name For Log In',
UserName varchar(150) NOT NULL DEFAULT '' COMMENT 'User Real Name',
Password varchar(50) NOT NULL DEFAULT '' COMMENT 'Password For Log in',
EmailAddress varchar(100) NOT NULL DEFAULT '',
IsDeleted tinyint(3) unsigned DEFAULT NULL COMMENT '1 = deleted',
CreatedDateTime datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Date and time this record created',
CreatedBy int(10) unsigned NOT NULL DEFAULT '0',
IsApproved tinyint(3) unsigned DEFAULT NULL COMMENT '0 = waiting approval',
IsActive varchar(1) NOT NULL DEFAULT 'Y',
PRIMARY KEY (UserId)
)
CREATE TABLE IF NOT EXISTS sys_user_access_right (
UserId int(10) unsigned NOT NULL AUTO_INCREMENT,
TreeId int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (UserId,TreeId),
KEY FK_SysUserAccessRight_SysTree (TreeId)
)
CREATE TABLE IF NOT EXISTS mktg_spa (
SpaId int(10) unsigned NOT NULL AUTO_INCREMENT,
AccountId int(10) unsigned NOT NULL DEFAULT '0',
SPADate datetime DEFAULT NULL,
AgreementNo varchar(50) DEFAULT NULL,
AgreementStatus varchar(10) DEFAULT NULL,
AgreementDate datetime DEFAULT NULL,
SPAReference varchar(100) DEFAULT NULL,
SolicitorId int(10) unsigned DEFAULT NULL,
SolicitorRefNo varchar(50) DEFAULT NULL,
TitleDetailReason varchar(200) DEFAULT NULL,
Remark text,
CreatedDateTime datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
CreatedBy int(10) unsigned NOT NULL DEFAULT '0',
IsDeleted tinyint(3) unsigned NOT NULL DEFAULT '0',
IsApproved tinyint(3) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (SpaId)
)
I run EXPLAIN and it show the table :Query Analysis
You should use join instead of subqueries. Written with join, your query would look like
SELECT mktg_account.*,
tbCustomerCategory.MasterDescription as CustomerCategory,
(CASE WHEN mktg_account.BusinessSourceId = 97
THEN mktg_account.BusinessSourceOthers
ELSE tbBusinessSource.MasterDescription
END) AS BusinessSource,
sys_tree.TreeName as PhaseName,
sys_solicitor.SolicitorName,
tbAgentCompanyText.MasterCode as AgentCompanyText,
tbFinanceSource.MasterCode as FinanceSource,
sys_user.UserName as CreatedByName
from mktg_account
left join sys_master_code as tbCustomerCategory on tbCustomerCategory.MasterId = mktg_account.CategoryId
left join sys_master_code as tbBusinessSource on tbBusinessSource.MasterId = mktg_account.BusinessSourceId
left join sys_tree on sys_tree.TreeId = mktg_account.TreeId
left join mktg_spa on mktg_spa.AccountId = mktg_account.AccountId
left join sys_solicitor on sys_solicitor.SolicitorId = mktg_spa.SolicitorId
left join sys_master_code as tbAgentCompanyText on tbAgentCompanyText.MasterId = mktg_account.AgentCompany
left join sys_master_code as tbFinanceSource on tbFinanceSource.MasterId = mktg_account.FinancialTypeId
left join sys_user on sys_user.UserId = mktg_account.CreatedBy
join sys_user_access_right on sys_user_access_right.TreeId = mktg_account.TreeId
WHERE sys_user_access_right.UserId = 4
AND mktg_account.IsApproved = '1' AND mktg_account.IsDeleted <> '1'
ORDER BY mktg_account.AccountNo;
And you are missing an index on mktg_spa, so add it:
create index idx_mktg_spa_AccountId on mktg_spa (AccountId);
(You might want to try this first, it might already improve your query enough without converting it to join).
There might be some potential for an index on mktg_account, but it really depends on your data. If most of your rows are "deleted" or "not approved", you can try e.g.
create index idx_mktg_account_IsApprovedAccountNoIsDeleted
on mktg_account (IsApproved, AccountNo, IsDeleted);
But if your query returns more than just a small percentage of your rows, this index might actually slow down your query, so test without it first.
I'm not going to rewrite the whole thing for you, but here's a start. Appropriately indexed, this solution will be orders of magnitude faster than what you currently have...
SELECT a.*
, c.MasterDescription CustomerCategory
FROM mktg_account a
JOIN sys_master_code c
ON c.MasterId = a.CategoryId
JOIN sys_user_access_right r
ON r.TreeId = a.TreeId
WHERE r.UserId = 4
AND a.IsApproved = 1
AND IsDeleted <> 1
ORDER
BY a.AccountNo

Advanced query running slowly

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

MySQL - Optimise Query

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 ;

MySQL join empty rows

I'm trying to construct a simple query in for getting users with particular meta fields (this is in Wordpress, but doesn't matter cause it's raw sql)
A simple query i did looks like this
SELECT * FROM wp_sb_users u
LEFT OUTER JOIN wp_sb_usermeta m ON (u.ID=m.user_id)
LEFT OUTER JOIN wp_sb_usermeta mm ON (u.ID=mm.user_id)
LEFT OUTER JOIN wp_sb_usermeta mmm ON (u.ID=mmm.user_id)
WHERE
m.meta_key = "autostatus" AND
mm.meta_key = "first_name" AND
mmm.meta_key = "last_name"
though i have only one small trouble -- if mmm.meta_key = "last_name" doesn't exist at all, the row isn't returned.. i tried mmm.meta_key <=> "last_name" but then it takes any other meta_key (like "user_email") and puts there in row, which then results in having a user with first name Alex and last name my#email.com
I also tried (mmm.meta_key = "last_name" OR mmm.meta_key IS NULL), but it doesn't work too
Help me figure it out please
PS
the tables structure:
CREATE TABLE IF NOT EXISTS `wp_sb_users` (
`ID` bigint(20) unsigned NOT NULL auto_increment,
`user_login` varchar(60) NOT NULL default '',
`user_pass` varchar(64) NOT NULL default '',
`user_nicename` varchar(50) NOT NULL default '',
`user_email` varchar(100) NOT NULL default '',
`user_url` varchar(100) NOT NULL default '',
`user_registered` datetime NOT NULL default '0000-00-00 00:00:00',
`user_activation_key` varchar(60) NOT NULL default '',
`user_status` int(11) NOT NULL default '0',
`display_name` varchar(250) NOT NULL default '',
PRIMARY KEY (`ID`),
KEY `user_login_key` (`user_login`),
KEY `user_nicename` (`user_nicename`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=30 ;
CREATE TABLE IF NOT EXISTS `wp_sb_usermeta` (
`umeta_id` bigint(20) unsigned NOT NULL auto_increment,
`user_id` bigint(20) unsigned NOT NULL default '0',
`meta_key` varchar(255) default NULL,
`meta_value` longtext,
PRIMARY KEY (`umeta_id`),
KEY `user_id` (`user_id`),
KEY `meta_key` (`meta_key`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=792 ;
SELECT * FROM wp_sb_users u
LEFT OUTER JOIN wp_sb_usermeta m ON (u.ID=m.user_id and m.meta_key = "autostatus")
LEFT OUTER JOIN wp_sb_usermeta mm ON (u.ID=mm.user_id and mm.meta_key = "first_name")
LEFT OUTER JOIN wp_sb_usermeta mmm ON (u.ID=mmm.user_id and mmm.meta_key = "last_name")