Сложные запросы к Базе данных.

Главные вкладки

Аватар пользователя buddy90210 buddy90210 12 января 2023 в 21:06

Всем доброго времени суток! Встала задача сделать сложный запрос к базе данных, подскажите что лучше использовать?
Кратко про структуру, есть две сущности - родитель и дети. У родителя свои поля для фильтрации - термины таксономии, текстовые и числовые поля. У "потомка" свои, так же термины и прочее.
Связаны родители и дети через entity reference.
Задача отфильтровать "детей" одновременно по своим полям и полям его родителя.
Обычно всегда хватало entityQuery(), но как я понял за один запрос он так не сможет (может заблуждаюсь?)
Наверное правильней использовать \Drupal::database()? Смущает что они первым делом предупреждают:
Вы почти никогда не должны делать вызовы базы данных напрямую, если только вы не разрабатываете основные API.))

Можно конечно выбрать по параметрам родителей и детей, а потом в массив с детьми добавлять детей от родителей которые попали под фильтры, затем прогнать через array_unique. Но это доп итерации загрузки, как то не круто)

Лучший ответ

Аватар пользователя buddy90210 buddy90210 13 января 2023 в 8:48

Еще раз убеждаюсь, что Drupal это WOW!!!)
Покурил тему, что производительней сложная выборка с кучей условий или простая но с последующей итерацией (foreach) и разбору по полочкам. Ответ однозначный, выборка из базы быстрей, на то есть несколько причин, основная - установление соединения с БД (PDO), при выборке оно устанавливается один раз, а при итерации так или иначе приходится подключаться к базе для "дерганья" полей, это если кратко)
Теперь по теме вопроса.
Все оказалось максимально просто) Может кому пригодится.

Причем, при создании запроса, если поле не существует получаем соответствующую ошибку. Что, придется проверять какому типу сущности принадлежит поле прежде чем добавлять в запрос? Нет! Если поле существует хотя бы в одном типе сущности все отрабатывает) Может конечно не есть хорошо добавлять не существующие условия для выборки, но это мелочи и решаются просто.

Ответ: к запросу можно добавить условие - проверить поле у ссылочной сущности, например:

<?php
$query
->condition('field_entity_reference.entity:node.field_for_query'$value);
?>

или термин таксономии

<?php
$query
->condition('field_entity_reference.entity:taxonomy_term.name'$value);
?>

В итоге получаем что-то в этом роде:

<?php
$query 
= \Drupal::entityQuery('node');
$query->condition('type'$bundle);
$query->condition('status'1);
$query->accessCheck(TRUE);
$orGroup $query->orConditionGroup();
foreach(
$filters as $key => $value) {
  
$field 'field_'.$key;
  
$orGroup->condition($field$value'IN');
  
$orGroup->condition('field_parent.entity:node.'.$field$value'IN');
}
$query->condition($orGroup);
$result $query->execute();?>

Комментарии

Аватар пользователя buddy90210 buddy90210 13 января 2023 в 8:48

Еще раз убеждаюсь, что Drupal это WOW!!!)
Покурил тему, что производительней сложная выборка с кучей условий или простая но с последующей итерацией (foreach) и разбору по полочкам. Ответ однозначный, выборка из базы быстрей, на то есть несколько причин, основная - установление соединения с БД (PDO), при выборке оно устанавливается один раз, а при итерации так или иначе приходится подключаться к базе для "дерганья" полей, это если кратко)
Теперь по теме вопроса.
Все оказалось максимально просто) Может кому пригодится.

Причем, при создании запроса, если поле не существует получаем соответствующую ошибку. Что, придется проверять какому типу сущности принадлежит поле прежде чем добавлять в запрос? Нет! Если поле существует хотя бы в одном типе сущности все отрабатывает) Может конечно не есть хорошо добавлять не существующие условия для выборки, но это мелочи и решаются просто.

Ответ: к запросу можно добавить условие - проверить поле у ссылочной сущности, например:

<?php
$query
->condition('field_entity_reference.entity:node.field_for_query'$value);
?>

или термин таксономии

<?php
$query
->condition('field_entity_reference.entity:taxonomy_term.name'$value);
?>

В итоге получаем что-то в этом роде:

<?php
$query 
= \Drupal::entityQuery('node');
$query->condition('type'$bundle);
$query->condition('status'1);
$query->accessCheck(TRUE);
$orGroup $query->orConditionGroup();
foreach(
$filters as $key => $value) {
  
$field 'field_'.$key;
  
$orGroup->condition($field$value'IN');
  
$orGroup->condition('field_parent.entity:node.'.$field$value'IN');
}
$query->condition($orGroup);
$result $query->execute();?>
Аватар пользователя Andruxa Andruxa 13 января 2023 в 11:47

Ответ однозначный, выборка из базы быстрей, на то есть несколько причин, основная - установление соединения с БД (PDO), при выборке оно устанавливается один раз, а при итерации так или иначе приходится подключаться к базе для "дерганья" полей, это если кратко)

Был такой кейс: фотогалерея (с пагинацией, конечно), под фото нужно выводить копирайт.

Копирайт мог быть одним из трех вариантов:
- зарегистрированный пользователь, приславший свое фото на конкурс,
- профессиональный фотограф, которому не было никакой нужды регистрироваться на сайте - для таких создавалиь ноды определенного типа,
- медиа агенство, купившее право на фото, тоже отдельная нода

В запросе, соответственно - 3 join'а, по которым подтягивались имя пользователя, имя фотографа или же название агентства.

Все это неплохо работало вначале, когда контента было мало.
Но когда кол-во фотографий перевалило за несколько десятков тысяч, кол-во пользователей - за несколько тысяч, и несколько сотен фотографов и агенств - каждый такой запрос отрабатывал по несколько секунд, а временная таблица mysql разросталась до гигабайта.

Пришлось переделать это все на вложенные подзапросы.
Так что оптимизация соединения с БД - это экономия на спичках.