IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Requêtes MySQL Discussion :

Indexation MySQL pour optimisation SUM [MySQL-5.5]


Sujet :

Requêtes MySQL

  1. #1
    Membre habitué

    Profil pro
    Inscrit en
    Février 2005
    Messages
    317
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 317
    Points : 183
    Points
    183
    Par défaut Indexation MySQL pour optimisation SUM
    salut à tous,
    J'ai une grosse table (environ 1 million de lignes), organisée grosso modo de la sorte:

    Pays, Région, Type d'équipement, Modèle, Nombre 2008, Nombre 2009, Nombre 2010, ...
    Ceci est un example.
    Les colonnes nombre sont de type Decimal, les autres varchar.
    L'idée est de pouvoir générer des rapports, sur n colonnes, dans n'importe quel ordre, par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Select Pays, Modèle, Sum(Nombre 2008), Sum(Nombre 2009) GROUP By Pays, Modèle
    Ou encore:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Select Modèle, Region Sum(Nombre 2008), Sum(Nombre 2009) GROUP By Modèle, Région
    Toutes les combinaisons doivent être possibles. J'aurai voulu savoir quels seraient les index a créer pour avoir de meilleurs performances ?

    Un seul index n'est pas possible car j'ai environ une trentaine de colonnes dans ma db.
    Aussi je pensais à faire des index 1:1 entre toutes les colonnes et dans les deux sens, mais ça fait beaucoup peut être (du genre index pays,modèle + index modèle, pays + index pays, nombre 2008 + index nombre 2008, pays + ...)

    Si vous avez des tuyaux ça serait sympa

    * Merci *

  2. #2
    Modérateur
    Avatar de Chtulus
    Homme Profil pro
    Ingénieur
    Inscrit en
    Avril 2008
    Messages
    3 094
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2008
    Messages : 3 094
    Points : 8 678
    Points
    8 678
    Par défaut
    Bonsoir,

    Même pour un exemple c'est une très mauvaise conception.

    Il faudrait revoir votre modèle puis passer par des Vues ce qui reviendrait à cette horreur

    « Je ne cherche pas à connaître les réponses, je cherche à comprendre les questions. »
    - Confucius -

    Les meilleurs cours, tutoriels et Docs sur les SGBD et le SQL
    Tous les cours Office
    Solutions d'Entreprise



  3. #3
    Membre habitué

    Profil pro
    Inscrit en
    Février 2005
    Messages
    317
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 317
    Points : 183
    Points
    183
    Par défaut
    En fait ma table contient des statistiques basées sur un nombre critères hiérarchisés, aussi, je ne vois pas trop comment je pourrais faire autrement.
    L'idée est de pouvoir générer des rapports un peu à la façon des tableaux croisés sous excel. Si tu as une meilleure idée je suis preneur.

  4. #4
    Modérateur
    Avatar de Chtulus
    Homme Profil pro
    Ingénieur
    Inscrit en
    Avril 2008
    Messages
    3 094
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2008
    Messages : 3 094
    Points : 8 678
    Points
    8 678
    Par défaut
    Bonjour,

    Aurais tu ton modèle ?, cela aiderait à comprendre.
    Et avec les DDL...

    Après, à quoi correspondent les colonnes :
    Nombre 2008, Nombre 2009, Nombre 2010, ...
    « Je ne cherche pas à connaître les réponses, je cherche à comprendre les questions. »
    - Confucius -

    Les meilleurs cours, tutoriels et Docs sur les SGBD et le SQL
    Tous les cours Office
    Solutions d'Entreprise



  5. #5
    Membre habitué

    Profil pro
    Inscrit en
    Février 2005
    Messages
    317
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 317
    Points : 183
    Points
    183
    Par défaut
    En fait c'est une table contenant des statistiques sur la surface totale et le nombre de batiments en fonction de plusieurs critères hiérarchiques, et sur les 10 dernières années.

    Je ne préfères pas fournir le DDL car c'est une appli à but commercial, mais l'idée est par exemple:
    Pays | Type de bâtiment | Classe Energétique | Type de chauffage | nombre XX | Surface XX
    France | Type 1 | A | Fioul | 100 000 | 1 000 000
    France | Type 1 | A | Gaz | 100 000 | 1 000 000
    France | Type 1 | A | Electrique | 100 000 | 1 000 000
    France | Type 1 | B | Fioul | 100 000 | 1 000 000
    France | Type 1 | B | Gaz | 100 000 | 1 000 000
    France | Type 1 | B | Electrique | 100 000 | 1 000 000
    France | Type 2 | A | Fioul | 100 000 | 1 000 000
    France | Type 2 | A | Gaz | 100 000 | 1 000 000
    France | Type 2 | A | Electrique | 100 000 | 1 000 000
    France | Type 2 | B | Fioul | 100 000 | 1 000 000
    France | Type 2 | B | Gaz | 100 000 | 1 000 000
    France | Type 2 | B | Electrique | 100 000 | 1 000 000

    Avec biensur plus de colonnes et de valeurs différentes par colonnes...

    Le but de mon appli est de pouvoir générer des requêtes pour obtenir une consolidation de n'importe quelle donnée de la table, par exemple, je peux vouloir le nombre de bâtiment de classe énergétique A par pays, ou bien le nombre total de bâtiment de type 2 par classe énergétique, ... Toutes les combinaisons sont possibles.

    Ce que je cherches à faire, c'est à définir les indexes de telle sorte que MySQL n'aient pas à faire la somme de plusieurs centaines de milliers de ligne à chaque requêtes. Je ne pourrais pas faire un index unique car il dépassera la limite en taille. Je vais faire quelques tests de tout de façon, mais j'aurai bien aimé avoir un peu de théorie sur la meilleure façon de procéder...
    Si je créé des indexes entre toutes les valeurs de colonnes possibles, ça me fait 30 * 29 = 870 Indexes pour 30 colonnes...

    J'espère que c'est plus clair

  6. #6
    Membre habitué

    Profil pro
    Inscrit en
    Février 2005
    Messages
    317
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 317
    Points : 183
    Points
    183
    Par défaut
    Bon au cas ou ça aides certains, j'ai trouvé une solution.

    En fait je me suis rendu compte que l'ordre des colonnes n'a pas d'importance. Par exemple:
    select pays, region, SUM(2012) Group by pays, region
    et
    select region, pays, SUM(2012) Group by region, pays
    me donnerons les même lignes.

    J'ai donc dévelopé un algorithme qui compile toutes mes tables. Cela fait donc 511 Tables pour un total de 30Go, mais mes requêtes sont instantannées maintenant

    Voilà à quoi ça ressemble. Les champs "L" = label, contiennent les intitulés. les champs "D" = Data, contiennent les données.
    J'ai une table qui contiens le type de colonne pour chacune des colonnes appelées

    fieldsdefs de la sorte (field_id (AI), tablename, fieldname)
    une table contenant la liste des table aggrégée
    et une table N:N faisant le lien entre les 2.

    Ci dessous, les 2 fonctions de créations des tables (la première fonctions génère les combinaisons, la seconde créé la table), et la 3e fonction sert à trouver la table correspondantes pour les champs donnés.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
     
    $LFields=$this->getFieldsAsArray($table,"L");
    		$DFields=$this->getFieldsAsArray($table,"D");
     
    		$len  = count($LFields);
    		$tablefields = array();
     
    		for($i = 1; $i < (1 << $len); $i++) {
    			$c = Array();
    			for($j = 0; $j < $len; $j++)
    				if($i & (1 << $j))
    				$c [] = $LFields[$j];
    				$tablefields[] = $c;
    		}
     
    		/**
    		 * Creating & populating tables
    		 */
    		$this->setConsolidationStatusWithPG("Mise à jour des données", 0);
    		$i=0;
    		$max=count($tablefields);
    		foreach($tablefields as $tf)
    		{
    			$i++;
    			$this->createAggregateTable($tf, $DFields, $table);
    			$this->logtofile("Etape $i/$max Terminée","notice","<br /> \n","<br /> \n");
    			$this->setConsolidationStatusWithPG("Mise à jour des données: $i/$max", round($i/$max*100));
    		}
     
    function createAggregateTable($LFields, $DFields, $basetable)
    	{
    		$fields_id=array_value_recursive("field_id",$LFields);
    		$fields_name=array_value_recursive("fieldname",$LFields);
     
    		$aggregate_table_name=$basetable."_".join("_",$fields_id);
    		$this->logtofile("Création de la table $aggregate_table_name pour les champs: ".join(",",$fields_name));
     
    		/*
    		 * Création de la table en premier pour ne pas corrompre les entrées si erreur
    		*/
     
    		$ddl = "CREATE TABLE IF NOT EXISTS `$aggregate_table_name` (";
    		$fields="";
    		$keys="";
    		foreach($fields_name as $fn)
    		{
    			$fields.="\n `$fn` varchar($this->defaultVarcharSize) DEFAULT NULL,";
    			$keys.="\nKEY `$fn` (`$fn`),";
    		}
    		foreach($DFields as $df)
    		{
    			$fields.="\n`".$df["fieldname"]."` decimal(30,15) DEFAULT NULL,";
    		}
     
    		$keys=substr($keys,0,strlen($keys)-1); //on enlève la dernière ',' 
    		$ddl .= $fields;
    		$ddl .= $keys;
    		$ddl .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8;";
    		$ddlhtml="<a href='#' style='color:red;' onclick='$(\"#div_$aggregate_table_name\").toggle(); return false;'>Afficher/Masquer le DDL SQL</a>
    		<div style='display:none;' id='div_$aggregate_table_name'><pre>$ddl</pre> </div>";
    		$this->logtofile("Table $aggregate_table_name créée:\n <br /> $ddlhtml \n <br />\n <br /> Création des entrées...", "success","","<br />\n");
    		$this->db->query($ddl);
     
    		/*
    		 * Insertion de l'entrée dans aggregate_tables
    		 */
    		$data=Array("aggregate_table_name" => $aggregate_table_name,
    				"basetable"=>$basetable,
    				"fieldscount"=>count($fields_id));
    		$aggregate_table_id=$this->db->query_insert("aggregate_tables", $data);
    		$this->logtofile("Insertion de l'entrée dans aggregate_tables réeussie", "success");
    		/*
    		 * Insertion des champs dans aggregate_tables_fields
    		*/
    		foreach($fields_id as $fid)
    		{
    			$data=Array("aggregate_table_id" => $aggregate_table_id,
    					"field_id"=>$fid);
    			$this->db->query_insert("aggregate_tables_fields", $data);
     
    		}
    		$this->logtofile("Insertion des entrées dans aggregate_tables_fields réeussie. Remlissage ...", "success");
     
    		/*
    		 * Remplissage de la table
    		 */
     
    		$insertLFields=$fields_name;
    		$insertLFields=array_map(function ($str) { return "`$str`"; }, $insertLFields);
    		$insertLFields=join(",",$insertLFields);
     
    		$sumfields=array_value_recursive("fieldname",$DFields);
    		$sumfields=array_map(function ($str) { return "SUM(`$str`)"; }, $sumfields);
    		$sumfields=join(",",$sumfields);
     
    		$query="INSERT INTO $aggregate_table_name SELECT $insertLFields, $sumfields FROM $basetable GROUP BY $insertLFields";
    		$this->logtofile("Execution de la requête: <br />\n$query", "notice","<br />\n<br />\n","<br />\n<br />\n");
    		$this->db->query($query);
    		$numlines=@mysql_affected_rows($this->db->link_id);
    		/*
    		 * Optimisation de la table
    		*/
    		$this->optimizeAggregateTableFieldLengths($aggregate_table_id);
     
    		$this->logtofile("Optimisation de la table (OPTIMIZE TABLE): $aggregate_table_name");
    		$this->db->query("OPTIMIZE TABLE $aggregate_table_name");
    		$this->logtofile("Table Optimisée","success");
     
    		$this->logtofile("Table $aggregate_table_name créée et consolidée ($numlines Lignes)", "success","","<br />\n");
     
    	}
     
    	function getAggregateTableNameFromId($aggregate_table_id)
    	{
    		$res=$this->db->fetch_all_array("SELECT aggregate_table_name FROM aggregate_tables WHERE aggregate_table_id=$aggregate_table_id");
    		if(empty($res))
    		{
    			$this->logtofile("La table consolidée avec l'ID $aggregate_table_id n'a pas été trouvée, opération annulée","error");
    			die("La table consolidée avec l'ID $aggregate_table_id n'a pas été trouvée, opération annulée");
    		} else {
    			return $res[0]["aggregate_table_name"];
    		}
    	}
     
    	function getAggregateTableFromFieldsIds($fieldsids)
    	{
    		$Q="SELECT aggregate_table_name, aggregate_table_id FROM aggregate_tables WHERE \n";
    		for($i=0;$i<count($fieldsids); $i++)
    		{
    			$Q.= ($i>0 ? "\n AND " : "\n" )."aggregate_table_id IN (SELECT aggregate_table_id FROM aggregate_tables_fields WHERE field_id =".$fieldsids[$i].")";
    		}
     
    		$Q.=" AND fieldscount=".count($fieldsids);
    		$db=new Database();
    		$res=$db->fetch_all_array($Q);
    		if(count($res)>1)
    		{
    			die("Plusieurs tables aggrégées ont étées trouvées pour les champs " . join(",",$fieldsids).".\n Cela ne devrait pas arriver. Merci de prévenir l'administrateur");
    		}
    		if(count($res)<1)
    		{
    			die("Aucune tables aggrégée n'a été trouvée pour les champs " . join(",",$fieldsids).".\n Cela ne devrait pas arriver. Merci de prévenir l'administrateur");
    		}
    		return $res[0]["aggregate_table_name"];
    	}

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Optimiser Mysql pour un super serveur
    Par Ekimasu dans le forum MySQL
    Réponses: 4
    Dernier message: 10/10/2008, 21h58
  2. Réponses: 2
    Dernier message: 26/09/2008, 22h32
  3. Requetes et Index pour optimisation...
    Par manuds dans le forum PostgreSQL
    Réponses: 2
    Dernier message: 17/03/2006, 09h05
  4. Optimisation MySQL pour gros volumes
    Par barns dans le forum Requêtes
    Réponses: 8
    Dernier message: 01/10/2005, 11h28
  5. Optimiser MySql pour plusieurs milliers de tables
    Par compu dans le forum Installation
    Réponses: 14
    Dernier message: 02/09/2005, 15h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo