В приведенном ниже MySQL-запросе используется PHP, чтобы вывести сектор $, который представляет собой одну цифру, и $ subsector_text, который представляет собой строку, разделенную запятой. $ Subsector_text может представлять собой одну цифру или список из нескольких идентификаторов, например «3,4,7,9».
$sql = " SELECT DISTINCT a.id , a.name , a.category_id , a.sector , a.subsector , a.year_in_operation , a.state , a.total_value , b.country_id , b.project_id , c.isocode_3 , c.name FROM com_barchan_project a JOIN com_barchan_location b ON b.project_id = a.id JOIN com_barchan_country c ON c.id = b.country_id JOIN com_barchan_project_value_join d ON a.id = d.project_id WHERE a.state = 1 AND a.sector = '$sector' AND a.subsector REGEXP '^{$subsector_text}[,]|[,]{$subsector_text}[,]|[,]{$subsector_text}$|^{$subsector_text}$' ORDER BY a.total_value DESC , a.category_id ASC , a.name ASC ";
Проблема, с которой я столкнулся с запросом выше, – это строка:
AND a.subsector REGEXP '^{$subsector_text}[,]|[,]{$subsector_text}[,]|[,]{$subsector_text}$|^{$subsector_text}$'
Если $ subsector_text = "3,4,5,9", то это только возвращает записи, которые содержат ровно "3,4,5,9" в поле $ subsector.
Желаемый результат состоит в том, что он вернет любую запись, которая имеет любое из значений в $ subsector_text. Например, все они должны быть возвращены, но в настоящее время их нет. Этот список является примером и отнюдь не точным.
1,3 1,5 1,3,7,9 3,5 3,4,5,9 9 3 5 4
Как изменить запрос, чтобы выбрать любые записи, которые имеют значение в строке $ subsector_text?
Обратите внимание: если $ subsector_text = 11, то в качестве примера не следует выбирать следующее.
1 12 21
Любая помощь будет принята с благодарностью.
Нецелесообразно сопоставлять любое значение в отдельной строке с запятой против любого значения в другой разделимой запятой строке в одном предикате.
Вы можете использовать FIND_IN_SET () для поиска по одному значению за раз.
Это означает, что вам нужно несколько предикатов, по одному для каждого значения, которое вы получаете, разбивая ваш вход $subsector_text
. Так разделите свою переменную и переместите ее в последовательность вызовов FIND_IN_SET ().
Я не тестировал следующий код, но он должен дать вам представление о том, что я говорю:
$subsector_array = array_map('intval', explode(',', $subsector_text)); $subsector_terms = array_map( function ($id) { return "FIND_IN_SET($id, a.subsector)"; }, $subsector_array); $subsector_expr = implode(' OR ', $subsector_terms); $sql = " SELECT ... WHERE a.state = 1 AND a.sector = '$sector' AND ($subsector_expr) ...";
Это, конечно же, заставит сканирование таблицы, потому что нет возможности индексировать FIND_IN_SET () или любую другую операцию, которая ищет подстроки. Я полагаю, что ваши условия для a.state
и a.sector
будут использовать индекс, чтобы сузить поиск до применения условий FIND_IN_SET ().
Я понимаю дилемму работы с системой, которую вы унаследовали. Пусть ваш менеджер знает, что в какой-то момент это нужно, чтобы получить рефакторинг, потому что он никогда не будет эффективным или надежным, как он разработан сейчас.
Ваш подход правильный, но вам нужны некоторые изменения. Вместо того, чтобы пытаться сопоставить только одно условие (REGEXP), можно создать несколько условий, связанных с OR
…
Пример:
$subsectorArray = explode(',', $subsector_text); $or = []; foreach ($subsectorArray as $subsector){ $or[] = "a.subsector REGEXP '[^[:alnum:]]{$subsector}[^[:alnum:]]|^{$subsector}[^[:alnum:]]|[^[:alnum:]]{$subsector}$|^{$subsector}$'"; } $orStr = implode(' OR ', $or); $sql = " SELECT DISTINCT a.id , a.name , a.category_id , a.sector , a.subsector , a.year_in_operation , a.state , a.total_value , b.country_id , b.project_id , c.isocode_3 , c.name FROM com_barchan_project a JOIN com_barchan_location b ON b.project_id = a.id JOIN com_barchan_country c ON c.id = b.country_id JOIN com_barchan_project_value_join d ON a.id = d.project_id WHERE a.state = 1 AND a.sector = '$sector' AND ($orStr) ORDER BY a.total_value DESC , a.category_id ASC , a.name ASC ";
,$subsectorArray = explode(',', $subsector_text); $or = []; foreach ($subsectorArray as $subsector){ $or[] = "a.subsector REGEXP '[^[:alnum:]]{$subsector}[^[:alnum:]]|^{$subsector}[^[:alnum:]]|[^[:alnum:]]{$subsector}$|^{$subsector}$'"; } $orStr = implode(' OR ', $or); $sql = " SELECT DISTINCT a.id , a.name , a.category_id , a.sector , a.subsector , a.year_in_operation , a.state , a.total_value , b.country_id , b.project_id , c.isocode_3 , c.name FROM com_barchan_project a JOIN com_barchan_location b ON b.project_id = a.id JOIN com_barchan_country c ON c.id = b.country_id JOIN com_barchan_project_value_join d ON a.id = d.project_id WHERE a.state = 1 AND a.sector = '$sector' AND ($orStr) ORDER BY a.total_value DESC , a.category_id ASC , a.name ASC ";
Решением было реорганизовать приложение. Это заняло пару дней, но код нарушения исчез, и была создана новая таблица подсекторов. Всем спасибо.