Поиск товаров в каталоге

-faqer-

Я только учусь
Поиск товаров в каталоге

Есть древовидная структура каталога товаров построенная по принципу NestedSets

Cat
Id|title|left|right|level

Каталог планируется достаточно большой и разветвленный
Допустим, как price.ua
Категории могут быть двух типов: которые содержат подкатегории, которые содержат продукты. Смешанного содержимого (категории и продукты) быть не может

Есть таблица товаров, связанная с первой таблицей

Prod
Id|category|title

Предположим есть такая структура
+Принтеры
++Матричные
+++А4
+++А3
++Лазерные
+++Монохромные
++++А4
++++А3
+++Цветные
++++А4
++++А3

Не проблема сделать запрос на поиск категорий в которых есть товары с названием, допустим, содержащим usb. А также на вывод количества найденных товаров

SELECT cat.title as cat, COUNT(prod.title) AS c FROM cat, prod WHERE cat.id = prod.category AND prod.title LIKE '%usb%' GROUP BY cat

Такой запрос нам выведет, допустим
А4 – 3
А4 – 5

А как сделать, чтоб вывод был красив
Вот так

+Принтеры
++Матричные
+++А4 - 3
++Лазерные
+++Монохромные
++++А4 – 5

Т.е. получить катеогрии, содержащие искомый товар, а также всех предков этих категорий

Реальный пример можно увидеть на том же price.ua забив что либо в поиск справа вверху

Мой вариант:
- указанным выше методом находим все категории, которые содержат искомый товар
- рекурсивно для каждой категории находим всех предков и заносим их в массив с ключем равным значению left (таким образом, если у нескольких категорий одни родители, то мы просто еще раз перезапишем элемент массива с таким же left)
- проходим по созданному массиву и выводим структуру

Вроде как все неплохо
Только вылазит такое количество циклов, что мне кажется, при большой структуре каталога, такой подход будет серьезно тормозить. Есть ли какой-то иной вариант??
 

440hz

php.ru
храни в узле инфу о родителях до корня. например сериализованным массивом. правда при работе с узлами придется немного попотеть заьо на выборках будет быстро работатью.
 

alexhemp

Новичок
440hz

Зачем какие-то костыли из списков смежности? В Nested Sets уже есть вся информация для таких выборок, ничего не надо дополнительно хранить, нужно лишь поинтересоваться реализацией типовых операций.

-faqer-
Изучи подробнее Nested Sets.

Есть у тебя есть id разделов, то ты можешь взять LEFT и RIGHT
А по ним можешь вытащить ОДНИМ запросом все что тебя интересует.

Более того - то что ты хочешь можно сделать в один запрос :)

Примерно так:

Выбираешь LEFT и RIGHT каждого нужного элемента, потом
строишь все пути до них от корня - пользуясь запросом с самообъдинением

Получиться что-то вроде:

SELECT p.TITLE, p.ID FROM cat c, parent p WHERE p.LEFT < c.LEFT AND p.RIGHT > c.RIGHT AND c.TITLE LIKE '%usb%'

Ну и приклеить количество товаров и сгруппировать, это уже доп. сервис.
 

-faqer-

Я только учусь
2 alexhemp

Честно скажу, не понял я запроса
Поискал ничего не нашел по поводу самообъединение
И что это за синтаксис такой FROM cat c, parent p я тоже не понял
Не вижу никакой связи между таблицами

Как найти в NestedSets информацию про предков зная id я понимаю
А как выбрать информацию про предков когда id несколько без рекурсии так и не понял.

Поясни плиз твой запрос

Еще раз повторю задачу
Нужно построить структуру категорий и количество товаров по запросу на определенное название в таблице товаров

-~{}~ 23.10.06 11:31:

Подскажите, пожалуйста
 

alexhemp

Новичок
-faqer-

Почитай теорию SQL. "Связь" между таблицами - она у тебя в голове, а не в базе.

Ты сделай выборку такую, и посмотри как она работает.

Про Nested Sets читай до просветления, там как раз фишка в том, что выборка делается одним запросом в 99,9% случаев. Твой запрос совершенно обычный для Nested Sets.
 

ru_skol

Новичок
вообще достаточно чуть-чуть изменить исходный запрос:
SELECT cat.id, cat.title, cat.left, cat.right, COUNT(prod.title) AS c FROM cat, prod WHERE cat.left <= prod.category AND cat.right >= prod.category AND prod.title LIKE '%usb%' GROUP BY cat.id, cat.title, cat.left, cat.right

