Bir-çok ilişkileri ile birden fazla MySQL tabloları birleştirme ve arama

3 Cevap php

Ben öfkeyle şaşırtıcı biraz şans ile, bu anlamaya çalışıyor Googling oldum; Bu ortak bir sorun olduğunu tahmin ediyorum.

Siparişleri, adresler, notlar, işlemler, line_items ve gönderiler: ben 5 tablo var.

transactions, addresses ve notes, tüm order_id alanları dizine sahip - line_items ve shipments transaction_id alanları endeksli olması.

Bazen 30 saniye yukarıda - Ben kazanılmış ettik iyi tek bir sorgu performansı tamamen savunulamaz. Büyük ve sinir bozucu ironi ben 1 altında PHP kod büyük bir blok ile bu yapabiliyor olmasıdır. Örneğin, ben bir dizide order_ids tüm tasarruf, belirli bir arama karşı maç için tüm notlar arasında yineleme olacak. Sonra diğer tüm tabloları için aynısını yapacağım. Sonra emir masanın benim son sorgunun üzerinde büyük bir IN (...) deyimi eklemek gerekir. Bu iyi çalışır, ama ben daha iyisini biliyorum.

En belirgin yolları işe yaramazsa; sadece order.id BY orijinal emir masa ve gruplama için bu tabloların tüm katılmadan SOL çok uzun sürüyor - yaklaşık 9 secs.

Beni yaşam için, benim janky PHP çözüm daha verimli nasıl göremiyorum dahili bütün bu hesaplamaların yapıyor mysql.

Ben bunu bir çok kez yazılabilir ettik, ben pek bu benim ilk denemem olduğunu düşünüyorum ... Ben denedim tüm farklı şeyler çağırabilirsiniz:

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 results 9.6895699501 seconds

Ben biçimlendirme bu olacak ne kadar doğru emin değilim, ama ben de explaination bağlı olacak:

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

Çok, çok teşekkürler.

[Edit: referans için, burada ~ 0.02s alır PHP çözümdür - Ben bu düz mysql nasıl yapabilirim!?]

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: yine bu baktığımızda; Hala daha iyi bir çözüm bulamadı. "FORCE INDEX (İLKÖĞRETİM)" eklemek için yorumlarda öneri sonra "o emir" tutarlı bir kaç saniye çaldı - ama gerçekten neden anlayamamışlardır. Ayrıca ben beri birden fazla terim yalnızca bir tablo içinde yerine tablolar genelinde eşleştirilir ile arar benim PHP çözeltide bir sınırlama var fark ettik.

3 Cevap

Lütfen İZAH ilk satırı bana dışarı atlar. Eğer o.id alanı birincil benzersiz anahtar olarak ayarlanmış mı?

Lütfen tuşları / indeksleri düzgün kurmak sağlanması (1 saniyelik yanıtları içine sunucu çöker dönüşürken) huuuuuge büyüklükleri tarafından sorgu süresini azaltabilir

Ayrıca, ben, bir concatı karşı GİBİ yaparak karşılaştırma mantığı basitleştirmek olur:

WHERE CONCAT(
  a.email,
  a.contactname,
  ....
) LIKE "%lachman%"

İşte fıkra basitleştirilmiş mevcut bulunuyor:

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.ülke LIKE "%Lachman%" 
   OR a.şehir LIKE "%Lachman%" 
   OR a.bölge 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%"

Eğer aradıklarını sütunlar de ciddi bakmak gerekir - Burada WHERE yan tümcesinde olmamalı bu benim listesi:

  • ülke
  • şehir
  • bölge
  • PostalCode
  • pp-txn-id
  • ebay-madde-id
  • ebay-işlem kimliği
  • ebay-sipariş id
  • partnum
  • part_id

Eğer gerçekten bazı alanları alt kümesi olan, kullanıcı tarafından belirlenen dizeleri için çok fazla sorgu yapıyorsanız, ben MySQL MyISAM tabloları için destekleyen bir full-text index, yaratma bakarak düşünün.