2016-10-17 100 views
0

我有如下主要名为PROG表:如何提高我的MySQL查询的执行性能

CREATE TABLE `prog` (
`prog_id` int(11) NOT NULL AUTO_INCREMENT, 
`prog_insert_date` varchar(16) COLLATE utf8_persian_ci NOT NULL, 
`prog_edit_date` varchar(16) COLLATE utf8_persian_ci DEFAULT NULL, 
`prog_name` text COLLATE utf8_persian_ci NOT NULL, 
`prog_desc` text COLLATE utf8_persian_ci NOT NULL, 
PRIMARY KEY (`prog_id`), 
KEY `prog_insert_date` (`prog_sabt_date`), 
KEY `prog_edit_date` (`prog_edit_date`) 
) ENGINE=InnoDB AUTO_INCREMENT=1; 

INSERT INTO prog VALUES 
(1,'1395-01-01 11:00','1395-01-01 12:00','prog A', 'prog A description'), 
(2,'1395-01-02 11:00','1395-01-02 12:00','prog B', 'prog B description'), 
(3,'1395-01-03 11:00','1395-01-03 12:00','prog C', 'prog C description'); 

,因为我需要使用贾拉利日历在我的应用程序,我决定使用日期列VARCHAR(16)将它们保存为这种格式:'1395-07-20 12:43'

我也有三个相似的表,其中包含几行对应每个prog_id。他们是信贷,资金,如下支付:

CREATE TABLE `credit` (
`credit_id` int(11) NOT NULL AUTO_INCREMENT, 
`credit_insert_date` varchar(16) COLLATE utf8_persian_ci NOT NULL, 
`credit_edit_date` varchar(16) COLLATE utf8_persian_ci NOT NULL, 
`credit_prog` int(11) NOT NULL, 
`credit_amount` bigint(20) NOT NULL, 
`credit_desc` text COLLATE utf8_persian_ci NOT NULL, 
PRIMARY KEY (`credit_id`), 
KEY `credit_prog` (`credit_prog`), 
KEY `credit_insert_date` (`credit_insert_date`), 
KEY `credit_edit_date` (`credit_edit_date`) 
) ENGINE=InnoDB AUTO_INCREMENT=1; 

INSERT INTO credit VALUES 
(1,'1395-02-01 11:00','1395-02-01 12:00',1, 100000, 'sample description'), 
(2,'1395-02-02 11:00','1395-02-02 12:00',1, 200000, 'sample description'), 
(3,'1395-02-03 11:00','1395-02-03 12:00',2, 300000, 'sample description'), 
(4,'1395-02-04 11:00','1395-02-04 12:00',2, 400000, 'sample description'), 
(5,'1395-02-05 11:00','1395-02-05 12:00',3, 500000, 'sample description'), 
(6,'1395-02-06 11:00','1395-02-06 12:00',3, 600000, 'sample description'); 

CREATE TABLE `fund` (
`fund_id` int(11) NOT NULL AUTO_INCREMENT, 
`fund_insert_date` varchar(16) COLLATE utf8_persian_ci NOT NULL, 
`fund_edit_date` varchar(16) COLLATE utf8_persian_ci NOT NULL, 
`fund_prog` int(11) NOT NULL, 
`fund_amount` bigint(20) NOT NULL, 
`fund_desc` text COLLATE utf8_persian_ci NOT NULL, 
PRIMARY KEY (`fund_id`), 
KEY `fund_prog` (`fund_prog`), 
KEY `fund_insert_date` (`fund_insert_date`), 
KEY `fund_edit_date` (`fund_edit_date`) 
) ENGINE=InnoDB AUTO_INCREMENT=1; 

INSERT INTO fund VALUES 
(1,'1395-03-01 11:00','1395-03-01 12:00',1, 10000, 'sample description'), 
(2,'1395-03-02 11:00','1395-03-02 12:00',1, 20000, 'sample description'), 
(3,'1395-03-03 11:00','1395-03-03 12:00',2, 30000, 'sample description'), 
(4,'1395-03-04 11:00','1395-03-04 12:00',2, 40000, 'sample description'), 
(5,'1395-03-05 11:00','1395-03-05 12:00',3, 50000, 'sample description'), 
(6,'1395-03-06 11:00','1395-03-06 12:00',3, 60000, 'sample description'); 