это если [left..right] включает свой id, если нет, то дописать как
SELECT cat.id, cat.title, cat.left, cat.right, COUNT(prod.title) AS c FROM cat, prod WHERE ((cat.left <= prod.category AND cat.right >= prod.category) or cat.id = prod.category) AND prod.title LIKE '%usb%' GROUP BY cat.id, cat.title, cat.left, cat.right
 

-faqer-

Я только учусь
ru_skol
Долго не было меня не было возможности отвечать на сообщения.
За это время так и не разобрался

Возможно я четко обрисовал задачу
В таблице с товарами нет никаких данных о лефт и райт категории, только id
Поэтому данная часть запроса cat.left <= prod.category AND cat.right >= prod.category просто не может работать, т.к. id и left, right ничего общего не имеют

Собственно как и в посте alexhemp

p.LEFT < c.LEFT AND p.RIGHT > c.RIGHT

таблица с продуктми не несет данных о лефт и райт

Это конечно сделать несложно, но тогда при изменении структуры категорий, нужно будет редактировать и все товары, дего делать крайне не хочется

-~{}~ 04.11.06 01:44:

Люди!!!, неужели никто не поможет????
 

alexhemp

Новичок
-faqer-

Не надо писать мне в личку. Я дал тебе достаточно информации, чтобы написать запрос.

Зачем тебе в таблице товаров LEFT и RIGHT, если ты можешь использовать в нем JOIN c таблицей категорий?

Это конечно сделать несложно, но тогда при изменении структуры категорий, нужно будет редактировать и все товары, дего делать крайне не хочется
С чего ты это взял? Есть у тебя ID категории, его и записываешь.
Хочешь изменить структуру - измени LEFT и RIGHT, не трогай ID
Если переносишь товар из раздела в раздел все равно придется поменять ему идентификатор раздела.

Насчет запросов с самобъединением конечно я немного напутал тебя в синтаксисе.

Исправлюсь, прочитаю маленькую лекцию:

Что такое реляционная база данных? Это набор множеств (таблиц) которые состоят из элементов (строк).
Все операции над таблицами можно представить в виде операций над множествами - собственно это называется реляционной алгеброй.
Как выполняется SQL запрос?

1. Сперва сервер стоит декартово произведение множеств, учавствующих в запросе, например есть таблица A cо строками 1,1 и 1,2 и таблица B - со строками 2,1 и 2,2. Тогда декартово произведение - это множество всех комбинаций, а именно (1,1,2,1),(1,1,2,2),(1,2,2,1),(1,2,2,2) - грубо говоря комбинируем каждую строку с каждой, получаем множество из M * N элементов где длина каждого элемента - сумма длин строк исходных множеств.

2. Далее сервер выкидывает из множества все строки, что не удовлетворяют критериям выборки, что указаны в WHERE (и JOIN - это просто более наглядная форма условия WHERE)

Все что осталось - выдается как результат

Итак запрос с самобъединением, это такой запрос, в котором таблица объединяется сама с собой, например SELECT * FROM cat c, cat p
Что выходит - большое множество, в котором все строки комбинируются каждая с каждой.
Теперь мы хотим построить "ПУТЬ" до элемента с определенным ID - замечательно - наложим сперва ограничение по p.ID
SELECT * FROM cat c, cat p WHERE с.ID=x

Что вышло - все данные из таблицы категорий, но в каждой строке есть данные по категории, к которой мы ищем путь, отлично, давайте выкинем все строки, что нам не нужны, а именно те, что не удовлетворяют условию "LEFT ребенка лежит между LEFT и RIGHT родителя", задно отсортируем:
SELECT p.* FROM cat c, cat p WHERE с.ID=x AND c.LEFT BETWEEN p.LEFT AND p.RIGHT ORDER BY p.LEFT

Далее - нужно пристегнуть к таблице p таблицу товаров, сгруппировать по ID раздела и посчитать сколько товаров у нас попало в тот или иной раздел (COUNT)

Вероятно ты получишь цифры только для самых "низших" в уровне разделов.

Тут есть 2 выхода - или суммировать при выводе, или дописать еще одно самообъединение для суммирования не только товаров текущего раздела, а товаров принадлежащих ко всем потомкам.

Поэкспериментируй, усложняй запросы поэтапно, изучай SQL и теорию множеств, а также типовые операции над вложенными множествами.
 
Сверху