J'ai été furieusement googling essayer de comprendre cela, avec étonnamment peu de chance; Je suppose que c'est un problème commun.Rejoindre et rechercher plusieurs tables MySQL avec des relations un-à-plusieurs
J'ai 5 tables: ordres, adresses, notes, transactions, éléments de ligne et envois.
transactions
, addresses
et notes
tous ont indexé order_id
champs - line_items
et shipments
ont indexés transaction_id
champs.
La meilleure performance de requête unique que j'ai obtenue est complètement indéfendable - parfois plus de 30 secondes. L'ironie du sort est que je peux le faire avec un gros bloc de code PHP en dessous de 1. Par exemple, je vais parcourir toutes les notes pour comparer avec une recherche donnée, en sauvegardant tous les order_ids dans un tableau. Ensuite, je ferai de même pour toutes les autres tables. Ensuite, je vais ajouter une instruction IN (...) massive sur ma requête finale de la table des ordres. Cela fonctionne bien, mais je sais que je peux faire mieux.
Les routes les plus évidentes ne fonctionnent pas; RACCORDER simplement toutes ces tables à la table des commandes d'origine et GROUPING BY à order.id prend trop de temps - environ 9 secondes.
Pour la vie de moi, je ne peux pas voir comment ma solution PHP janky est plus efficace que mysql faire tous ces calculs en interne.
J'ai réécrit ce que je peux tant de fois, à peine rappeler toutes les différentes choses que j'ai essayé ... Je pense que ce fut ma première tentative:
SELECT o.id FROM orders o
LEFT JOIN addresses a ON a.order_id = o.id
LEFT JOIN notes n ON (n.parent_id = o.id AND n.type = "parts")
LEFT JOIN transactions t ON t.order_id = o.id
LEFT JOIN line_items li ON li.transaction_id = t.id
LEFT JOIN shipments s ON s.transaction_id = t.id
WHERE 0 = 0
AND ((a.`email` LIKE "%Lachman%" || a.`contact_name` LIKE "%Lachman%" || a.`company_name` LIKE "%Lachman%" || a.`address1` LIKE "%Lachman%" || a.`address2` LIKE "%Lachman%" || a.`country` LIKE "%Lachman%" || a.`city` LIKE "%Lachman%" || a.`region` LIKE "%Lachman%" || a.`postal_code` LIKE "%Lachman%" || n.`note` LIKE "%Lachman%" || t.`g_order_number` LIKE "%Lachman%" || t.`pp_txn_id` LIKE "%Lachman%" || t.`fm_invoice_num` LIKE "%Lachman%" || t.`ebay_item_id` LIKE "%Lachman%" || t.`ebay_buyer_id` LIKE "%Lachman%" || t.`ebay_transaction_id` LIKE "%Lachman%" || t.`ebay_order_id` LIKE "%Lachman%" || li.`partnum` LIKE "%Lachman%" || li.`part_id` LIKE "%Lachman%" || li.`desc` LIKE "%Lachman%" || li.`source` LIKE "%Lachman%" || s.`tracking` LIKE "%Lachman%" || s.`carrier` LIKE "%Lachman%"))
GROUP BY o.id
ORDER BY `created` DESC
2 résultats 9,6895699501 secondes
Je ne suis pas sûr de savoir comment la mise en forme précise sera sur ce point, mais je vais aussi attaché le explaination:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE o ALL NULL NULL NULL NULL 2840 Using temporary; Using filesort
1 SIMPLE a ref order_id order_id 5 apple_components.o.id 1
1 SIMPLE n ref parent_id,type type 22 const 314
1 SIMPLE t ref order_id order_id 5 apple_components.o.id 1
1 SIMPLE li ref transaction_id transaction_id 4 apple_components.t.id 1
1 SIMPLE s ref transaction_id transaction_id 4 apple_components.t.id 1 Using where
Un grand merci.
[Edit: pour référence, voici la solution PHP qui prend ~ 0.02s - comment puis-je faire cela dans une base MySQL droite !?]
if ($s['s']) {
$search_fields = array(
'a' => array('email', 'contact_name', 'company_name', 'address1', 'address2', 'country', 'city', 'region', 'postal_code'),
'n' => array('note'),
't' => array('g_order_number', 'pp_txn_id', 'fm_invoice_num', 'ebay_item_id', 'ebay_buyer_id', 'ebay_transaction_id', 'ebay_order_id'),
'li' => array('partnum', 'part_id', 'desc', 'source'),
's' => array('tracking', 'carrier')
);
$search_clauses = array();
foreach ($search_fields as $table => $fields) {
$the_fields = array();
foreach ($fields as $field) $the_fields[] = $table.'.`'.$field.'`';
$clauses = array();
foreach (explode(' ', $s['s']) as $term) $clauses[] = 'CONCAT_WS(" ", '.implode(', ', $the_fields).') LIKE "%'.$term.'%"';
$search_clauses[$table] = $clauses;
}
$order_ids = array();
$results = mysql_query('SELECT order_id FROM addresses a WHERE '.implode(' AND ', $search_clauses['a']));
while ($result = mysql_fetch_assoc($results)) $order_ids[] = $result['order_id'];
$results = mysql_query('SELECT parent_id FROM notes n WHERE type = "orders" AND '.implode(' AND ', $search_clauses['n']));
while ($result = mysql_fetch_assoc($results)) $order_ids[] = $result['parent_id'];
$results = mysql_query('SELECT order_id FROM transactions t WHERE '.implode(' AND ', $search_clauses['t']));
while ($result = mysql_fetch_assoc($results)) $order_ids[] = $result['order_id'];
$transaction_ids = array();
$results = mysql_query('SELECT transaction_id FROM line_items li WHERE '.implode(' AND ', $search_clauses['li']));
while ($result = mysql_fetch_assoc($results)) $transaction_ids[] = $result['transaction_id'];
$results = mysql_query('SELECT transaction_id FROM shipments s WHERE '.implode(' AND ', $search_clauses['s']));
while ($result = mysql_fetch_assoc($results)) $transaction_ids[] = $result['transaction_id'];
if (count($transaction_ids)) {
$results = mysql_query('SELECT order_id FROM transactions WHERE id IN ('.implode(', ', $transaction_ids).')');
while ($result = mysql_fetch_assoc($results)) if (!empty($result['order_id'])) $order_ids[] = $result['order_id'];
}
}
$query = 'SELECT id FROM orders WHERE id IN ('.implode(', ', $order_ids).')';
2009-10-07: En regardant ce nouveau ; toujours pas trouvé une meilleure solution. La suggestion dans les commentaires d'ajouter "FORCE INDEX (PRIMARY)" après "orders o" a systématiquement disparu quelques secondes - mais je n'ai jamais vraiment compris pourquoi. Aussi, j'ai réalisé depuis lors qu'il y a une limite dans ma solution PHP dans la mesure où les recherches avec plusieurs termes ne sont comparés qu'au sein d'une table plutôt qu'à travers les tables.
Je ne suis pas sûr de faire ce CONCAT. Chaque fois que vous faites LIKE contre une valeur calculée, le serveur doit parcourir toutes les lignes une par une. Si vous faites LIKE contre les lignes directes dans la table (et que vous avez une sorte d'index de texte intégral dans la base de données), le serveur peut vérifier l'index à la place. (bien qu'il n'y ait pas d'index de texte intégral, vous êtes bloqué en parcourant toutes les lignes puisqu'il y a un% au début de la chaîne.) –
En d'autres termes, vous avez simplifié le texte de la requête, mais vous ' J'ai rendu la recherche dans la base de données plus compliquée. –
Oui, mais je suis sous l'hypothèse que le LIKE sera presque instantané, ce sont les jointures qui provoquent le ralentissement. – rooskie