CREATE TABLE `pay` (
`pay_id` int(11) NOT NULL AUTO_INCREMENT, 
`pay_insert_date` varchar(16) COLLATE utf8_persian_ci NOT NULL, 
`pay_edit_date` varchar(16) COLLATE utf8_persian_ci NOT NULL, 
`pay_prog` int(11) NOT NULL, 
`pay_amount` bigint(20) NOT NULL, 
`pay_desc` text COLLATE utf8_persian_ci NOT NULL, 
PRIMARY KEY (`pay_id`), 
KEY `pay_prog` (`pay_prog`), 
KEY `pay_insert_date` (`pay_insert_date`), 
KEY `pay_edit_date` (`pay_edit_date`) 
) ENGINE=InnoDB AUTO_INCREMENT=1; 

INSERT INTO pay VALUES 
(1,'1395-04-01 11:00','1395-04-01 12:00',1, 1000, 'sample description'), 
(2,'1395-04-02 11:00','1395-04-02 12:00',1, 2000, 'sample description'), 
(3,'1395-04-03 11:00','1395-04-03 12:00',2, 3000, 'sample description'), 
(4,'1395-04-04 11:00','1395-04-04 12:00',2, 4000, 'sample description'), 
(5,'1395-04-05 11:00','1395-04-05 12:00',3, 5000, 'sample description'), 
(6,'1395-04-06 11:00','1395-04-06 12:00',3, 6000, 'sample description'); 

现在我想有相应的信贷,资金的总和每个PROG行和支付,也应该从所有表构造最后修改日期。我的查询是:

SELECT 
    prog_id, 
    GREATEST(IFNULL(credit_edit_date,''),IFNULL(fund_edit_date,''),IFNULL(pay_edit_date,'')) last_edit_date, 
    prog_name, 
    credit_amount, 
    fund_amount, 
    pay_amount, 
    prog_desc 
FROM prog 
LEFT JOIN (
    SELECT 
     credit_prog, 
     sum(credit_amount) as credit_amount, 
     max(credit_edit_date) as credit_edit_date 
    FROM credit GROUP BY credit_prog 
) as credit ON credit_prog=prog_id 
LEFT JOIN (
    SELECT 
     fund_prog, 
     sum(fund_amount) as fund_amount, 
     max(fund_edit_date) as fund_edit_date 
    FROM fund GROUP BY fund_prog 
) as fund ON fund_prog=prog_id 
LEFT JOIN (
    SELECT 
     pay_prog, 
     sum(pay_amount) as pay_amount, 
     max(pay_edit_date) as pay_edit_date 
    FROM pay GROUP BY pay_prog 
) as pay ON pay_prog=prog_id 
ORDER BY (SELECT last_edit_date) DESC 

我得到真正的结果,但在表几行实际情况下,时间过长回应。我认为主要问题是last_edit_date列,因为MYSQL优化器不能考虑子查询中这些日期字段的索引。

sqlfiddle

任何想法,以改善执行时间?

+0

如果是我,我想我会存储ansi日期,并在其他地方处理转换。 – Strawberry

+0

@Strawberry您确定更改日期列类型可以显着改善时间吗?我认为主要问题是子查询中的索引 –

+0

Dup http://dba.stackexchange.com/questions/152456/how-to-improve-my-mysql-query-execution-performance –

回答

0

如果你的subquerys使用的不是主键索引(他们可能会这样做),这实际上会减慢你的查询速度,因为它会让MySQL跳回来并强制你的表而不是将整个表读入内存一次,然后在那里休息。

尝试添加以下指标:

alter table credit add index 
    idx_credit_p_a_ed(credit_prog, credit_amount, credit_edit_date); 
alter table pay add index 
    idx_pay_p_a_ed(pay_prog, pay_amount, pay_edit_date); 
alter table fund add index 
    idx_fund_p_a_ed(fund_prog, fund_amount, fund_edit_date); 

如果不使用MySQL 5.7(或者,如果不采取这些索引某些其他原因),则可能必须强制使用他们,所以添加例如

... FROM credit force index (idx_credit_p_a_ed) GROUP BY credit_prog ... 

到子查询。

如果您将日期列更改为每字节1个字节的数据类型,您可以获得一些额外的速度,例如,因为您只是在那里存储日期,所以请使用latin1_swedish_ci而不是utf8_persian_ci。这会减少你的索引和表格大小,从而降低速度(可能很大)。

另外,将ORDER BY (SELECT last_edit_date) DESC更改为ORDER BY last_edit_date DESC

+0

谢谢,我创建了索引并使用力量指数,但似乎没有任何显着变化 –

+0

@MohammadHasanBakhtiarifar你可以发布'解释'为您的查询(只需添加'解释'直接在'select'的前面)? – Solarflare

+0

但你是否也从'ORDER BY'中删除'SELECT'?它防止使用任何索引。 –