MariaDB/PostreSQL - 查询优化
我需要生成一些数字,我设计了一个查询来获得我的“客户”所需的结果。 此查询基于一个包含一百万条记录的表。 我通常使用MariaDB,我得到了〜7s的结果。 这个执行时间非常合适,但我期待再次优化以提高我的技能。 经过一番研究,我发现有几篇文章说“MySQL很好,但不是在表> 1M的记录上,你必须打开其他的东西”PostgreSQL已被多次引用。 所以我安装了PostgreSQL,并复制了我的表格,索引和数据。 我执行了相同的查询,并且结果在〜12sMariaDB/PostreSQL - 查询优化
我知道PostgreSQL更少,我想我没有使用该语言固有的特性。 因此,现在我留在MariaDB。你有想法来提高执行时间吗?
这里我的查询:
select categorie.cat
,dhu_type.type
,COUNT(DISTINCT(
CASE WHEN dhu.date between '2013-01-01' and '2013-12-31'
THEN dhu.id
END)
) AS "2013"
,COUNT(DISTINCT(
CASE WHEN dhu.date between '2014-01-01' and '2014-12-31'
THEN dhu.id
END)
) AS "2014"
,COUNT(DISTINCT(
CASE WHEN dhu.date between '2015-01-01' and '2015-12-31'
THEN dhu.id
END)
) AS "2015"
,COUNT(DISTINCT(
CASE WHEN dhu.date between '2016-01-01' and '2016-12-31'
THEN dhu.id
END)
) AS "2016"
from dhu
inner join dhu_type on dhu.type_id = dhu_type.id
inner join patient on dhu.patient_id=patient.id
inner join fa on patient.id = fa.patient_id
inner join categorie on categorie.id = fa.cat_id
group by cat,dhu_type.type
我完成了我的问题,以图
这里的CREATE TABLE:
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40014 SET @[email protected]@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @[email protected]@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
CREATE TABLE IF NOT EXISTS `categorie` (
`id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
`cat` varchar(50) NOT NULL DEFAULT 'neonat',
PRIMARY KEY (`id`,`cat`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `cp` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`cp` varchar(5) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `cp` (`cp`)
) ENGINE=InnoDB AUTO_INCREMENT=4096 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `dhu` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`patient_id` int(10) unsigned NOT NULL,
`date` date NOT NULL,
`type_id` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `FK_dhu_patient` (`patient_id`),
KEY `FK_dhu_dhu_type` (`type_id`),
CONSTRAINT `FK_dhu_dhu_type` FOREIGN KEY (`type_id`) REFERENCES `dhu_type` (`id`),
CONSTRAINT `FK_dhu_patient` FOREIGN KEY (`patient_id`) REFERENCES `patient` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=953590 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `dhu_import` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`noip` bigint(10) unsigned zerofill NOT NULL,
`date` date NOT NULL,
`cp` varchar(5) NOT NULL,
`type` varchar(4) NOT NULL,
PRIMARY KEY (`id`),
KEY `noip` (`noip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `dhu_type` (
`id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
`type` varchar(4) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `type` (`type`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `dpt` (
`dpt` tinyint(3) unsigned DEFAULT NULL,
`abrev` char(3) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `fa` (
`patient_id` int(10) unsigned NOT NULL,
`cat_id` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`patient_id`,`cat_id`),
KEY `idx_cat_id_pat_id` (`cat_id`,`patient_id`),
CONSTRAINT `FK_fa_patient` FOREIGN KEY (`patient_id`) REFERENCES `patient` (`id`),
CONSTRAINT `FK_fa_categorie` FOREIGN KEY (`cat_id`) REFERENCES `categorie` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `fa_import` (
`noip` bigint(10) unsigned zerofill NOT NULL,
`cat` varchar(50) NOT NULL,
PRIMARY KEY (`noip`,`cat`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
CREATE TABLE IF NOT EXISTS `patient` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`noip` bigint(10) unsigned zerofill NOT NULL,
`cp_id` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `FK_patient_cp` (`cp_id`),
CONSTRAINT `FK_patient_cp` FOREIGN KEY (`cp_id`) REFERENCES `cp` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=262141 DEFAULT CHARSET=utf8;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET [email protected]_CHARACTER_SET_CLIENT */;
这里的修改提高性能(选择categorie.id而不是categorie.cat):
这里最好的最好的查询,我发现由于@RickJames & @BillKarwin
select categorie.cat
,dhu_type.`type`
,t.`2013`
,t.`2014`
,t.`2015`
,t.`2016`
from (select fa.cat_id as catid
,dhu.type_id typid
,COUNT(DISTINCT(
CASE WHEN dhu.date between '2013-01-01' and '2013-12-31'
THEN dhu.id
END)
) AS "2013"
,COUNT(DISTINCT(
CASE WHEN dhu.date between '2014-01-01' and '2014-12-31'
THEN dhu.id
END)
) AS "2014"
,COUNT(DISTINCT(
CASE WHEN dhu.date between '2015-01-01' and '2015-12-31'
THEN dhu.id
END)
) AS "2015"
,COUNT(DISTINCT(
CASE WHEN dhu.date between '2016-01-01' and '2016-12-31'
THEN dhu.id
END)
) AS "2016"
from dhu
inner join patient on dhu.patient_id=patient.id
inner join fa on patient.id = fa.patient_id
group by fa.cat_id, dhu.type_id) t
inner join categorie on t.catid = categorie.id
inner join dhu_type on t.typid = dhu_type.id
order by categorie.cat,dhu_type.`type`
对于亿行表,MySQL确实很好。
任何数据库引擎是在缓存的磁盘速度和多少(或少)RAM的摆布。
教科书说要规范化所有内容,但我建议4字符
type
不值得正常化。同上5字符cp
。除非您确实需要全零输出行,否则在
GROUP BY
之前添加WHERE dhu.date between '2016-01-01' and '2016-12-31'
。按照我的建议here许多:很多架构设计(
fa
)。这可能加快MySQL的查询。 (我不知道是否同样的原则适用于Postgres。)
我编辑了我的帖子 –
谢谢。我删除了你处理的物品,并增加了几个。 –
我添加了“reverse”索引(cat_id,patient_id),但没有影响。我从来没有想过使用这个,感谢您的建议;)我不能使用'WHERE YEAR(dhu.date)= 2016'因为我需要指望范围[2013-01-01; 2016-12-31]我认为执行时间只来自桌面上运行的虚拟机(Centos/1Go RAM)。悲伤...... –
做内部联接fa,病人,分类代替。 – jarlh
你说得对,查询更具可读性,但执行时间相同。 –
有条件的聚合/ FILTER #GIYF – wildplasser