[英]Joining and searching multiple MySQL tables with one-to-many relationships
我一直在疯狂地谷歌试图解决这个问题,但运气却很少。 我想这是一个普遍的问题。
我有5个表格:订单,地址,票据,交易,line_items和发货。
transactions
, addresses
和notes
都具有索引的order_id
字段line_items
和shipments
具有索引的transaction_id
字段。
我获得的最佳单查询性能完全无法维持-有时超过30秒。 具有讽刺意味的是,我可以使用低于1的一大段PHP代码来完成此操作。例如,我将遍历所有注释以与给定的搜索进行匹配,将所有order_id保存在数组中。 然后,我将对所有其他表执行相同的操作。 然后,在订单表的最终查询中附加大量IN(...)语句。 效果很好,但我知道我可以做得更好。
最明显的路线无效; 只需将所有这些表左联接到原始订单表,然后按order.id分组即可花费太长时间-大约9秒。
在我的生命中,我看不到我的垃圾PHP解决方案比mysql内部进行所有这些计算如何更有效。
我已经重写了很多次,几乎无法回忆起我尝试过的所有不同方法……我认为这是我的第一次尝试:
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结果9.6895699501秒
我不确定格式的准确性如何,但我还会附上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
非常感谢。
[编辑:供参考,这是需要约0.02s的PHP解决方案-如何在直接的mysql中做到这一点!
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:再看一次; 仍然没有找到更好的解决方案。 注释中在“ orders o”之后添加“ FORCE INDEX(PRIMARY)”的建议持续敲响了几秒钟-但我从未真正理解为什么。 此外,我还意识到我的PHP解决方案存在局限性,即具有多个术语的搜索仅在表内而不是跨表匹配。
您的解释的第一行跳出我的视线。 您是否将o.id字段设置为主唯一键?
确保正确设置键/索引可以减少巨大的查询量(将服务器崩溃转换为1秒响应)
另外,我将通过对CONCAT进行LIKE来简化比较逻辑:
WHERE CONCAT(
a.email,
a.contactname,
....
) LIKE "%lachman%"
这是您当前简化的WHERE子句:
WHERE a.email LIKE "%Lachman%"
OR a.contact_name LIKE "%Lachman%"
OR a.company_name LIKE "%Lachman%"
OR a.address1 LIKE "%Lachman%"
OR a.address2 LIKE "%Lachman%"
OR a.country LIKE "%Lachman%"
OR a.city LIKE "%Lachman%"
OR a.region LIKE "%Lachman%"
OR a.postal_code LIKE "%Lachman%"
OR n.note LIKE "%Lachman%"
OR t.g_order_number LIKE "%Lachman%"
OR t.pp_txn_id LIKE "%Lachman%"
OR t.fm_invoice_num LIKE "%Lachman%"
OR t.ebay_item_id LIKE "%Lachman%"
OR t.ebay_buyer_id LIKE "%Lachman%"
OR t.ebay_transaction_id LIKE "%Lachman%"
OR t.ebay_order_id LIKE "%Lachman%"
OR li.partnum LIKE "%Lachman%"
OR li.part_id LIKE "%Lachman%"
OR li.desc LIKE "%Lachman%"
OR li.source LIKE "%Lachman%"
OR s.tracking LIKE "%Lachman%"
OR s.carrier LIKE "%Lachman%"
您需要认真考虑要查找的列-这是我不应该在WHERE子句中列出的那些列表:
如果您确实在查询用户指定的字符串(属于您的某些字段的子集),我会考虑创建一个全文索引 ,MySQL支持MyISAM表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.