В настоящее время я разрабатываю приложение с использованием PHP и MySQL, построенное на платформе Kohana. Я использую встроенный ORM, и он оказался чрезвычайно полезным. Все работает нормально, но меня очень беспокоит количество запросов, выполняемых на определенных страницах.
Настройка
Например, есть страница, на которой вы можете просмотреть категорию, полную разделов, которые, в свою очередь, полны продуктов. Это перечислено в табличном формате. Каждый продукт имеет (возможно) множество атрибутов, флагов, ценовых льгот. Все это должно быть представлено в таблице.
Сколько запросов?
Что касается запросов: категория должна запрашивать все разделы в ней, и эти разделы должны запрашивать все продукты, которые они содержат. Не так уж плохо, но каждый продукт должен затем запросить все его атрибуты продукта, цены уровня и флаги. Таким образом, добавление большего количества товаров в категорию увеличивает количество запросов во много раз (поскольку в настоящее время я в основном использую ORM). Наличие нескольких сотен продуктов в разделе приведет к паре сотен запросов. Небольшие запросы, но это все еще не хорошо.
Пока ...
Все ключи проиндексированы. Я могу получить всю информацию одним запросом (см. Редактирование ниже), однако, как вы можете себе представить, это приведет к тому, что множество избыточных данных будет разбросано по нескольким строкам для каждого продукта, для каждого дополнительного (например) атрибута, флаг и т. д.
Я не против отказа от ORM для отображаемой части приложения и для построения запросов или даже необработанного SQL.
Решение для этого может быть довольно простым, и я просто не знаю об этом прямо сейчас, что, честно говоря, было бы облегчением. Или, может быть, это не так. Я не уверен. Если какое-либо из моих объяснений было недостаточно адекватным, чтобы понять проблему, просто спросите, и я постараюсь привести лучший пример. (Правка: приведен лучший пример, см. Ниже
Хотя, примечание ...
Одна вещь, которая может иметь какое-то значение: хотя я всегда хочу, чтобы приложение разрабатывалось наиболее эффективно, это не тот сайт, который будет посещаться десятки или сотни раз в день. Это скорее административное приложение, которое, вероятно, не будет использоваться несколькими людьми одновременно. Я не могу предвидеть слишком много перезагрузки, так как большая часть редактирования данных на странице выполняется через AJAX. Итак, должно ли меня волновать, что на этой странице каждый раз при загрузке этой конкретной страницы выполняется пара сотен запросов (в зависимости от того, сколько продуктов находится в просматриваемом разделе)? Просто побочная мысль, даже если бы можно было решить основную вышеупомянутую проблему, я бы предпочел это.
Большое спасибо!
EDIT
Судя по паре ответов, я, кажется, не объяснил себя адекватно. Итак, позвольте мне опубликовать пример, чтобы вы увидели, что происходит.
Перед примером, однако, я также должен сделать два пояснения: (1) есть также пара отношений «многие ко многим», (2) и вы можете сравнить то, что я ищу, с запросом кросс-таблицы.
Давайте упростим и скажем, что у нас есть 3 основные таблицы:
продукты (product_id, product_name, product_date_added)
product_attributes (product_attribute_id, product_id, value)
уведомления (уведомление_id, уведомление_label)
и 1 опорный тальбе:
уведомления_продукта (уведомление_ид, идентификатор_продукта)
Мы собираемся перечислить все продукты в таблице. В ORM достаточно просто вызвать все продукты.
Таким образом, для каждого «продукта» мы перечисляем product_name и product_date_added. Однако нам также необходимо перечислить все атрибуты продуктов. Есть 0 или более из них на продукт. Мы также должны показать, какие уведомления есть у продукта, а также 0 или более.
Итак, на данный момент, как это работает в основном:
foreach ($products->find_all() as $product) //given that $products is an ORM object
{
echo $product->product_id; //lets just pretend these are surrounded by html
echo $product->product_name;
foreach ($products->product_attributes->find_all() as $attribute)
{
echo $attribute->value;
}
foreach ($products->notifications->find_all() as $notification)
{
echo $notification->notification_label;
}
}
Это, конечно, упрощенно, но об этом я и говорю.Это прекрасно работает . Однако , как вы можете видеть, для каждого продукта он должен запросить все его атрибуты, чтобы получить соответствующую коллекцию или строки.Функция find_all () будет возвращать результаты запроса чего-либо в соответствии с: SELECT product_attributes.* FROM product_attributes WHERE product_id = '#'
, и аналогично для уведомлений.И он выполняет эти запросы для каждого продукта.
Таким образом, для каждого продукта в базе данных количество запросов в несколько раз превышает эту сумму. Итак, хотя это работает хорошо, оно плохо масштабируется, поскольку потенциально может привести к сотням запросов.
Если я выполню запрос, чтобы собрать все данные в одном запросе, вдольстроки:
SELECT p.*, pa.*, n.*
FROM products p
LEFT JOIN product_attributes pa ON pa.product_id = p.product_id
LEFT JOIN product_notifications pn ON pn.product_id = p.product_id
LEFT JOIN notifications n ON n.notification_id = pn.notification_id
(опять упрощенно).Это получает данные как таковые, но для каждого атрибута и уведомления, которое имеет продукт, будет возвращена дополнительная строка с избыточной информацией.
Например, если у меня есть два продукта в базе данных;один имеет 1 атрибут и 1 флаг, а другой имеет 3 атрибута и 2 флага, он вернет:
product_id, product_name, product_date_added, product_attribute_id, value, notification_id, notification_label
1, My Product, 10/10/10, 1, Color: Red, 1, Add This Product
2, Busy Product, 10/11/10, 2, Color: Blue, 1, Add This Product
2, Busy Product, 10/11/10, 2, Color: Blue, 2, Update This Product
2, Busy Product, 10/11/10, 3, Style: New, 1, Add This Product
2, Busy Product, 10/11/10, 3, Style: New, 2, Update This Product
Излишне говорить, что это много избыточной информации.Количество строк, возвращаемых для каждого продукта, будет равно количеству атрибутов, которые оно имеет, умноженному на количество уведомлений.
ORM (или, просто создавая новые запросы в цикле в целом), объединяет всю информациюв каждой строке в своем собственном объекте, что позволяет обрабатывать данные более логично.Это камень.Вызов информации в одном запросе устраняет необходимость в сотнях запросов, но создает много избыточных данных в строках и, следовательно, не возвращает данные отношения (один / много) ко многим в сжатых наборах.Это трудное место.
Извините, что так долго пытался быть тщательным, ха-ха, спасибо!