Выборка ORM возвращает не то - разбор причин
Запрос через ORM отрабатывает без ошибок, а строк больше нужного, меньше нужного или в них пусто. Разбираем причины по убыванию частоты.
С чего начать
Смотрим собранный запрос целиком:
$query = ElementTable::query() ->setSelect(['ID', 'TITLE']) ->where('TITLE', 'тест');echo $query->getQuery(); // готовый SQL со всеми объединениями// фильтр без оператора собирается в поиск подстроки, а не в равенствоСобранный запрос отвечает на большинство вопросов сразу. В нём видно и лишние объединения таблиц, и условие поиска подстроки там, где ожидалось точное сравнение.
Проверяем оператор в условии:
->where('=TITLE', 'тест') // точное совпадение->where('TITLE', 'тест') // поиск подстроки: вернёт и «тест два»// оператор указывают явно: значение по умолчанию удивляет чаще всегоУсловие без явного оператора ищет вхождение подстроки. Это объясняет лишние строки в выборке, которые выглядят похожими на нужные, но нужными не являются.
Смотрим, что вернулось на самом деле:
$rows = $query->fetchAll();printf("строк=%d уникальных ID=%d\n", count($rows), count(array_unique(array_column($rows, 'ID'))));// строк больше, чем уникальных записей - объединение размножило результатРазница между числом строк и числом уникальных записей выдаёт объединение таблиц. Связь один-ко-многим множит основную запись по числу связанных, и постраничная навигация после этого считает неправильно.
Разделяем запрос со связями:
use Bitrix\Main\ORM\Query\QueryHelper;
$rows = QueryHelper::decompose($query, true); // связи выбираются отдельно// без этого ограничение считает строки объединения, а не записи сущностиПомощник разбирает запрос на части и собирает результат по записям. Это же снимает произведение строк, когда в одном запросе выбрано сразу несколько отношений.
Проверяем результат записи:
$result = ElementTable::update($id, ['TITLE' => 'новое']);if (!$result->isSuccess()) { print_r($result->getErrorMessages());}printf("изменено строк: %d\n", $result->getAffectedRowsCount());// успех операции и факт изменения строки - разные вещиПроверка результата обязательна на каждой записи. Без неё непрошедшая проверку операция сообщает об ошибке предупреждением уровня PHP, которое на боевом сайте никто не увидит.
Причины
-
В условии нет явного оператора примерно 30% случаев
ПризнакВ выборке лишние записи, похожие на нужные по началу строки.
ПроверкаСмотрим собранный запрос: условие собралось в поиск подстроки вместо равенства.
Что делатьПишем оператор явно: точное сравнение задаётся отдельным знаком перед именем поля.
-
Ограничение применяется к строкам объединения примерно 25% случаев
ПризнакВместо пяти записей приходит одна-две, зато с неполными связанными данными.
ПроверкаСчитаем строки и уникальные записи в результате, смотрим объединения в запросе.
Что делатьРазделяем запрос помощником для связей: ограничение должно считать записи, а не строки.
-
Несколько связей в одном запросе примерно 20% случаев
ПризнакСтрок в разы больше ожидаемого, запрос съедает память и выполняется долго.
ПроверкаПеремножаем число связанных записей: результат совпадёт с числом строк.
Что делатьВыбираем отношения отдельными запросами, а не одним общим с несколькими объединениями.
-
Поле не запрошено и не подгружается само примерно 15% случаев
ПризнакОбъект получен, а поле пустое или обращение к нему даёт исключение.
ПроверкаСмотрим список выбранных полей и то, какие поля читает код дальше.
Что делатьНазываем нужные поля в выборке или подгружаем их явно: сами они не появятся.
-
Результат записи не проверен примерно 10% случаев
ПризнакКод отработал, а данные в базе прежние или изменились не у всех записей.
ПроверкаПечатаем ошибки результата и число затронутых строк после записи.
Что делатьПроверяем результат всегда: успех операции и факт изменения строки - разные вещи.
Частые вопросы
Почему выборка возвращает лишние записи?
Условие без оператора ищет подстроку. Для точного совпадения оператор указывают явно, и это первое, что проверяют при лишних строках.
Почему постраничная навигация врёт при связях?
Ограничение применяется к строкам результата, а объединение размножает основную запись. Считать надо записи, а связи разбирать отдельным запросом.
Можно ли брать объекты при группировке?
Нет, при группировке и агрегатах форма результата другая: берут обычные массивы. Объекты рассчитаны на выборку записей сущности, а не на сводные числа.
Удаление записи удаляет связанные данные?
Нет, удаляется только сама запись. Связанные строки убирают явно или обработчиком события удаления.
Как понять, что запись действительно изменилась?
По числу затронутых строк в результате. Успешный результат означает лишь отсутствие ошибок, а не факт изменения данных.
Смежное
- Свои таблицы на ORM - оглавление подтемы
- Своя таблица на ORM: сущность, запросы, изменение структуры - как устроены выборки
- Связи между таблицами ORM: ссылки, выборка, удаление - объединения и их последствия
- Объекты ORM: выборка объектами, ленивая загрузка, сохранение - почему поле объекта оказалось пустым
- Транзакции при записи: откат, границы, что не откатится - когда запись прошла частично
- Медленный запрос к базе: поиск, план, индекс - когда запрос ещё и медленный
- Выборки из инфоблоков: GetList, ORM и разделы - те же правила на инфоблоках
- ORM в 1С-Битрикс - устройство ORM целиком