2017-08-30 54 views
0

我需要生成一些數字,我設計了一個查詢來獲得我的「客戶」所需的結果。 此查詢基於一個包含一百萬條記錄的表。 我通常使用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 

我完成了我的問題,以圖

enter image description here

這裏的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 */; 

這裏解釋查詢: enter image description here

這裏的修改提高性能(選擇categorie.id而不是categorie.cat):

enter image description here

這裏最好的最好的查詢,我發現由於@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` 
+1

做內部聯接fa,病人,分類代替。 – jarlh

+0

你說得對,查詢更具可讀性,但執行時間相同。 –

+0

有條件的聚合/ FILTER #GIYF – wildplasser

回答

1
  • 對於億行表,MySQL確實很好。

  • 任何數據庫引擎是在緩存的磁盤速度和多少(或少)RAM的擺佈。

  • 教科書說要規範化所有內容,但我建議4字符type不值得正常化。同上5字符cp

  • 除非您確實需要全零輸出行,否則在GROUP BY之前添加WHERE dhu.date between '2016-01-01' and '2016-12-31'

  • 按照我的建議here許多:很多架構設計(fa)。這可能加快MySQL的查詢。 (我不知道是否同樣的原則適用於Postgres。)

+0

我編輯了我的帖子 –

+0

謝謝。我刪除了你處理的物品,並增加了幾個。 –

+0

我添加了「reverse」索引(cat_id,patient_id),但沒有影響。我從來沒有想過使用這個,感謝您的建議;)我不能使用'WHERE YEAR(dhu.date)= 2016'因爲我需要指望範圍[2013-01-01; 2016-12-31]我認爲執行時間只來自桌面上運行的虛擬機(Centos/1Go RAM)。悲傷...... –