2017-08-30 1 views
0

Ich brauche einige Zahlen zu produzieren, habe ich eine Abfrage entwickelt, um das gewünschte Ergebnis von meinen "Kunden" zu bekommen. Diese Abfrage basiert auf einer Tabelle, die eine Million Datensätze enthält. Normalerweise verwende ich MariaDB dafür, und ich bekomme ein Ergebnis in ~ 7s. Diese Ausführungszeit ist durchaus geeignet, aber ich möchte noch einmal optimieren, um meine Fähigkeiten zu verbessern. Nach ein paar Recherchen stieß ich auf ein paar Posts, die sagten "MySQL ist in Ordnung, aber nicht auf Tabellen> 1M Datensätze, müssen Sie etwas anderes einschalten" PostgreSQL wurde mehrmals zitiert. So habe ich PostgreSQL installiert und meine Tabellen, Indizes und Daten kopiert. Ich führte die gleiche Abfrage, und ich hatte ein Ergebnis in ~ 12sMariaDB/PostreSQL - Abfrage Optimierung

Ich weiß weniger PostgreSQL, ich glaube, ich habe nicht die Besonderheiten der Sprache verwendet. So für jetzt bleibe ich bei MariaDB. Haben Sie eine Idee, die Ausführungszeit zu verbessern?

Hier meine Frage:

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 

ich meine Frage mit einem Diagramm vervollständigen

enter image description here

Hier ist die CREATE TABLE:

/*!40101 SET @[email protected]@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 */; 

Hier ist die Abfrage erklären: enter image description here

Hier eine Modifikation Verbesserung der Leistung (Wahlen statt categorie.cat categorie.id):

enter image description here

Hier ist die beste beste Abfrage fand ich dank @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

Machen Sie innere Verbindung Fa, Patienten, Kategorie statt. – jarlh

+0

Sie haben Recht, die Abfrage ist lesbarer, aber die Ausführungszeit ist gleich. –

+0

Bedingte Aggregation/FILTER #GIYF – wildplasser

Antwort

1
  • MySQL passt gut zu Milliarden-Tabellen.

  • Alle Datenbank-Engine ist der Geschwindigkeit der Festplatte und wie viel (oder wenig) RAM haben Sie für die Zwischenspeicherung.

  • Die Lehrbücher sagen, alles zu normalisieren, aber ich schlage vor, dass ein 4-char type nicht normalisieren ist. Dito für die 5-Char cp.

  • Wenn Sie keine Zeilen mit allen Nullen ausgeben möchten, fügen Sie diese WHERE dhu.date between '2016-01-01' and '2016-12-31' vor der GROUP BY hinzu.

  • Folgen Sie meinem Ratschlag here auf vielen: viele Schemadesign (fa). Diese kann beschleunigen die Abfrage für MySQL. (Ich weiß nicht, ob die gleichen Prinzipien für Postgres gelten.)

+0

Ich habe meinen Beitrag bearbeitet –

+0

Danke. Ich habe die Gegenstände, mit denen du fertig warst, entfernt und einige weitere hinzugefügt. –

+0

Ich habe den "reverse" Index (cat_id, patient_id) hinzugefügt, aber es gibt keine Auswirkungen. Ich hatte nie daran gedacht dies zu benutzen, danke für deinen Rat;) Ich kann kein 'WHERE YEAR (dhu.date) = 2016' verwenden, weil ich muss auf die Reichweite zählen [2013-01-01; 2016-12-31] Ich denke, die Erstellungszeit kommt nur von meiner virtuellen Maschine (Centos/1Go RAM), die auf dem Desktop läuft. Traurigkeit .... –