Есть массив чисел (по сути - id'шки категорий, классификаторов и их значений). Например: PHP: $data = array( array( 'category_id' => 5, 'clf_id' => 7, 'clf_value' => 2 ), array( 'category_id' => 3, 'clf_id' => 4, 'clf_value' => 0 ), ); Есть таблица с полями category_id clf_id clf_value, все поля имеют тип unsigned int NULL Задача: Написать запрос, возвращающий id категорий, поля которых указаны в таблице (то есть совпадения 3 значений массива совпадают с тремя значениями полей в строке таблицы, соответственно). При этом, также нужно вернуть id категорий, которые есть в массиве, но нет в базе с заданным классификатором (то есть если совпала category_id и clf_id но нет совпадения clf_value) - то возвращать не нужно, однако, если если совпала category_id, но нет совпадений с clf_id - возвращаем id категории, И ещё нужно вернуть id категорий у которого значение классификатора = NULL если произошло совпадение по первым двум полям (category_id, clf_id), И ещё возвратить нужно id категорий, которые есть в заданном массиве но нет в таблице вовсе. Ну и всё это должно работать оптимизировано. Пока реализовал с помощью 15 join'ov скорость запроса колеблется в районе 4 секунд на 5000 записей. Было бы интересно посмотреть на ваши прототипы и идеи.
ИМХО, если запрос содержит больше 2-3 join-ов на ту же самую таблицу, то его нужно пересматривать. Думаю, что следует разбить это запрос на несколько - по одному для каждой задачи. А дальше смотреть по ситуации - может какие-то объеденить, другие - оптимизировать.
Написал так, по-моему работает, и быстро. PHP: <?php public function getFilteredCatIds() { $this->_name = 'shop_catalogue_classifiers_values'; $PetID = $animalId = false; $sess = new Zend_Session_Namespace( 'ZooZNS'); if( empty( $sess->animal_id)) { $animals = new AnimalsModel(); $PetID = $animals->getActivePetID(); $petInfo = $animals->getPetCoditionFields( $PetID); } else $animalId = $sess->animal_id; // Getting all available catalogues $shop = new ShopCatalogueModel(); $catalogues = $shop->GetAllByType('C'); if( !$total = count( $catalogues)) return $catalogues; $select = $this->select()->SetIntegrityCheck( false)->distinct() ->from( array( 'a' => $this->_name), array('a.catalogue_id')) ->where('a.catalogue_id IN(?)', $catalogues); if( $PetID) { // Животное $select = $select->where('clf_id = 59 AND clf_value IN(0,' .$petInfo['AnimalID'] .')'); // Размер if( !empty( $petInfo['SizeID'])) $select = $select->where('clf_id = 52 AND clf_value IN(0,' .$petInfo['SizeID'] .')'); // Возраст может, в IN() 0 не нужен? if( !empty( $petInfo['Birthday'])) { $year = date('Y'); $month = date('m'); $total_month = $year * 12 + $month; $info = explode('-', $petInfo['Birthday']); $pet_year = $info[0]; $pet_month = $info[1]; $total_pet_month = $pet_year * 12 + $pet_month; $pet_age_in_months = $total_month - $total_pet_month; $periods_model = new PeriodsModel(); $petInfo['Birthday'] = $periods_model->getPeriodIds( $pet_age_in_months); $select = $select->where('clf_id = 60 AND clf_value IN(0,' .$petInfo['Birthday'] .')'); } // Порода if( !empty( $petInfo['BreedID'])) $select = $select->where('clf_id = 50 AND clf_value IN(0,' .$petInfo['BreedID'] .')'); // Цвет шерсти if( !empty( $petInfo['WoolColorID'])) $select = $select->where('clf_id = 56 AND clf_value IN(0,' .$petInfo['WoolColorID'] .')'); // Тип шерсти if( !empty( $petInfo['WoolTypeID'])) $select = $select->where('clf_id = 57 AND clf_value IN(0,' .$petInfo['WoolTypeID'] .')'); // Колтуны if( !empty( $petInfo['MatsID'])) $select = $select->where('clf_id = 58 AND clf_value IN(0,' .$petInfo['MatsID'] .')'); // Чувствительность кожи if( !empty( $petInfo['SkinID'])) $select = $select->where('clf_id = 53 AND clf_value IN(0,' .$petInfo['SkinID'] .')'); // Где живёт if( !empty( $petInfo['WhereLivesID'])) { $wla = new WherelivesanimalModel(); $lives = $wla->getSingle( array( 'WhereLivesID'), 'id = ' .$petInfo['WhereLivesID']); $petInfo['WhereLivesID'] = $lives['WhereLivesID']; $select = $select->where('clf_id = 54 AND clf_value IN(0,' .$petInfo['WhereLivesID'] .')'); } // Образ жизни elseif( !empty( $petInfo['WayOfLifeID'])) $select = $select = $select->where('clf_id = 54 AND clf_value IN(0,' .$petInfo['WayOfLifeID'] .')'); } elseif( $animalId) { // Животное $select = $select->where('clf_id = 59 AND clf_value IN(0,' .$animalId .')'); } $data = $this->getAdapter()->fetchAll( $select); $found = array(); if( !empty( $data)) { foreach( $data as $row) $found[] = $row['catalogue_id']; } if( count( $found) == $total) return $found; $catalogues = array_diff( $catalogues, $found); /////////// Creating query, to select records, which MUST NOT be in the result $select = $this->select()->SetIntegrityCheck( false)->distinct() ->from( array( 'a' => $this->_name), array('a.catalogue_id')) ->where('a.catalogue_id IN(?)', $catalogues); if( $PetID) { // Животное $select = $select->where('clf_id = 59 AND clf_value NOT IN(0,' .$petInfo['AnimalID'] .')'); // Размер if( !empty( $petInfo['SizeID'])) $select = $select->where('clf_id = 52 AND clf_value NOT IN(0,' .$petInfo['SizeID'] .')'); // Возраст может, в IN() 0 не нужен? if( !empty( $petInfo['Birthday'])) { $select = $select->where('clf_id = 60 AND clf_value NOT IN(0,' .$petInfo['Birthday'] .')'); } // Порода if( !empty( $petInfo['BreedID'])) $select = $select->where('clf_id = 50 AND clf_value NOT IN(0,' .$petInfo['BreedID'] .')'); // Цвет шерсти if( !empty( $petInfo['WoolColorID'])) $select = $select->where('clf_id = 56 AND clf_value NOT IN(0,' .$petInfo['WoolColorID'] .')'); // Тип шерсти if( !empty( $petInfo['WoolTypeID'])) $select = $select->where('clf_id = 57 AND clf_value NOT IN(0,' .$petInfo['WoolTypeID'] .')'); // Колтуны if( !empty( $petInfo['MatsID'])) $select = $select->where('clf_id = 58 AND clf_value NOT IN(0,' .$petInfo['MatsID'] .')'); // Чувствительность кожи if( !empty( $petInfo['SkinID'])) $select = $select->where('clf_id = 53 AND clf_value NOT IN(0,' .$petInfo['SkinID'] .')'); // Где живёт if( !empty( $petInfo['WhereLivesID'])) $select = $select->where('clf_id = 54 AND clf_value NOT IN(0,' .$petInfo['WhereLivesID'] .')'); // Образ жизни elseif( !empty( $petInfo['WayOfLifeID'])) $select = $select = $select->where('clf_id = 54 AND clf_value NOT IN(0,' .$petInfo['WayOfLifeID'] .')'); } elseif( $animalId) { // Животное $select = $select->where('clf_id = 59 AND clf_value NOT IN(0,' .$animalId .')'); } $data = $this->getAdapter()->fetchAll( $select); $found2 = array(); if( !empty( $data)) { foreach( $data as $row) $found2[] = $row['catalogue_id']; } if( count( $found2)) $catalogues = array_diff( $catalogues, $found2); $result = array_merge( $catalogues, $found); $this->_name = 'shop_catalogue_classifiers'; return $result; } ?>
если считаешь, что работает быстро - то это хорошо. а то мне легче пойти и побиться голой о ближайшую стенку, чем попытаться разобраться в этом код красивый, но за 5 мин не осилю, а больше 5 мин жалко тратить на чужой код