这个select语句运行得非常慢。完成执行需要10秒以上。可能会更长,但我无法知道,因为与MySQL的连接超时。这是一个单独的问题。
以下是代码:
SELECT
f.id, f.name, GROUP_CONCAT(DISTINCT (c.firstname)) children
FROM
families f,
children c,
transactions t
WHERE
f.companyid = 1170 AND f.id = t.familyid
AND f.id = c.familyid
AND t.transactiontype = 'P'
AND t.taxdeductible = 'Y'
AND YEAR(t.date) = 2017
AND status = 'A'
OR f.id = 9779432
GROUP BY f.id
ORDER BY name;
我确实有来自families.companyid,children.familyid,transactions.transactiontype,transactions.taxdeductible和transactions.date的索引。
有没有理由为什么它会进行全表扫描,尽管我的索引?或者是否有其他原因导致此查询运行缓慢?
编辑:根据以下评论填写一些空白:
儿童表
CREATE TABLE `children` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`familyid` int(10) unsigned DEFAULT '0',
`companyid` int(11) DEFAULT '0',
`picture` varchar(250) DEFAULT NULL,
`stockpicture` varchar(1) DEFAULT 'N',
`firstname` varchar(250) DEFAULT NULL,
`lastname` varchar(250) DEFAULT NULL,
`nickname` varbinary(250) DEFAULT NULL,
`birthdate` date NOT NULL DEFAULT '0000-00-00',
`usecustomfee` varchar(1) NOT NULL DEFAULT 'N',
`usecustomproviderfee` varchar(1) NOT NULL DEFAULT 'N',
`customfee` decimal(10,2) DEFAULT '0.00',
`customfeetypecode` varchar(45) DEFAULT 'MONTH',
`customproviderfee` decimal(10,2) DEFAULT '0.00',
`customproviderfeetypecode` varchar(45) DEFAULT 'MONTH',
`usecustomchargeitem` varchar(1) DEFAULT 'N',
`customchargeitem` int(11) DEFAULT '0',
`dailyrate` decimal(10,2) DEFAULT '55.00',
`startdate` date DEFAULT NULL,
`enddate` date DEFAULT NULL,
`subsidynotrequired` char(1) NOT NULL DEFAULT 'Y',
`subsidychildid` varchar(250) DEFAULT NULL,
`subsidyapplicantid` varchar(250) DEFAULT NULL,
`subsidynote` text,
`waitingsince` date DEFAULT NULL,
`waitingroom` int(11) DEFAULT NULL,
`waitingtype` varchar(1) DEFAULT 'F',
`preferredstart` date DEFAULT NULL,
`registrationdate` date DEFAULT NULL,
`groupid` int(11) NOT NULL DEFAULT '0',
`providerisparent` varchar(1) NOT NULL DEFAULT 'N',
`attendingschool` char(1) NOT NULL DEFAULT 'N',
`schoolname` varchar(250) DEFAULT NULL,
`liveswithmother` char(1) NOT NULL DEFAULT 'Y',
`liveswithfather` char(1) NOT NULL DEFAULT 'Y',
`liveswithother` char(1) NOT NULL DEFAULT 'N',
`otherguardian` varchar(250) DEFAULT NULL,
`sex` char(1) NOT NULL DEFAULT 'M',
`note` text,
`archived` char(1) NOT NULL DEFAULT 'N',
`priorityid` int(11) DEFAULT '0',
`onlineregistration` varchar(1) NOT NULL DEFAULT 'N',
`onlineregistrationaccept` varchar(1) NOT NULL DEFAULT 'N',
`registrationconfirmed` varchar(1) NOT NULL DEFAULT 'N',
`registrationconfirmeddate` datetime DEFAULT NULL,
`createddate` datetime DEFAULT NULL,
`modifieddate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`fullpart` varchar(1) DEFAULT 'F',
`parttimedays` int(11) DEFAULT '10',
`parttimedaystype` varchar(45) DEFAULT 'D',
`parttimedaystypecode` varchar(45) DEFAULT 'MONTH',
`program` varchar(45) DEFAULT 'daycare',
`registrationnote` varchar(2000) DEFAULT NULL,
`registrationnoteread` varchar(1) DEFAULT 'N',
`registrationsubsidy` varchar(45) DEFAULT 'noplan',
`registrationsubsidydate` datetime DEFAULT NULL,
`registrationsubsidyamount` decimal(10,2) DEFAULT '0.00',
PRIMARY KEY (`id`),
KEY `Familyid` (`familyid`),
KEY `companyid` (`companyid`),
KEY `startdate` (`startdate`),
KEY `enddate` (`enddate`),
KEY `roomid` (`groupid`),
KEY `providerisparent` (`providerisparent`)
) ENGINE=InnoDB AUTO_INCREMENT=93685 DEFAULT CHARSET=latin1;
家庭表
CREATE TABLE `families` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`accountnumber` varchar(100) DEFAULT NULL,
`name` varchar(245) NOT NULL COMMENT 'The account name will typically be the name of the parent responsible for payment',
`motherid` int(10) unsigned NOT NULL,
`fatherid` int(10) unsigned NOT NULL,
`balance` decimal(10,2) NOT NULL DEFAULT '0.00',
`notes` varchar(2000) DEFAULT NULL,
`companyid` int(10) unsigned NOT NULL,
`status` varchar(1) NOT NULL DEFAULT 'A',
`financialaidrequired` char(1) NOT NULL DEFAULT 'N',
`intakesurveyid` int(10) unsigned DEFAULT NULL,
`referralid` int(10) unsigned NOT NULL DEFAULT '0',
`registrationemailrequired` varchar(1) DEFAULT 'N',
`registrationemailsent` varchar(1) DEFAULT 'N',
`registrationemaildate` date DEFAULT NULL,
`registrationemailaddressfound` varchar(1) DEFAULT NULL,
`waitinglistemailrequired` varchar(1) DEFAULT 'N',
`waitinglistemailsent` varchar(1) DEFAULT 'N',
`waitinglistemaildate` date DEFAULT NULL,
`waitinglistemailaddressfound` varchar(1) DEFAULT NULL,
`activationemailrequired` varchar(1) DEFAULT 'N',
`activationemailsent` varchar(1) DEFAULT 'N',
`activationemaildate` date DEFAULT NULL,
`activationemailaddressfound` varchar(1) DEFAULT NULL,
`createddate` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `companyid` (`companyid`),
KEY `intakesurveyid` (`intakesurveyid`),
KEY `status` (`status`)
) ENGINE=InnoDB AUTO_INCREMENT=9803007 DEFAULT CHARSET=latin1;
交易表
CREATE TABLE `transactions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`familyid` int(10) unsigned NOT NULL,
`date` datetime NOT NULL,
`transactiontype` varchar(1) NOT NULL DEFAULT 'C' COMMENT '''C'' = Charge, ''P'' = Payment',
`paymenttype` varchar(3) DEFAULT NULL COMMENT '''DBT'' = Debit, ''CSH'' = Cash, ''CRE'' = Credit Card, ''CHQ'' = Cheque, ''MNY'' = Money Order,''EFT'' = Electronic Funds Transfer',
`comment` varchar(500) DEFAULT NULL,
`amount` decimal(10,2) NOT NULL DEFAULT '0.00',
`reference` varchar(45) DEFAULT NULL,
`chargeitem` int(10) unsigned DEFAULT '0',
`taxdeductible` varchar(1) NOT NULL DEFAULT 'Y',
`payer` varchar(1) DEFAULT 'M',
`createddate` datetime DEFAULT NULL,
`modifieddate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `Familyid` (`familyid`),
KEY `Transaction Type` (`transactiontype`),
KEY `Tax Deductible` (`taxdeductible`),
KEY `date` (`date`)
) ENGINE=InnoDB AUTO_INCREMENT=1013472 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC;
答案 0 :(得分:0)
尝试
EXPLAIN
SELECT
f.id, f.name, GROUP_CONCAT(DISTINCT (c.firstname)) children
FROM
families f,
children c,
transactions t
WHERE
f.companyid = 1170 AND f.id = t.familyid
AND f.id = c.familyid
AND t.transactiontype = 'P'
AND t.taxdeductible = 'Y'
AND YEAR(t.date) = 2017
AND f.status = 'A'
OR f.id = 9779432
GROUP BY f.id
ORDER BY name;
确保加载了正确的索引
你说你“有索引”,但每个查询只能使用1个索引,为你需要的查询设置1
索引。
另外我建议永远不要使用倍数from
,但是使用JOIN
语句而不是能够针对连接的表索引和索引进行目标
答案 1 :(得分:0)
请提供您的表架构。我们需要检查你有哪些索引。
同时,您可以尝试JOIN
个表并删除ORDER BY
。
从我看到你只有一个f.id = 9779432
,你为什么要订购相同的价值?
检查OR
条件,我已将其转化为对我有意义的事情。您对该广泛OR的初始陈述意味着您需要任何YEAR(t.date) OR f.id = 9779432
这对您有意义吗?
SELECT
f.id, f.name, GROUP_CONCAT(DISTINCT (c.firstname)) children
FROM
families f
INNER JOIN children c
ON f.id = c.familyid
INNER JOIN transactions t
ON f.id = t.familyid
AND t.transactiontype = 'P'
AND t.taxdeductible = 'Y'
AND YEAR(t.date) = 2017
WHERE
(f.companyid = 1170 OR f.id = 9779432)
AND f.status = 'A'
GROUP BY f.id;
答案 2 :(得分:0)
最好使用21世纪的JOIN语法。
SELECT f.id, f.name, GROUP_CONCAT(DISTINCT (c.firstname)) children
FROM families f
JOIN children c ON f.id = c.familyid
JOIN transactions t ON f.id = t.familyid
WHERE f.companyid = 1170
AND t.transactiontype = 'P'
AND t.taxdeductible = 'Y'
AND YEAR(t.date) = 2017
AND status = 'A'
OR f.id = 9779432
GROUP BY f.id
ORDER BY name;
将AND YEAR(t.date) = 2017
更改为AND t.date >='2017-01-01 AND t.date < '2018-01-01'
。为什么?该过滤条款的YEAR()
形式不是sargeable。
您的问题无法确定哪个表包含status
列,并且对于性能而言非常重要 。如果是t.status
,请尝试在
transaction(status, transactiontype, taxdeductible, date, familyid)
然后在
上尝试复合索引 transaction(familyid, status, transactiontype, taxdeductible, date)
其中一个应该有很多帮助。为什么?当在transaction
表上满足您的查询时,MySQL可以随机访问索引到第一个符合条件的记录:匹配所有=
条件并具有最低值date
的记录。然后它可以按顺序扫描索引,直到找到最后一个符合条件的日期。
使用效果最佳的索引。
如果status
表中的transaction
列不在该表中,请将其从该索引中取出。
答案 3 :(得分:0)
假设你的意思是这(MySQL将如何解释它):
(this AND that ...) OR (f.id=...)
我们使用UNION
代替OR
。 (OR
优化得很差。)
我们也使用'标准'JOIN...ON
代替'commajoin'。
我们不要隐藏函数中的列(YEAR
);它禁止使用索引。
您已经因为没有说哪个表包含status
而受到谴责。我看到Hamoon意外地丢失了status
在f
中的事实(?)。我会假设。
DISTINCT
不是函数,所以我删除了它后面的parens。
我会选择UNION DISTINCT
(较慢,但匹配OR
的语义)而不是UNION ALL
(更快,但可能会重复一行)。
我会将children
移到外部SELECT
以避免一些潜在的打嗝。
当GROUP BY
和ORDER BY
匹配时,查询可以更快地运行。因此,假设id
和name
在逻辑上捆绑在一起,我认为这将为您提供相同的分组和排序:
GROUP BY name, id
ORDER BY name, id
将我的所有提示放在一起:
SELECT x.id, x.name,
GROUP_CONCAT(DISTINCT c.firstname) children
FROM (
( SELECT f.id, f.name,
FROM families f
JOIN transactions t ON f.id = t.familyid
WHERE f.companyid = 1170
AND t.transactiontype = 'P'
AND t.taxdeductible = 'Y'
AND t.date >= '2017-01-01'
AND t.date < '2017-01-01' + INTERVAL 1 YEAR
AND f.status = 'A'
)
UNION DISTINCT
( SELECT f.id, f.name
FROM families f
WHERE f.id = 9779432
)
) AS x
JOIN children c ON x.id = c.familyid
GROUP BY x.name, x.id
ORDER BY x.name, x.id
您将需要这些索引。列排序通常很重要。
f: I assume it has PRIMARY KEY(id)
f: (companyid, status) -- in either order
t: (familyid, transactiontype, taxdeductible, date)
t: (transactiontype, taxdeductible, date, familyid)
c: (familyid, firstname)
一些注意事项:
t
提供了2个索引 - 同时提供这两个索引,从而让优化程序决定是从f
还是t
开始。DISTINCT
中的GROUP_CONCAT
可能是不必要的。