source: spip-zone/_plugins_/polyhierarchie/branches/v2.0/polyhier_fonctions.php @ 94720

Last change on this file since 94720 was 94720, checked in by cedric@…, 5 years ago

Perf issue retour sur r94692 et retour des sous requetes :

  • les sous requetes dans un IN sont lentes a cause d'un bug de mysql qui les interprete en correlated queries. Consequence, la sous requete est executee pour chaque ligne de la requete principale, ce qui fait exploser le temps de requete totale, de complexite m*n

Reference :
https://dev.mysql.com/doc/refman/5.5/en/correlated-subqueries.html
http://bugs.mysql.com/bug.php?id=9090

  • On peut contourner le bug en emballant la sous-requete dans un (SELECT * FROM(...) AS subquery). Elle redevient uncorrelated, executee une seule fois, et on retrouve la perf mysql attendue, meilleure qu'en rapatriant les resultats dans PHP et en les reinjectant dans la requete principale

cf http://stackoverflow.com/questions/6135376/mysql-select-where-field-in-subquery-extremely-slow-why#6157797

File size: 7.8 KB
Line 
1<?php
2/*
3 * Plugin Polyhierarchie
4 * (c) 2009-2010 Cedric Morin
5 * Distribue sous licence GPL
6 *
7 */
8
9if (!defined("_ECRIRE_INC_VERSION")) return;
10
11/**
12 * {enfants} ou {enfants #ID_RUBRIQUE}
13 * renvoit tous les enfants d'une rubrique ou article
14 * directs (liens descendants) ou indirects (liens transverses)
15 *
16 * @global <type> $exceptions_des_tables
17 * @param <type> $idb
18 * @param <type> $boucles
19 * @param <type> $crit
20 * @param <type> $tous
21 */
22function critere_enfants($idb, &$boucles, $crit, $tous=true) {
23        global $exceptions_des_tables;
24        $boucle = &$boucles[$idb];
25
26        if (isset($crit->param[0])){
27                $arg = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
28        }
29        else
30                $arg = kwote(calculer_argument_precedent($idb, 'id_rubrique', $boucles));
31
32        if ($boucle->type_requete == 'rubriques' OR isset($exceptions_des_tables[$boucle->id_table]['id_parent'])) {
33                $id_parent = isset($exceptions_des_tables[$boucle->id_table]['id_parent']) ?
34                        $exceptions_des_tables[$boucle->id_table]['id_parent'] :
35                        'id_parent';
36                $mparent = $boucle->id_table . '.' . $id_parent;
37        }
38        else {
39                $mparent = $boucle->id_table . '.id_rubrique';
40        }
41
42        $where = array();
43
44        if ($tous!=='indirects')
45                $where[] = "is_array(\$r=$arg)?sql_in('$mparent',\$r):array('=', '$mparent', \$r)";
46
47        if ($tous!=='directs'
48          AND in_array(table_objet_sql($boucle->type_requete),array_keys(lister_tables_objets_sql()))){
49                $type = objet_type($boucle->type_requete);
50                $cond = "is_array(\$r=$arg)?sql_in('rl.id_parent',\$r):'rl.id_parent='.\$r";
51                $sous = "sql_get_select('rl.id_objet','spip_rubriques_liens as rl',$cond.' AND rl.objet=\'$type\'')";
52                $where[] = "array('IN', '".$boucle->id_table.".".$boucle->primary."', '(SELECT * FROM('.$sous.') AS subquery)')";
53        }
54        if (count($where)==2)
55                $where = array("'OR'",$where[0],$where[1]);
56        else
57                $where = reset($where);
58
59        $boucle->where[]= $where;
60}
61
62function critere_enfants_directs_dist($idb, &$boucles, $crit) {
63        critere_enfants($idb, $boucles, $crit, 'directs');
64}
65
66function critere_enfants_indirects_dist($idb, &$boucles, $crit) {
67        critere_enfants($idb, $boucles, $crit, 'indirects');
68}
69
70/**
71 * {parents}
72 * renvoit tous les parents d'une rubrique ou article
73 * {parents #ID_RUBRIQUE}
74 * renvoit tous les parents d'une rubrique
75 * directs (liens ascendants) ou indirects (liens transverses)
76 *
77 * @global <type> $exceptions_des_tables
78 * @param <type> $idb
79 * @param <type> $boucles
80 * @param <type> $crit
81 * @param <type> $tous
82 */
83function critere_parents($idb, &$boucles, $crit, $tous=true) {
84        global $exceptions_des_tables;
85        $boucle = &$boucles[$idb];
86        $boucle_parent = $boucles[$boucle->id_parent];
87
88        $primary = $boucle->id_table.".".$boucle->primary;
89
90        $where = array();
91
92        if ($tous!=='indirects'){
93                $argp = kwote(calculer_argument_precedent($idb, $boucle_parent->type_requete == 'rubriques' ? 'id_parent' : 'id_rubrique', $boucles));
94                $where[] = "is_array(\$r=$argp)?sql_in('$primary',\$r):array('=', '$primary', \$r)";
95        }
96
97        if ($tous!=='directs'
98          AND in_array(table_objet_sql($boucle_parent->type_requete),array_keys(lister_tables_objets_sql()))){
99                $arg = kwote(calculer_argument_precedent($idb, id_table_objet(objet_type($boucle_parent->type_requete)), $boucles));
100                $type = objet_type($boucle_parent->type_requete);
101                $sous = "sql_get_select('rl.id_parent','spip_rubriques_liens as rl','rl.id_objet='.$arg.' AND rl.objet=\'$type\'')";
102                $where[] = array("'IN'", "'$primary'", "'(SELECT * FROM('.$sous.') AS subquery)'");
103        }
104        if (count($where)==2)
105                $where = array("'OR'",$where[0],$where[1]);
106        else
107                $where = reset($where);
108
109        $boucle->where[]= $where;
110}
111
112function critere_parents_directs_dist($idb, &$boucles, $crit) {
113        critere_parents($idb, $boucles, $crit, 'directs');
114}
115function critere_parent($idb, &$boucles, $crit) {
116        critere_parents($idb, $boucles, $crit, 'directs');
117}
118
119function critere_parents_indirects_dist($idb, &$boucles, $crit) {
120        critere_parents($idb, $boucles, $crit, 'indirects');
121}
122
123
124/**
125 * Calcul d'une branche
126 * (liste des id_rubrique contenues dans une rubrique donnee)
127 * pour le critere {branche}
128 *
129 * @param <type> $id
130 * @return <type>
131 */
132function calcul_branche_polyhier_in($id, $tous=true) {
133
134        // normaliser $id qui a pu arriver comme un array, comme un entier, ou comme une chaine NN,NN,NN
135        if (!is_array($id)) $id = explode(',',$id);
136        $id = array_map('intval', $id);
137
138        // Notre branche commence par la rubrique de depart
139        $branche = $id;
140
141        // On ajoute une generation (les filles de la generation precedente)
142        // jusqu'a epuisement
143        while (
144                $id = array_merge(
145                $filles_directes = ($tous!=='indirects'?array_map('reset',sql_allfetsel('id_rubrique', 'spip_rubriques',sql_in('id_parent', $id))):array()),
146                $filles_indirectes = ($tous!=='directs'?array_map('reset',sql_allfetsel('id_objet', 'spip_rubriques_liens',"objet='rubrique' AND " . sql_in('id_parent', $id))):array())
147                )) {
148
149                // enlever les rubriques deja trouvee, sinon on risque de tourner en rond a l'infini en cas
150                // de polyhierarchie bouclee
151                $id = array_diff($id,$branche);
152                $branche = array_merge($branche,$id);
153        }
154
155        return implode(',',$branche);
156}
157
158
159
160/**
161 * {branche ?} ou {branche #ID_RUBRIQUE}
162 * {branche_directe ?} ou {branche_directe #ID_RUBRIQUE}
163 * {branche_principale ?} ou {branche_principale #ID_RUBRIQUE}
164 * {branche_complete ?} ou {branche_complete #ID_RUBRIQUE}
165 *
166 *
167 * @param <type> $idb
168 * @param <type> $boucles
169 * @param <type> $crit
170 */
171function critere_branche($idb, &$boucles, $crit, $tous='elargie') {
172
173        $not = $crit->not;
174        $boucle = &$boucles[$idb];
175        if (isset($crit->param[0])){
176                $arg = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
177        }
178        else
179                $arg = kwote(calculer_argument_precedent($idb, 'id_rubrique', $boucles));
180
181
182        //Trouver une jointure
183        $champ = "id_rubrique";
184        $desc = $boucle->show;
185        //Seulement si necessaire
186        if (!array_key_exists($champ, $desc['field'])){
187                $cle = trouver_jointure_champ($champ, $boucle);
188                $trouver_table = charger_fonction("trouver_table", "base");
189                $desc = $trouver_table($boucle->from[$cle]);
190                if (count(trouver_champs_decomposes($champ, $desc))>1){
191                        $decompose = decompose_champ_id_objet($champ);
192                        $champ = array_shift($decompose);
193                        $boucle->where[] = array("'='", _q($cle.".".reset($decompose)), '"'.sql_quote(end($decompose)).'"');
194                }
195        }
196        else $cle = $boucle->id_table;
197
198
199        $c = "sql_in('$cle" . ".$champ', \$b = calcul_branche_polyhier_in($arg,".($tous===true?'true':"'directs'").")"
200          . ($not ? ", 'NOT'" : '') . ")";
201        $where[] = $c;
202       
203        if ($tous!=='directs'
204          AND in_array(table_objet_sql($boucle->type_requete),array_keys(lister_tables_objets_sql()))){
205                $type = objet_type($boucle->type_requete);
206                $primary = $boucle->id_table.".".$boucle->primary;
207                $sous = "sql_get_select('rl.id_objet','spip_rubriques_liens as rl',sql_in('rl.id_parent',\$b" . ($not ? ", 'NOT'" : '') . ").' AND rl.objet=\'$type\'')";
208                $where[] = "array('IN', '$primary', '(SELECT * FROM('.$sous.') AS subquery)')";
209        }
210
211        if (count($where)==2)
212                $where = "array('OR',".$where[0].",".$where[1].")";
213        else
214                $where = reset($where);
215
216        $boucle->where[]= !$crit->cond ? $where :
217          ("($arg ? $where : " . ($not ? "'0=1'" : "'1=1'") .')');
218}
219
220function critere_branche_principale_dist($idb, &$boucles, $crit) {
221        critere_branche($idb, $boucles, $crit, 'directs');
222}
223// un alias
224function critere_branche_directe_dist($idb, &$boucles, $crit) {
225        critere_branche($idb, $boucles, $crit, 'directs');
226}
227
228function critere_branche_complete_dist($idb, &$boucles, $crit) {
229        critere_branche($idb, $boucles, $crit, true);
230}
231
232/*
233 * Déclarer un fonction générique pour pouvoir chercher dans les champs des rubriques liées
234 *
235 */
236function inc_rechercher_joints_objet_rubrique_dist($table, $table_liee, $ids_trouves, $serveur){
237        $cle_depart = id_table_objet($table);
238        $s = sql_select(
239                "id_objet as $cle_depart, id_parent as id_rubrique",
240                'spip_rubriques_liens',
241                array("objet='$table'", sql_in('id_parent', $ids_trouves)),
242                '','','','',$serveur
243        );
244        return array($cle_depart, 'id_rubrique', $s);
245}
246
247?>
Note: See TracBrowser for help on using the repository browser.